实验要求
找到目标颜色所在区域
算法实现
-
色度空间转换
1.1 读取图像并滤波
1.2 根据公式将图像从rgb转换到hsv色度空间
-
颜色分割
2.1 利用 createTrackbar()函数建立滑动条,对颜色空间转换后的各通道进行阈值分割
2.2 根据阈值分割的结果,判断各种颜色的对应阈值
2.3 针对不同颜色,分别对图像进行阈值分割
2.4 对阈值分割结果进行数学统计,判断图像的颜色并输出分类结果
-
目标颜色检测
3.1 对图像进行预处理,消除噪声并获取二值化图
3.2 对二值图进行轮廓检测;
3.3 根据任务目标选择合适的多边形描述轮廓;
3.4 获取多边形区域后,从原图中截取该区域图像;
3.5 对多边形区域的图像进行颜色分割,对分割结果进行统计,判断图像的颜色并输出分类结果
代码实现
#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;
}
运行结果