|

楼主 |
发表于 2021-1-2 09:20:48
|
显示全部楼层
#include<iostream>
( d( `* q0 f# i, R#include<opencv2/core/core.hpp>3 i+ V, O7 H# U) Q
#include<opencv2/highgui/highgui.hpp>
& D. n; E) u4 t7 H3 z/ S* Y+ c7 e9 G- Q#include <opencv2/opencv.hpp>5 j) x3 Q; O+ J: Z& u' D6 p5 N
#include <opencv2/imgproc/imgproc.hpp>9 v- M! q- u2 W$ h. O
#include <opencv2/imgproc/types_c.h>
1 L# o- c8 V* g! {4 L3 K$ \$ k1 q$ x#include <stdlib.h>
1 F; |! e. Y9 W1 ]7 O9 d7 T; d#include <stdio.h>
% ~' v; W6 V, H# q% S#include <opencv2/highgui/highgui_c.h>; I) r: n8 O: ^! t4 @
#include <math.h>* |! O: N. L2 o9 ]2 i* `" q
//#include "iostream"
) y p# f9 w: [* @1 ?& l \//#include "cv.h"
' d4 _8 r9 L! y! i, e3 f//#include "highgui.h"
/ r0 v. i9 Q% ^! ^//#include "math.h"
! M5 d3 M9 @% f+ ?+ A. h0 N wusing namespace cv; //命名空间CV" F C+ w' K% y' a9 |- A
using namespace std; //命名空间 std1 d6 I" [. m0 `
4 Y6 y: _% k2 x( w* V0 R. u
int threshold_value = 225; //启动程序时的阈值初始值,因为227能够完全提取轮廓,所以设置为225,实际上227也可以。7 H+ F h- E2 |
int threshold_type = 3; //启动程序的时候阈值类型,默认的为不足部分取零2 G3 t6 Q# g0 F7 d! \. D$ v
int const max_value = 255;
- Z/ N ~- F: d- Uint const max_type = 4;7 r5 l1 P1 x9 M2 _6 D
int const max_BINARY_value = 255;2 ~/ h# v- x& G' |" o- D0 R
7 {) t: d/ o$ |$ E0 z
CvFont font;2 r, J5 A* J& n( H1 ~% Y
uchar* ptr;
0 W; A) t; ]! s. \ char label[20];, H. w) i# T. W* q0 S
char label2[20];% x& ~ U& S& [1 e4 d p
+ F9 @! B6 I+ o2 P6 y( }Mat src, blured, src_e, src_gray, dst; //类定义几个图片变量,dst是最后转化阈值之后的图片,src.gray是灰度图) L; a ?& H5 a9 `8 P, X* Y5 \
//在C语言中“char*”是声明一个字符类型du的指针,定义数据类型,char可以定义字符zhi有变量、数组、指针。dao
; s7 j0 p3 u- J( [+ A, T //例如:char *string="I love C#!"
- P& S1 v: y* G6 u A7 q" B p //定义了一个字符指针变量string,用字符串常量"I love C#!",对它进行初始化。对字符指针变量初始化,实际上就是把字符串第1个元素的地址(即存放字符串的字符数组的首元素地址)赋给string。*/
6 s1 S9 Q- _* G( yMat element = getStructuringElement(MORPH_RECT, Size(5,5)); //用于腐蚀的参数' V. E$ r/ x1 g) e: _- y
char* window_name = "阈值演示程序20201121";
7 P* k5 D, e# y R' W( |7 ^char* trackbar_type = "类型: \n 0: 二值化 \n 1: 反二值化 \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted"; //
' t* n0 ?. E8 c0 b8 r, U1 j! Pchar* trackbar_value = "数值";
8 B- I9 K* V9 J% B: m: ?
! m- y9 f7 L( V$ c- B' V: u/// 自定义函数声明: M, d. v$ q6 B" |6 ~) ]1 C% \
void Threshold_Demo( int, void* );* e2 T4 X/ ]# k5 B
( p4 O' h- z" @& j
/**
1 i# } t8 l2 y * @主函数$ L3 Q" D& b( @$ M. m+ J
*/8 J( N, k- R- q5 E1 u
int main( int argc, char** argv )
' J& G* \/ b+ Z, }: ~{( w `3 X) S5 P2 C) x Y) P5 G
/// 读取一副图片,不改变图片本身的颜色类型(该读取方式为DOS运行模式)
" v8 P1 a: @, ~8 h) n- u+ Q) O/ Z src = imread("121.JPG", 1); //目前还未使用摄像头拍摄照片,暂时以直接读取文件的方式来测试。
. f& d: A/ A4 _7 X7 U& r* M erode (src, src_e, element); //对图片进行腐蚀,参数可调,正常为9或者10,过大则造成轮廓过小,应该再进行降噪2 p4 d( H4 c4 h' Q" o8 `/ H! h2 @
blur (src_e, blured, Size (3,3));//3*3内核降噪
& g* Q2 J+ A7 Y, F# g7 E' l4 A5 f# k imshow("腐蚀和降噪后的图片", blured); //显示图片: Y# O9 Z m& o) \9 B% w; W0 p/ A
int width=blured.rows; //图像的行列
. k" P( L6 k7 Y+ U. P. d9 v% m int height=blured.cols; //图像的列数量
1 {$ u1 d! x* r( }8 x" c$ \% D cout<<width<<endl; //显示行列的具体像素
$ ~. Z1 Q- K R cout<<height<<endl;
1 [8 f8 f Y: s$ ^9 ~" W: S1 Q int a[500][1]; //定义整型数组,后面的1应该可以不要的6 ?0 I, Y+ Y1 v( n
int b[500]; //设置一维数组,用于判断曲线的切线斜率
! j4 p2 `7 z- z- i0 E/ |! }$ M/ L6 ~, _. h6 a# r4 P* _
/// 将图片转换成灰度图片 Mat img = imread("11.jpg", IMREAD_GRAYSCALE); //在读取图片的同时直接转化成灰度图, 下一步是要将像素亮度超过一定阈值的点提取出来,并找到该点的坐标,然后记录该点坐标,用于后期的比对
1 @- a( N+ a- T! ^4 \- ^) C3 e( v cvtColor( blured, src_gray, CV_RGB2GRAY );
& s o' g$ L- C. {4 Q8 s. t& ~+ Z- G' r1 j- P6 l
/// 创建一个窗口显示图片1 v4 ~7 S, K# f8 k d* G; h0 C0 d
namedWindow( window_name, CV_WINDOW_AUTOSIZE );$ a( H5 N: G/ \) M3 {6 d7 g
. P& e! [% m# }/ Q8 n. O) s! s% A
/// 创建滑动条来控制阈值
4 k7 w- R0 y3 d6 c# E createTrackbar( trackbar_type, window_name, &threshold_type, max_type, Threshold_Demo);
; e/ C6 z% }6 ^# ?
, u0 W' ]8 k1 H; G0 o7 z: K: X9 F/ P createTrackbar( trackbar_value, window_name, &threshold_value, max_value, Threshold_Demo);0 G1 H) W. [- b: x% U; S) `
8 n) N, H' o- \ d
/// 初始化自定义的阈值函数
5 X( P2 s% W" w$ j1 V Threshold_Demo( 0, 0 );
5 W8 V& |' a# ^; {- C& R7 z v3 V$ U 4 C9 [; I+ K2 T0 a
// Mat img=src; //暂时无用& R( H) u6 I" o# K& K. R
//imshow("转化之后图片",dst);5 @" h% A7 V: Z+ @
1 {( L8 {9 f- a; y6 n: v
//遍历图片的每个像素点,当像素点的阈值大于227时,将最左侧的该像素地址保存在二维数组中,在该行之后的像素点抛弃,如果阈值低于227,则向下遍历至该行末,然后从下一行开始对像素进行比较0 a6 q" K" b0 T0 B
; F, U' n+ [$ g8 j$ D, _" F//Mat BW = imread(imgName); F: Q8 B @. K/ N$ j
//int value = BW.at<uchar>(191, 51);0 U+ x g/ K/ o. D( P( p
int width1=dst.rows; //处理之后图像的行列& ^9 g" f" R/ J: Y
int height1=dst.cols; //处理之后图像的列数量& X# e0 b# O. N! T, l: n
! g( R8 @ d+ h& J" e4 j8 k for (int i=0 ; i<height1; i++) //从第一行开始 应该从最后一行开始向上检索,这样可以减少计算量,一旦出现与之前曲线K值相反的方向,则确定是拐点,不用再考虑,但是要考虑出现切线斜率始终是减少的趋势,这种情况下往往是蒜尖9 f: o. Z9 A6 a: B0 X
{
0 P. i/ M+ ^7 A" { for (int j = 0; j < width1; j++) //从第一行的第一列开始. U% C2 W" ? e; z" X% J
{
4 G2 o' S0 {8 M1 R7 c# R# L% N //int index = i * width + j;
( @% _& k3 z+ j+ `4 r* i* Y b int value = dst.at<uchar>(i,j); //读取给定坐标处的像素值9 {) O- ?( F4 c( f: I" ^
//if; //像素值. l9 E K2 v0 l4 n- O: g. Y2 a
//int data = (int)dst.data[index];6 w7 {! J5 P1 N8 g# Y
if ( value >200) //如果像素值大于某个数值,则将其地址记录在数组内,且仅记录首像素,后面的舍弃
: U. i" {. I3 e; y" j; V3 E; S { - U1 s, L( @/ f) L
a[i][1]=j; //数组值等于列数,便于后期对比: g& e. w+ Y8 y6 w2 ~
//cout<<i<<" --- "<<j<<endl; //i为行数
/ Y V {' s# _/ m //cout<<i<<" -坐标-- "<<a[i][1]<<endl;* }. x3 _; v" q0 F. z: _/ l
if (i>1)
3 o. j' v4 |! R! ]! C { //116 r: F! X, e' T
if (a[i-1][1]<a[i][1]) //如果第一行中大于某个阈值的像素地址与下一行相比靠右,也就是列数比上一行要大,则说明该曲线向左侧倾斜,说
2 z6 Y" \, H) Q //明是底部,如果曲线向右侧弯曲,则是蒜尖 (之所以用i-1,是因为总不能和没有像素的地址对比,所以必须向后取值)
0 o8 b; e" ?* p. J { ; F$ h7 |7 z# M. C n& t1 x! P
b[i]=0; //因此,当下一行的地址比上一行的地址小,则用1表示,如果下一行地址比上一行大,用0表示,1是蒜尾,0是蒜尖。
/ |5 n& K; T/ ~" l6 a" X7 v; u B }, W8 q0 m! X8 q# G b2 H$ g7 o
else if (a[i-1][1]>=a[i][1])
3 {8 Z g9 {- K+ y {# K, n$ P+ e' x+ a; H) V( j
b[i]=1;
; o. r- V9 r" }0 I7 q& P& F }
$ C( q" O2 D" ^5 m/ y, s- ?, D) i' v9 n$ m5 {
cout<<i<<" -标识符-- "<<b[i]<<endl; ' [" X5 P( m l5 x0 Z+ l9 k
//cout<<j<<endl; //j为列数5 _; f$ M: W5 j( l0 l
} //11* s7 q7 R" e, n7 v# B' l. n
% m% i* }7 P% F
- S8 t4 F5 V" a6 c0 T, ?2 m, k+ @ break;
3 x" {! g p6 s) {$ U; @ }
3 D% T# Q$ _; U$ Z/ Y% K5 Q }
1 \" R5 w8 i" Z. i1 p }* H! _* R- [4 D' l& q3 T$ ?
//开始对b数组进行分析,确定是否为头尾(但是需要对曲线进行圆滑处理)) [4 d3 G5 p, ~3 A" ]+ W& A
for (int i=0 ; i<height1; i++) //对数组b进行遍历,检查是否存在误判的问题
9 U m' q2 R+ E" r& @ //寻找拐点,找到用于判断的后段曲线的拐点! N# f: f" X: q7 q* d
//对图形进行圆滑处理,除了最大的拐点之外,尽量不要出现折线
' P7 Z- E% N7 p* b7 U2 X" G : g: t( i: W T( `
: _4 `/ y3 ]# j# `0 z7 H3 B
1 i4 T& b, n) N) x H# ~+ i
// int width=dst.rows; //图像的行列
( F i: s8 A6 l1 G" X3 M1 i7 {6 F S //int height=dst.cols; //图像的列数量5 A5 L$ S1 t6 N+ @3 K3 m' d
cout<<width<<endl; //显示行列的具体像素
2 w1 r* ?* ~, y. G cout<<height<<endl;- ?7 R s ~4 Q! {. L$ M( M6 p
//for (int i =height1-5 ; i>20; i--) //对收集的坐标数据进行分析
% T" h" P0 n3 x8 }8 K
; f( u5 _0 o1 L+ L0 T: L // 等待用户按键。如果是ESC健则退出等待过程。7 t6 m9 \1 a2 O& T( E% h. S; e/ h, |( \
while (true)
. _2 Z, m/ [6 L4 j1 ~* g {. d/ Q% J' {9 s4 O
int c;( |; q* T: t& C# A3 ]
c = waitKey( 20 );
/ |9 v. b ]) l if( (char)c == 27 )
9 W% T; L1 y( `$ | k! j4 f( \& ` { break; }
; y* p2 s1 F( }- k4 Y% i. ^ }& J5 e$ W6 w( S# [7 T8 s& |
# B$ ^7 w: d" G, a' y# L
}
8 p7 m& b1 G- j1 P0 w( g3 x3 k8 h% \ \5 O. p8 V
! {) s* P4 b! V) {6 i
/**
S) S+ c u6 \* T6 E * @自定义的阈值函数
1 x' _/ H# k! Y D */
c- ~( V- K4 Y( F) mvoid Threshold_Demo( int, void* )- U. `$ O3 r$ w* X2 l6 x! `( W: M
{+ S/ k9 B3 G. s# {9 z
/* 0: 二进制阈值
7 B% H# q- F) E1 e 1: 反二进制阈值 H( b3 E0 P B; H8 g# J1 V0 Y
2: 截断阈值9 F5 X" f( n& C# ?, ~# n0 O) m/ m& \
3: 0阈值! F! h5 O& ~+ A/ j
4: 反0阈值# l0 y2 N, C2 I1 B/ c/ ]4 k
*/
3 H6 Z9 I$ t+ g& d( N1 ~, D1 g Q7 d4 ~. v. }; J0 z
threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type );
( [( P/ s, K& z1 H& L Y* Y. ^! L" U6 ?
imshow( window_name, dst );6 b9 y' x$ [/ K$ i( z
}' y8 n+ j: I. h: p% a
1 N! ?$ U6 N5 _5 c* H" T2 e
4 k2 z8 e# M. X6 \' h3 X( K0 W
; J$ `' ]& m% R, }, K. |, T3 K t* i' c0 n
. Q* `1 D6 H" }' I; \: l
/*+ ?+ A+ z" k+ D" K+ n8 @0 u
void main(): p3 e! \/ m* z) u- q
{: R! h) f5 D7 q/ a& C( j/ n# v
0 m3 P$ j% v2 x0 E6 J4 A; q //读入彩色图像 单通道图像(CV_8UC1);CV 8位未指定的1通道的图像,backImg是单通道灰色图5 @* V# E, @0 z# S$ g1 Z
( }5 S e( D6 Q: D) O //Mat img = imread("fruits.jpg");
6 h* \2 z6 v/ c. K% r( t Mat img = imread("11.jpg", IMREAD_GRAYSCALE); //在读取图片的同时直接转化成灰度图, 下一步是要将像素亮度超过一定阈值的点提取出来,并找到该点的坐标,然后记录该点坐标,用于后期的比对
4 C1 Y: u+ m2 x1 j5 I6 T+ j6 o imshow("灰度图", img);
; `* q" b- X. e! M: _ //确定图像的尺寸,行列,
# ]/ M: ~% `% R: @ int width=img.rows; //图片的变量名加上行列,就是行列的数据' @- {' L0 T9 m2 a4 B2 ?. j& j5 p
int height=img.cols;" v- t! W; o" Q% d( z" P7 N9 b) _/ `
cout << width << endl; //显示行列的数据 本图片已经用358的像素值裁剪了,所以形成的就是高宽都是358& @% ~+ i1 s6 Q
cout << height << endl;
' Z' u, |' X, x! i# P& ]9 l7 F //定义一个二维数组,a[i][1],其行数等于图像行数,列数就一列,用于记录图片上像素的亮度超过某个阈值的像素所在的列数,用于下一步的对比。/ g! q2 c C/ G4 _: \; u
int a[358][1]; //确定一个358的二维数组7 |0 |" [" `; p, z5 A* i( H
$ b+ L+ D: D6 X. V: p$ q3 e
//int height = backImg.rows; //backImg是图片变量名
$ w- b& e/ y1 @8 d//int width = backImg.cols;
9 G# _/ s/ c4 L$ J+ K: s2 a5 y8 Jfor (int i = 0; i < height; i++)% p* Q- o" w7 O6 n) [
{! k* _) _- h, m' s8 z" n
for (int j = 0; j < width; j++)
! o3 T& f# E6 H {
. [6 u+ d. q4 Y, H( y% v int index = i * width + j;
* d( `3 i- n0 G4 d; s //像素值/ z- K+ d/ J; }/ p5 w& z! J
int data = (int)img.data[index];2 M; q# b/ y; Q9 H. @
}' J3 i+ S) Y$ }5 R( m0 L# T) n
}
0 H5 L: | _; V# T waitKey();3 K# Q7 s3 U# ^1 s# @
}
/ F- a: z; C. R7 M2 c' l*/ |
|