数字图像处理之目标颜色识别

实验要求

找到目标颜色所在区域

算法实现

  1. 色度空间转换
    1.1 读取图像并滤波
    1.2 根据公式将图像从rgb转换到hsv色度空间
    rgb2hsv公式
  2. 颜色分割
    2.1 利用 createTrackbar()函数建立滑动条,对颜色空间转换后的各通道进行阈值分割
    2.2 根据阈值分割的结果,判断各种颜色的对应阈值
    2.3 针对不同颜色,分别对图像进行阈值分割
    2.4 对阈值分割结果进行数学统计,判断图像的颜色并输出分类结果
    hsv参考表
  3. 目标颜色检测
    3.1 对图像进行预处理,消除噪声并获取二值化图
    3.2 对二值图进行轮廓检测;
    3.3 根据任务目标选择合适的多边形描述轮廓;
    3.4 获取多边形区域后,从原图中截取该区域图像;
    3.5 对多边形区域的图像进行颜色分割,对分割结果进行统计,判断图像的颜色并输出分类结果

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#include <stdlib.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#define LINEAR_X 0

#define SIZE 3
#define PI 3.1415926
#define HMaxValue 255
#define SMaxValue 255
#define VMaxValue 255
#define oo 1e9+7
using namespace std;
using namespace cv;
int H_min=100, H_max=124, S_min=43, S_max=255, V_min=46, V_max=255;
Mat img_in;
void Gaussian(Mat input, Mat output, double sigma){
double weight;//权重
double sum = 0;
double Gaussian_Temp[SIZE][SIZE] = {0};//模板
weight = (2*PI*sigma*sigma);
for(int i =0;i <SIZE;i++)
{
for(int j = 0;j < SIZE;j++)
{
int x = i - SIZE/2;
int y = j - SIZE/2;
Gaussian_Temp[i][j] =exp(-(x*x+y*y)/(2.0*sigma*sigma))/weight;
sum += Gaussian_Temp[i][j];
}
}

for(int i = 0; i < SIZE;i++)
{
for(int j = 0;j < SIZE;j++)
{
Gaussian_Temp[i][j] = Gaussian_Temp[i][j]/sum;//归一化处理
//printf("%f ",Gaussian_Temp[i][j]);
}
//printf("\n");
}

int rows = output.rows;
int cols = output.cols;
int channels = input.channels();
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
int sum3[3]={0};
int sum1 = 0;
for(int k=0;k<SIZE;k++)
for(int t=0;t<SIZE;t++)
{
int x=i+k-SIZE/2;
int y=j+t-SIZE/2;
if(x<0||y<0||x>=rows||y>=cols)continue;
double m=Gaussian_Temp[k][t];
if(channels == 1)sum1+=m*input.at<uchar>(x,y);
else if(channels == 3)
{
Vec3b rgb = input.at<Vec3b>(x,y);
sum3[0]+=m*rgb[0];
sum3[1]+=m*rgb[1];
sum3[2]+=m*rgb[2];

}
}
if(channels == 3){
for(int k=0;k<3;k++){
if(sum3[k]>255)sum3[k]=255;
if(sum3[k]<0)sum3[k]=0;
output.at<Vec3b>(i,j)[k]=sum3[k];
}
}
else {
if(sum1>255)sum1=255;
if(sum1<0)sum1=0;
output.at<uchar>(i,j)=sum1;
}
}
}
}
void RBG2HSV(Mat input,Mat output){
int rows=input.rows;
int cols=input.cols;
//cout<<rows<<" "<<cols<<endl;
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
Vec3b pix=input.at<Vec3b>(i,j);//012:BGR
float b=1.0*pix[0]/255;
float g=1.0*pix[1]/255;
float r=1.0*pix[2]/255;
float maxrgb=max(r,max(g,b));
float minrgb=min(r,min(g,b));
float diff=maxrgb-minrgb;
float v=maxrgb;
float s=(diff/v);
float h;
if(maxrgb-minrgb<1e-5)h=0;
else if(maxrgb==r)h=60*(g-b)/diff;
else if(maxrgb==g)h=60*(b-r)/diff+120;
else if(maxrgb==b)h=60*(r-g)/diff+240;
if(h<0)h+=360;
else if(h>359)h-=360;
output.at<Vec3b>(i,j)[0]=(int)(h*180/360);
output.at<Vec3b>(i,j)[1]=(int)(s*255);
output.at<Vec3b>(i,j)[2]=(int)(v*255);

}
}
}
/*
画颜色统计图
*/
void Statistical(Mat input){
int weigth=210,height=300;
Mat output=Mat::zeros(height,weigth,CV_8UC3);
int rows=input.rows;
int cols=input.cols;
int colorNum[7]={0};
int sum=rows*cols;
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
Vec3b pix=input.at<Vec3b>(i,j);//012:HSV
if(pix[1]<43||pix[2]<46)continue;
//pix[0]=pix[0]*180/255;
int color=0;
if((pix[0]>=0&&pix[0]<=10)||(pix[0]>=156&&pix[0]<=180))color=0;
else if(pix[0]>=11&&pix[0]<=25)color=1;
else if(pix[0]>=26&&pix[0]<=34)color=2;
else if(pix[0]>=35&&pix[0]<=77)color=3;
else if(pix[0]>=78&&pix[0]<=99)color=4;
else if(pix[0]>=100&&pix[0]<=124)color=5;
else if(pix[0]>=125&&pix[0]<=155)color=6;
colorNum[color]++;
}
}

Scalar Color[7]={Scalar(0,0,255),Scalar(0,125,255),Scalar(0,255,255),Scalar(0,255,0),Scalar(255,255,0),Scalar(255,0,0),Scalar(255,0,255)};
for(int i=0;i<7;i++){
int h=colorNum[i]*height/sum;
rectangle(output,Point(i*30,height),Point((i+1)*30-1, height-h),Color[i],-1);
}
imshow("color",output);
}
void colorDetection(Mat input){
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( input, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0) );
if(contours.size()==0)return;
int area=0,x=0,y=0,a=0,b=0,c=0,d=0;
for(int k=0;k<contours.size();k++){
RotatedRect rectPoint = minAreaRect(contours[k]);
Point2f point[4];
//将rectPoint变量中存储的坐标值放到 point的数组中
rectPoint.points(point);
//寻找轮廓的左上角和右下角,算出长宽
int max_x=0,max_y=0,min_x=oo,min_y=oo;
for (int i = 0; i < 4; i++)
{
if (max_x < point[i].x)max_x=int(point[i].x+0.5);
if (max_y < point[i].y)max_y=int(point[i].y+0.5);
if (min_x > point[i].x)min_x=(int)point[i].x;
if (min_y > point[i].y)min_y=(int)point[i].y;

}
if(min_x<0)min_x=0;
if(min_y<0)min_y=0;
if(max_x>=input.cols)max_x=input.cols-1;
if(max_y>=input.rows)max_y=input.rows-1;
if((max_x-min_x)*(max_y-min_y)>area){
area=(max_x-min_x)*(max_y-min_y);
x=min_x;
y=min_y;
a=max_x;
b=max_y;
c=max_x-min_x;
d=max_y-min_y;
}
}

if(c==0||d==0)return;
//截取ROI区域
Mat output=img_in(Rect(x,y,c,d));
imshow("ROI",output);
Mat hsv=Mat::zeros(output.size(),CV_8UC3);
RBG2HSV(output,hsv);
//统计截取之后的图像各种颜色的含量
Statistical(hsv);
}
/*
颜色分割
*/
void thresholdSeq(int var,void* usrdata){
Mat input = *(static_cast<Mat*> (usrdata));
Mat output=Mat::zeros(input.size(),CV_8UC1);
int rows=input.rows;
int cols=input.cols;
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
Vec3b pix=input.at<Vec3b>(i,j);//012:HSV
if(pix[0]<H_min||pix[0]>H_max)continue;
if(pix[1]<S_min||pix[1]>S_max)continue;
if(pix[2]<V_min||pix[2]>V_max)continue;
output.at<uchar>(i,j)=255;
}
}
imshow("thresholdSeq",output);
//目标颜色检测
colorDetection(output);
}
int main(int argc, char **argv)
{
//读取原始图像
img_in=imread(argv[1],IMREAD_UNCHANGED);
//检查是否读取图像
if(img_in.empty()){
cout<<"Error! Input image cannot be read...\n";
return -1;
}

imshow("src",img_in);
Mat frOut1=Mat::zeros(img_in.size(),CV_8UC3);
Mat frOut2=Mat::zeros(img_in.size(),CV_8UC1);
// 空域滤波函数
Gaussian(img_in,frOut1,1);
// 色度空间转换
RBG2HSV(frOut1,frOut1);
//阈值分割
namedWindow("thresholdSeq");
createTrackbar("H_min", "thresholdSeq", &H_min, HMaxValue, thresholdSeq,&frOut1);
createTrackbar("H_max", "thresholdSeq", &H_max, HMaxValue, thresholdSeq,&frOut1);
createTrackbar("S_min", "thresholdSeq", &S_min, SMaxValue, thresholdSeq,&frOut1);
createTrackbar("S_max", "thresholdSeq", &S_max, SMaxValue, thresholdSeq,&frOut1);
createTrackbar("V_min", "thresholdSeq", &V_min, VMaxValue, thresholdSeq,&frOut1);
createTrackbar("V_max", "thresholdSeq", &V_max, VMaxValue, thresholdSeq,&frOut1);
waitKey();
return 0;
}

运行结果

原图
hsv
杰尼龟
妙蛙种子
喷火龙