|
#include<iostream>
& |, Q# X7 O. x8 m2 \* @#include<opencv2/core/core.hpp>
6 L0 u/ ^% @( w/ p3 C$ X2 t#include<opencv2/highgui/highgui.hpp>5 G! p. u8 p( n1 l6 w* ?7 A
#include <opencv2/opencv.hpp>+ O. R+ p) C$ t. L# s- R
#include <opencv2/imgproc/imgproc.hpp>+ C; F0 M3 Y; \+ z9 x4 s+ ~' X
#include <opencv2/imgproc/types_c.h>
) o7 O* H' E5 P% s( o1 F#include <stdlib.h>5 \% y$ I7 a, [3 l9 F
#include <stdio.h>7 z) S% z1 e6 c+ s
#include <opencv2/highgui/highgui_c.h>$ Y, S* e$ ^' n( g% ?7 ]; C
#include <math.h>9 a; A# I5 r: R
//#include "iostream"6 Z$ B' S# X G/ W3 S$ u
//#include "cv.h"
7 ]9 ~ H' W0 D) U: { u//#include "highgui.h", z& K# d6 B& P7 E6 p
//#include "math.h"
( T# L) K* z; M* l8 D" g4 s+ U' @using namespace cv; //命名空間CV o% ^9 v( J) W" {! b3 @. w& F
using namespace std; //命名空間 std- o4 L0 q8 J! o
& ^6 B7 J0 [% V/ {/ T
int threshold_value = 225; //啟動程序時的閾值初始值,因為227能夠完全提取輪廓,所以設置為225,實際上227也可以。
8 D9 G8 q& w, r; |% Q( }# q9 rint threshold_type = 3; //啟動程序的時候閾值類型,默認的為不足部分取零% B% a# Q7 c. ~( \; u
int const max_value = 255;- M( _; }" q1 H: j" y; g
int const max_type = 4;
, n4 S' U( i; s/ d: t" Uint const max_BINARY_value = 255;6 g2 P6 d5 d' T% Y. ]- f+ k, F
9 z; `" b/ k' b* m; @0 o# g4 s
CvFont font;) s# x+ V% ^+ a" F3 U8 x1 P/ c, [
uchar* ptr;
3 N5 K' u; @0 x" _( y+ Q4 X char label[20];
. {8 _% n# E8 S6 C8 K3 x char label2[20];
6 f9 c1 o$ p* l. E
" E- ]' e3 |! D. ~+ f ~% JMat src, blured, src_e, src_gray, dst; //類定義幾個圖片變量,dst是最后轉化閾值之后的圖片,src.gray是灰度圖
- H+ D) ^9 s0 N8 t$ X c //在C語言中“char*”是聲明一個字符類型du的指針,定義數據類型,char可以定義字符zhi有變量、數組、指針。dao. ~9 _8 v& J' _7 r
//例如:char *string="I love C#!"
! H6 q+ Q6 |0 _9 t //定義了一個字符指針變量string,用字符串常量"I love C#!",對它進行初始化。對字符指針變量初始化,實際上就是把字符串第1個元素的地址(即存放字符串的字符數組的首元素地址)賦給string。*/+ w5 C T+ t3 ~- _0 d' Z( B0 f
Mat element = getStructuringElement(MORPH_RECT, Size(5,5)); //用于腐蝕的參數
z' U N; Y; |- z- F' F; X. ~char* window_name = "閾值演示程序20201121";
2 O5 p: |, x- I0 {- q9 }; Nchar* trackbar_type = "類型: \n 0: 二值化 \n 1: 反二值化 \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted"; //
- C) x. l m/ _3 W3 h: w& D4 g) Qchar* trackbar_value = "數值";
8 |4 g3 g/ l; U5 F
# d/ O' Y! b9 i A$ K/// 自定義函數聲明
3 ?( p" T4 v# t3 p, l) x6 Vvoid Threshold_Demo( int, void* );8 V0 K% c' L) j% y$ x3 P
+ u0 @; {7 ~5 o6 b7 r: f/**0 B. L( w3 V; j5 y- R3 U& i
* @主函數
9 \, B- G6 ]- t */5 \6 I9 H0 w. U/ c% ]
int main( int argc, char** argv )1 G: Y/ p: P( B& A9 R7 g6 C% V* W
{
/ r6 b0 A7 ~- l/ N& |( s /// 讀取一副圖片,不改變圖片本身的顏色類型(該讀取方式為DOS運行模式)
7 |- W5 q7 I# |( W src = imread("121.JPG", 1); //目前還未使用攝像頭拍攝照片,暫時以直接讀取文件的方式來測試。
9 _/ }, y* v+ p erode (src, src_e, element); //對圖片進行腐蝕,參數可調,正常為9或者10,過大則造成輪廓過小,應該再進行降噪
8 t1 B1 {- [4 D6 H4 I" p8 L blur (src_e, blured, Size (3,3));//3*3內核降噪+ V# _: h& Q( P, m9 [
imshow("腐蝕和降噪后的圖片", blured); //顯示圖片
, N( D) V9 g5 W int width=blured.rows; //圖像的行列. h! v/ \5 K: e( j: f9 W9 ]
int height=blured.cols; //圖像的列數量
) f, I8 _0 I! b0 ^: j cout<<width<<endl; //顯示行列的具體像素
3 y( Y+ {! Q% n8 p8 M cout<<height<<endl;
( L$ U: M- Y5 a# |. g) v( k. k2 S int a[500][1]; //定義整型數組,后面的1應該可以不要的/ G& ~6 b, f9 f( Z6 n3 v: L, q
int b[500]; //設置一維數組,用于判斷曲線的切線斜率
( d! n: Q( z; }6 g# L2 p2 A/ O( h* _& B; ?& `
/// 將圖片轉換成灰度圖片 Mat img = imread("11.jpg", IMREAD_GRAYSCALE); //在讀取圖片的同時直接轉化成灰度圖, 下一步是要將像素亮度超過一定閾值的點提取出來,并找到該點的坐標,然后記錄該點坐標,用于后期的比對
: _" x! n6 V/ U( C, ~ cvtColor( blured, src_gray, CV_RGB2GRAY );
. a6 F' P6 V: z% j/ u2 v- s* L# G
. q3 O5 _' F# i2 p0 Y z- U' W$ a /// 創建一個窗口顯示圖片
9 @- E+ M$ @* N. ?& g( D0 i8 l namedWindow( window_name, CV_WINDOW_AUTOSIZE );
+ \5 U4 u2 |% ^# w% j& |, `) G; S8 K% \4 v! N- n X j: {) }
/// 創建滑動條來控制閾值
8 q: X8 ^$ W8 r/ w( z$ s' {. Y createTrackbar( trackbar_type, window_name, &threshold_type, max_type, Threshold_Demo);# H- @( j- H# l9 b* d
% w- v/ k0 I' J9 t# S6 Y& t$ `
createTrackbar( trackbar_value, window_name, &threshold_value, max_value, Threshold_Demo);/ g% H o" Y0 w, b; J. `
) y1 H3 ~. r* |- a
/// 初始化自定義的閾值函數. m8 ~) d" W) M; R0 S" b
Threshold_Demo( 0, 0 );( S; Q c- Q- i" ~
4 m( T9 v; s& t6 i" w, x0 v; @$ @
// Mat img=src; //暫時無用
$ R( @9 U8 b/ o7 m1 o3 z2 F //imshow("轉化之后圖片",dst);
, q* _$ D2 a% D, r8 T- U9 {+ Q+ C5 X' V0 K0 H+ t
//遍歷圖片的每個像素點,當像素點的閾值大于227時,將最左側的該像素地址保存在二維數組中,在該行之后的像素點拋棄,如果閾值低于227,則向下遍歷至該行末,然后從下一行開始對像素進行比較
/ P7 D1 h% ?% `4 T2 z
+ ~7 k; S4 I) M7 q Y2 h//Mat BW = imread(imgName);4 L) f$ ~. w+ W% D2 e
//int value = BW.at<uchar>(191, 51);# p! S+ _. O& S5 ]
int width1=dst.rows; //處理之后圖像的行列
+ ^5 i9 ~+ U# i# I* f* X* \- ] int height1=dst.cols; //處理之后圖像的列數量( M4 T( i6 Q' c& \! N
. m, n/ J% ]* L$ l
for (int i=0 ; i<height1; i++) //從第一行開始 應該從最后一行開始向上檢索,這樣可以減少計算量,一旦出現與之前曲線K值相反的方向,則確定是拐點,不用再考慮,但是要考慮出現切線斜率始終是減少的趨勢,這種情況下往往是蒜尖
0 p1 S6 t" ^. H. r. I" H {
* ^0 d' y" C* ?4 s$ I for (int j = 0; j < width1; j++) //從第一行的第一列開始" H. X; y" h' ^; K
{
( S$ v9 u$ c2 j* }" H) A //int index = i * width + j;8 _ q! g7 ^' X3 }' _" \8 R
int value = dst.at<uchar>(i,j); //讀取給定坐標處的像素值
/ `5 L, o+ A; z; g0 x //if; //像素值3 K! Z' @ P( p2 o, ^. E& O
//int data = (int)dst.data[index];
+ N1 K* [$ T0 a* m- a: p0 z5 ~ if ( value >200) //如果像素值大于某個數值,則將其地址記錄在數組內,且僅記錄首像素,后面的舍棄
* G7 x% H5 F" N# v6 A7 g {
+ O. t- k7 ]. E3 L6 I a[i][1]=j; //數組值等于列數,便于后期對比( I% I1 p. a7 D/ y+ S9 l
//cout<<i<<" --- "<<j<<endl; //i為行數( N9 b( g3 s5 h6 I/ Q) {
//cout<<i<<" -坐標-- "<<a[i][1]<<endl;
( V6 c1 B7 g6 P- p6 E if (i>1)$ g( k; ~& i5 y2 J7 C1 v. Y. y
{ //11' C: R- ^( |0 i
if (a[i-1][1]<a[i][1]) //如果第一行中大于某個閾值的像素地址與下一行相比靠右,也就是列數比上一行要大,則說明該曲線向左側傾斜,說
, E* l( d* m. ~2 @; U# [$ c //明是底部,如果曲線向右側彎曲,則是蒜尖 (之所以用i-1,是因為總不能和沒有像素的地址對比,所以必須向后取值)
% o$ y1 s# r8 t8 k& G3 F { 1 { O7 X* {( C. W' F# j9 K {
b[i]=0; //因此,當下一行的地址比上一行的地址小,則用1表示,如果下一行地址比上一行大,用0表示,1是蒜尾,0是蒜尖。+ J! R4 |. b; _- p5 k% @
}# m+ b0 F: a5 q2 ]( h1 {, W
else if (a[i-1][1]>=a[i][1])
3 Z) i* v8 a' d' t) K, a {
9 Q1 o+ { F" U0 T* Z2 P$ f b[i]=1;/ A9 M# C- R; ]
}
; p6 p# s! I2 v2 H5 r
0 L4 A/ [7 ^7 @1 M cout<<i<<" -標識符-- "<<b[i]<<endl;
* X$ P' f5 ~8 _5 X- t0 G //cout<<j<<endl; //j為列數) R1 c" y; l! q) @: [9 U
} //11
+ G! i$ \& \, E. h7 {4 K
! x0 T) a7 i# _# `0 C! g' ^4 }5 C! j9 j, a
break; / e( c4 D. F7 e% [. w0 g3 x
}- c7 C2 `- b6 P( W: K! {
}* X5 D1 u- ~! z+ Q, X
}2 t: V/ [( M C7 Q* i' U; b
//開始對b數組進行分析,確定是否為頭尾(但是需要對曲線進行圓滑處理)- z c0 d- f, m8 x3 I W2 _# M* O
for (int i=0 ; i<height1; i++) //對數組b進行遍歷,檢查是否存在誤判的問題+ W; C6 \5 e/ T2 g
//尋找拐點,找到用于判斷的后段曲線的拐點
* ?% Q% S4 e, ^* R; L9 @# ^/ I //對圖形進行圓滑處理,除了最大的拐點之外,盡量不要出現折線
3 \" H5 A+ `( x0 l
% t# h% }; s2 Y: i: B! m- c % k& y3 M, {- z. U# P( G) O* e2 p5 X
% u6 g" |0 B/ K! z. P
// int width=dst.rows; //圖像的行列7 F. f$ ~& ?* b2 u4 U0 Z# G! E( u+ U
//int height=dst.cols; //圖像的列數量
7 `3 f' G3 Y7 b4 E0 J1 E" d# {" P cout<<width<<endl; //顯示行列的具體像素. e# u! E4 S, S- q- F/ {5 b
cout<<height<<endl;
$ ~ g* A& D2 p% G) \1 x7 ~ //for (int i =height1-5 ; i>20; i--) //對收集的坐標數據進行分析) g. q% _8 X2 b$ ]! n5 ]
0 a( [; J4 M5 y4 k1 O6 W // 等待用戶按鍵。如果是ESC健則退出等待過程。& X8 f. J7 r3 Q
while (true)
( j$ s& X. X8 @" { M {+ n' c' a% d# z
int c;
; P) Y- R' d. C. Y0 S7 S. J5 D c = waitKey( 20 );
+ g0 q# ]8 M1 e1 n4 `2 V0 n if( (char)c == 27 )
2 k* U! g4 q1 \$ H { break; }) t& |1 n, k1 {& F
}. ]1 C5 K8 j6 u9 c K1 \2 r
7 J8 r8 y0 X$ \$ Y( Q0 E& L* ?% M* j- W; d
}
( c! V- a0 y7 [" a B4 p
! R* b1 j6 s4 F" t( b! t4 Z2 o! L
" d3 k$ w( x6 T1 E% f/**
% Z6 o8 n, {' [& i * @自定義的閾值函數- m+ p c. {; F2 f
*/
) n( Q& P& ]! fvoid Threshold_Demo( int, void* )) a3 ~1 ^. y/ f# k4 Q/ T/ U. a
{
. _! ^' a7 ~6 j( N V. \1 L /* 0: 二進制閾值' }" T0 G& k& f; t9 n7 U6 W
1: 反二進制閾值5 Q: w/ O( ?, J: G
2: 截斷閾值7 |% Q& }% e% J4 B/ B# L
3: 0閾值/ N: \6 T, k+ s7 s& I+ h a
4: 反0閾值
, {9 t K8 S }: r. |* t* J8 ] */
7 \% v* Z/ V8 l1 i& I* z. c/ ~( t$ E3 M. ^1 t
threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type );
& u. }8 M H+ _( s, M# l; Q* L: ~( a4 G/ u. o9 |3 c" t
imshow( window_name, dst );
s" j) M. X9 `( r2 `" k, V- t6 |}, E7 R1 M8 j! n- |2 o! d6 @; c
A! I1 D; E5 k5 C& h, u5 L# s5 I4 q6 }
5 t7 _. X& ~+ f$ b, e) r4 h3 z& c0 C
5 Z% i* \. E: E( i! F- K/ n
3 }' I) y: Z, c1 p& K3 j/*
/ D0 S; P9 j& W- [' [' Tvoid main()
6 i" b/ K% B5 v/ {6 |* }4 R- @{
+ {; c+ q+ t$ {* a, k" W' z( A
. F9 } ^8 v) A5 X. j3 T& x! Y //讀入彩色圖像 單通道圖像(CV_8UC1);CV 8位未指定的1通道的圖像,backImg是單通道灰色圖
! C, \$ j/ _* W! p: J7 z / S1 H. A0 Y4 t8 Z0 k
//Mat img = imread("fruits.jpg");
( _) r0 K W- Z3 s1 M, [ Mat img = imread("11.jpg", IMREAD_GRAYSCALE); //在讀取圖片的同時直接轉化成灰度圖, 下一步是要將像素亮度超過一定閾值的點提取出來,并找到該點的坐標,然后記錄該點坐標,用于后期的比對3 q! U& P+ {& m
imshow("灰度圖", img);
: V( Z8 S7 k# ~- s! q8 L4 P //確定圖像的尺寸,行列,
* F& `7 ?9 m( \+ n6 G- r1 v int width=img.rows; //圖片的變量名加上行列,就是行列的數據7 w0 g4 h4 a; G" S$ ]/ I2 ]7 D
int height=img.cols;
4 h1 y2 c- T2 l3 Y( t0 K8 z cout << width << endl; //顯示行列的數據 本圖片已經用358的像素值裁剪了,所以形成的就是高寬都是358
% _$ ~0 `3 \8 } cout << height << endl;
4 O! } u. c# f2 N' O/ z //定義一個二維數組,a[i][1],其行數等于圖像行數,列數就一列,用于記錄圖片上像素的亮度超過某個閾值的像素所在的列數,用于下一步的對比。' [1 _1 \: A; B1 j2 a ]
int a[358][1]; //確定一個358的二維數組2 M* s1 }' g- S2 I
! e9 k# K! D! _ T% H d; x7 e
//int height = backImg.rows; //backImg是圖片變量名
% @ I0 W0 v/ n6 c( \# k) a//int width = backImg.cols;
l$ d# R$ W i, J) J# z7 ffor (int i = 0; i < height; i++)1 H: w3 }% s4 k3 F! L; u
{
- n; _+ w( ?! E3 d for (int j = 0; j < width; j++)% u) G& @1 y' F( m8 f+ r8 v
{' X1 c) u2 d8 L
int index = i * width + j;8 ^- g. O% y) G6 O# A' J4 m! w
//像素值
0 V M9 b( ^7 X1 D: q8 O int data = (int)img.data[index];" r" y/ ^' P4 V
}8 S) T0 P5 v% S" \1 c* g" T
}1 Z- m" E, E+ N
waitKey();) L2 B, k6 q5 O+ m& D
}9 @7 T2 S3 F$ N& E. v( k
*/ |
|