前面我讲述了直方图的概念以及如何用opencv实现一维和二维的直方图。详见这两篇blog: Mat 格式:http://blog.csdn.net/lu597203933/article/details/16884409
cv:http://blog.csdn.net/lu597203933/article/details/14104505
这次主要想讲点直方图的应用,其中包括使用查找表修改图像的外观、直方图的均衡化、反投影直方图检测特定图像的内容、meanshift算法<均值漂移>跟踪物体和利用图像直方图检索相似图像<可靠性比较低>。
一:使用查找表修改图像的外观
查找表是简单的一对一(或者多对一)函数,定义了如何将像素值转换为新像素值。Opencv的cv::LUT对图像应用查找表以生成新图像。公式为: result[i] = lookup[image[i]].
<1>图像反转
Code:
Main.cpp
- MatND hist = getHistogram(image);
- int i;
- for(i = 0; i < 256; i++)
- {
- cout << "Value" << i << ": " << hist.at<float>(i) << endl;
- }
- /*int dim(256);
- Mat lut(1, &dim, CV_8U);
- for(i = 0; i < 256; i++)
- {
- lut.at<uchar>(i) = 255 - i;
- }
- Mat result = applyLookUp(image, lut);
Result:
<2>提高图像对比度
以下代码定义了一个提高图像对比度的查找表。方法就是通过拉伸直方图,能得到扩展后的对比度。
Code:
得到图像的直方图:
- MatND getHistogram(Mat &image)
- {
- MatND hist;
- int channels[] = {0};
- int dims = 1;
- int histSize[] = {256};
- float granges[] = {0, 255};
- const float *ranges[] = {granges};
- calcHist(&image, 1, channels, Mat(), hist, dims, histSize, ranges);
- return hist;
- }
将图像的直方图展示出来:
- Mat getHistogramImage(Mat &image, int scaleX = 1, int scaleY = 1)
- {
- MatND hist = getHistogram(image);
- Mat showImage(scaleY * 256, scaleX*256, CV_8U,Scalar(255));
- int i;
- double maxValue = 0;
- minMaxLoc(hist, 0, &maxValue, 0, 0);
- for(i = 0; i < 256; i++)
- {
- float value = hist.at<float>(i);
- int intensity = saturate_cast<int>(256*scaleY - 256*scaleY * (value/maxValue));
- rectangle(showImage, Point(i*scaleX, scaleY*256 - 1), Point((i+1)*scaleX-1, intensity), Scalar(0));
- }
- return showImage;
- }
得到并应用查找表:
- Mat stretch(Mat &image, int minValue = 0)
- {
-
- MatND hist = getHistogram(image);
- int imin = 0,i = 0;
-
- for(; i < 256; i ++)
- {
- float iminValue = hist.at<float>(i);
- if(iminValue > minValue)
- {
- imin = i;
- break;
- }
- }
-
- int imax = 255;
- for(i = 255; i >= 0 ; i--)
- {
- float imaxValue = hist.at<float>(i);
- if(imaxValue > minValue){
- imax = i;
- break;
- }
- }
-
- Mat lookUp(1, 256, CV_8U);
-
- for(i = 0; i < 256; i++)
- {
-
- if(i < imin)
- lookUp.at<uchar>(i) = 0;
- else if(i > imax)
- lookUp.at<uchar>(i) = 255;
- else
- lookUp.at<uchar>(i) = saturate_cast<uchar>(255.0*(i - imin)/(imax - imin));
- }
-
- Mat result;
- LUT(image, lookUp, result);
- return result;
- }
- Mat applyLookUp(Mat &image, Mat& lut)
- {
- Mat result;
- LUT(image, lut, result);
- return result;
- }
Main.cpp
- int main()
- {
- Mat image = imread("F:\\group.jpg", 0);
- if(!image.data)
- {
- cout << "fail to load image" << endl;
- return 0;
- }
- Mat showImage = getHistogramImage(image);
- Mat result = stretch(image, 100);
- Mat showImage2 = getHistogramImage(result);
- namedWindow("image");
- imshow("image", image);
- namedWindow("showImage");
- imshow("showImage", showImage);
- namedWindow("showImage2");
- imshow("showImage2", showImage2);
- namedWindow("result");
- imshow("result", result);
- waitKey(0);
- return 0;
- }
Result:
二:直方图均衡化
直方图均衡化的目的也是提高图像的对比度,但它与通过查找表提高对比度方法不同。通过拉伸直方图,使它覆盖所有的取值范围。这个策略确实可以简单有效地提升图像质量。然而,大多数情况下,图像在视觉上的缺陷并非源于过窄的强度范围,而是由于某些颜色值出现的频率高于另外一些。事实上,一副高质量的图片应该均衡使用所有的像素值。
Code:
Main.cpp
- int main()
- {
- Mat image = imread("F:\\group.jpg", 0);
- if(!image.data)
- {
- cout << "fail to load image" << endl;
- return 0;
- }
- Mat result;
- equalizeHist(image, result);
- namedWindow("image");
- imshow("image", image);
- namedWindow("result");
- imshow("result", result);
- Mat imageHist = getHistogramImage(image);
- Mat resultHist = getHistogramImage(result);
-
- namedWindow("imageHist");
- imshow("imageHist", imageHist);
- namedWindow("resultHist");
- imshow("resultHist", resultHist);
-
- waitKey(0);
- return 0;
- }
Explaination:
opencv2中提供的函数为equalizeHist();它的原理是这样的,在一副完全均衡的直方图中,所有的容器拥有同等数量的像素。公式为:
lookup.at<uchar>(i)= static_cast<uchar>(255*p[i]);;其中p[i]代表强度值小于i的像素所占的比例。
Result:
三:反投影直方图检测特定图像的内容
如果一副图像的区域中显示的是一种独特的纹理或者是一个独特的物体,那么这个区域的直方图可以看做一个概率函数,它给出了某个像素属于该纹理或者物体的概率。直白的说,就是在一副图像中将与某特定感兴趣的区域全部找出来,使用的是概率。
Code:
Main.cpp:
- int main()
- {
- Mat image = imread("F:\\clound.jpg", 0);
- if(!image.data)
- {
- cout << "Fail to load image" << endl;
- return 0;
- }
- Mat imageROI = image(Rect(80, 120, 40, 50));
- MatND histImage = getHistogram(imageROI);
- normalize(histImage, histImage, 1.0);
- int channels[] = {0};
- Mat result;
- float granges[] = {0, 255};
- const float *ranges[] = {granges};
- calcBackProject(&image, 1, channels, histImage, result, ranges, 255.0);
-
-
-
- namedWindow("image", 0);
- imshow("image", image);
- namedWindow("result", 0);
- imshow("result", result);
- waitKey(0);
- return 0;
- }
Result:
四:使用meanshift算法<均值漂移>跟踪物体
Meanshift算法的具体介绍可以看我转载的一篇blog:http://blog.csdn.net/lu597203933/article/details/17042331 它就是以迭代的方式锁定概率函数的局部最大值
主要分为这三个步骤:首先需要得到目标的直方图,其次用该直方图对图片进行反投影,后对反投影的图像进行meanshift算法。其中迭代的次数和精度作为收敛的条件。
Code:
-
- MatND getHueHistogram(Mat &image, int minSaturation = 0)
- {
- MatND hist;
- Mat hsvColor;
- cvtColor(image, hsvColor, CV_BGR2HSV);
- vector<Mat> hsv;
- split(hsvColor, hsv);
- Mat mask;
- if(minSaturation > 0)
- {
- threshold(hsv[1], mask, minSaturation, 255, THRESH_BINARY);
- int channels[] = {0};
- int histSize[] = {181};
- float hRanges[] = {0, 180};
- const float *ranges[] = {hRanges};
- int dims = 1;
- calcHist(&hsv[0], 1, channels, mask, hist, dims, histSize, ranges);
- }
- normalize(hist, hist, 1.0);
- return hist;
- }
-
-
- Mat getBackProject(Mat &image, MatND colorHist)
- {
- int channels[] = {0};
- Mat result;
- float hRanges[] = {0, 180};
- const float *ranges[] = {hRanges};
- calcBackProject(&image, 1, channels, colorHist, result, ranges, 255);
- int thre = 60;
- threshold(result, result, 60, 255,THRESH_BINARY);
- return result;
- }
- int main()
- {
-
- Mat image = imread("F:\\4.jpg",1);
-
- Mat imageROI = image(Rect(230, 320, 60, 35));
-
- int minSat = 65;
- MatND colorHist = getHueHistogram(imageROI, minSat);
-
-
- Mat image2 = imread("F:\\10.jpg", 1);
-
- Mat hsvColor;
- cvtColor(image2, hsvColor, CV_BGR2HSV);
- vector<Mat> hsv;
- split(image2, hsv);
-
- Mat result = getBackProject(hsv[0], colorHist);
-
- threshold(hsv[1], hsv[1], minSat, 255, THRESH_BINARY);
- bitwise_and(result, hsv[1], result);
-
- Rect rect(230, 320, 60, 35);
- rectangle(image, rect, Scalar(0,0,255));
- rectangle(image2, rect, Scalar(0,0,255));
-
- TermCriteria criteria(TermCriteria::MAX_ITER, 10, 0.01);
-
- meanShift(result, rect, criteria);
- rectangle(image2, rect, cv::Scalar(0,255,0));
- namedWindow("image");
- imshow("image", image);
- namedWindow("result");
- imshow("result", result);
- namedWindow("image2", 0);
- imshow("image2", image2);
- waitKey(0);
- return 0;
-
- }
Explaination:
<1>代码中我们使用HSV颜色空间中的色调分量以描述所要搜索的物体。
<2>还需要注意的是如果一个颜色的饱和度偏低,会导致色调信息变得不稳定以及不可靠。这是因为低饱和度的颜色中,红绿蓝三个分量几乎是相等的。
<3>meanshift:intcvMeanShift( const CvArr* prob_image, CvRect window, CvTermCriteria criteria,CvConnectedComp* comp );
参数
prob_image
目标直方图的反向投影(见 cvCalcBackProject).
window
初始搜索窗口
criteria
确定窗口搜索停止的准则
五:通过比较直方图检索相似图片
基本思想就是得到两幅图像的直方图,然后通过opencv提供的函数compareHist来得到它们的相似程度,返回的是个double值。
Code:
- int main()
- {
- Mat imageSource = imread("F:\\test\\tongtong.jpg", 1);
- colorReduce(imageSource, 32);
- MatND sourceHist = getHistogram(imageSource);
- stringstream ss;
- string str;
- string strBest = "";
- double minDistance = 256*100*100*100;
- for(int i = 1; i < 10; i++)
- {
- str = "F:\\test\\";
- ss.clear();
- ss << str;
- ss << i;
- ss << ".jpg";
- ss >> str;
- Mat imageDst = imread(str, 1);
- colorReduce(imageDst, 1);
- MatND dstHist = getHistogram(imageDst);
- double distance = compareHist(sourceHist, dstHist, CV_COMP_INTERSECT);
- if(distance < minDistance)
- {
- strBest = str;
- minDistance = distance;
- }
- }
- Mat best = imread(strBest,1);
- namedWindow(strBest);
- imshow(strBest,best);
- waitKey(0);
- return 0;
-
- }
Explaination:
<1>代码中使用了降低颜色数。这是因为直方图的比较大多数都是基于逐个容器的,即比较直方图容器时并不考虑相邻容器的影像。因此,测量相似度之前减少颜色空间是很重要的。
<2>compareHist函数直接明了,只需提供两个直方图,函数边返回测量距离。通过一个标志参数可以指定测量方法。代码中的参数CV_COMP_INTERSECT表示交叉测量法。即简单比较每个直方图容器的值,并保留最小的一个。相似性测量值只是这些最小值的和。
作者:小村长 出处:http://blog.csdn.net/lu597203933 欢迎转载或分享,但请务必声明文章出处。 (新浪微博:小村长zack, 欢迎交流!)