cv1.6hist直方图操作

作者 群号 C语言交流中心 240137450  微信 15013593099


直方图均衡理论研究

我们先来看看原图的直方图

我们发现高亮区域的像素点很少,主要像素点集中在中低亮度区域

我们先设置一个阈值,也就是图中的那根粉色的线,

当某一亮度值的像素点的个数低于这个值时,我们认为这些像素点是无关紧要的。

灰度图的亮度值范围是0-255,若亮度值为1的像素点的个数低于阈值,我们可简单的把亮度为1的像素点的亮度

全设为0,同理,我们从高往低找,若亮度值为254的像素点的个数低于阈值,我们可以把这些像素点的亮度设为255

这样我们可以从小到大,从大到小分别找到两个亮度,它们的像素点的个数恰大于阈值

他们之间的区域,我们可以认为是有效区域,也就是蓝色框出来的区域

我们把这一区域扩展到0-255的区域去,可实现均衡化效果

编程实现为

cv::Mat Histogram::stretch1(const cv::Mat& image, int minValue) {
    cv::MatND hist = getHistogram(image);
    int imin =0;
    for (; imin < histSize[0]; imin++) {
        if (hist.at<float>(imin) > minValue) {
            break;
        }
    }
    int imax = histSize[0] -1;
    for (; imax >=0; imax--) {
        if (hist.at<float>(imax) > minValue) {
            break;
        }
    }
    cv::Mat lookup(cv::Size(1, 256), CV_8U);
    for (int i =0; i <256; i++) {
        if (i < imin) {
            lookup.at<uchar>(i) =0;
        } elseif (i > imax) {
            lookup.at<uchar>(i) =255;
        } else {
            lookup.at<uchar>(i) = static_cast<uchar>(255.0* (i - imin)
                    / (imax - imin) +0.5);}
        }
    cv::Mat result;
    cv::LUT(image, lookup, result);
    return result;
}

对于cv::LUT函数,我之前就介绍过了

可以看出拉伸后的直方图和原直方图形状是一致的

 

再来看看另一种直方图均衡化的思路

理想的直方图均衡化效果是希望每个亮度的像素点的个数都相同

我们设原亮度为 i 的点均衡化后亮度为S(i),原亮度为 i 的点的个数为N(i)

 其占总像素点的概率为p(i) = N(i) / SUM; SUM为像素点的总和

可以得到公式

S(0) = p(0)*255

S(1) = [p(0)+p(1)]*255

S(2) = [p(0)+p(1)+p(2)]*255

........

S(255) = [p(0)+p(1)+......+p(255)]*255 = 255

我们在原图中将亮度为 i 的像素点赋值为 S(i),就可以实现均衡化了

cv::Mat Histogram::stretch2(const cv::Mat& image) {
    cv::MatND hist = getHistogram(image);
    float scale[256];
    float lookupF[256];
    cv::Mat lookup(cv::Size(1, 256), CV_8U);
    int pixNum = image.cols * image.rows;
    for (int i =0; i <256; i++) {
        scale[i] = hist.at<float>(i) / pixNum *255;
        if (i ==0) {
            lookupF[i] = scale[i];
        } else {
            lookupF[i] = lookupF[i -1] + scale[i];
        }
    }
    for (int i =0; i <256; i++) {
        lookup.at<uchar>(i) = static_cast<uchar>(lookupF[i]);
    }
    cv::Mat result;
    cv::LUT(image, lookup, result);
    return result;
}

cv::Mat Histogram::stretch3(const cv::Mat& image) {
    cv::Mat result;
    cv::equalizeHist(image, result);
    return result;
}


在这里,我们定义了两个函数,一个按照刚才的思路来实现

另一个是OpenCV2 提供的标准的均衡化函数

我们来看看效果

两种方法得到的效果和直方图的形状几乎一模一样

可见,标准的均衡化方法也是按此思路实现的

具体的源代码就不研究了


反投影直方图以检测待定的图像内容



#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#include "Histogram1D.h"


class ObjectFinder {

private:

  float hranges[2];
  const float* ranges[3];
  int channels[3];

  float threshold;
  cv::MatND histogram;
  cv::SparseMat shistogram;

public:

  ObjectFinder() : threshold(0.1f){

    ranges[0]= hranges; 
    ranges[1]= hranges; 
    ranges[2]= hranges; 
  }

  // 设置阈值
  void setThreshold(float t) {

    threshold= t;
  }

  // 返回阈值
  float getThreshold() {

    return threshold;
  }

  // 设置目标直方图,进行归一化
  void setHistogram(const cv::MatND& h) {
    histogram= h;
    cv::normalize(histogram,histogram,1.0);
  }

  // 查找属于目标直方图概率的像素
  cv::Mat find(const cv::Mat& image) {

    cv::Mat result;

    hranges[0]= 0.0;	
    hranges[1]= 255.0;
    channels[0]= 0;		
    channels[1]= 1; 
    channels[2]= 2; 

    cv::calcBackProject(&image,
        1,            
        channels,     
        histogram,    
        result,       
        ranges,   
        255.0       
      );

    // 通过阈值投影获得二值图像
    if (threshold>0.0)
      cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);

    return result;
  }

};
int main()
{
  //读取圆图像
  cv::Mat initimage= cv::imread("f:\\img\\skin.jpg");
  if (!initimage.data)
    return 0; 

  //显示原图像
  cv::namedWindow("原图像");
  cv::imshow("原图像",initimage);

  //读取灰度图像
  cv::Mat image= cv::imread("f:\\img\\skin.jpg",0);
  if (!image.data)
    return 0; 

  //设置目标区域
  cv::Mat imageROI;
  imageROI= image(cv::Rect(262,151,113,150)); // 区域为小孩的脸部区域

  //显示目标区域
  cv::namedWindow("目标区域图像");
  cv::imshow("目标区域图像",imageROI);

  //计算目标区域直方图
  Histogram1D h;
  cv::MatND hist= h.getHistogram(imageROI);
  cv::namedWindow("目标区域直方图");
  cv::imshow("目标区域直方图",h.getHistogramImage(imageROI));

  //创建检查类
  ObjectFinder finder;
  //将目标区域直方图传入检测类
  finder.setHistogram(hist);

  //初始化阈值
  finder.setThreshold(-1.0f);

  //进行反投影
  cv::Mat result1;
  result1= finder.find(image);

  //创建负图像并显示概率结果
  cv::Mat tmp;
  result1.convertTo(tmp,CV_8U,-1.0,255.0);
  cv::namedWindow("负图像概率结果图像越暗概率越大");
  cv::imshow("负图像概率结果图像越暗概率越大",tmp);

  //得到二值反投影图像
  finder.setThreshold(0.01f);
  result1= finder.find(image);

  //在图像中绘制选中区域
  cv::rectangle(image,cv::Rect(262,151,113,150),cv::Scalar(0,0,0));

  //显示原图像
  cv::namedWindow("原图像的灰度图");
  cv::imshow("原图像的灰度图",image);

  //二值结果图
  cv::namedWindow("二值结果图");
  cv::imshow("二值结果图",result1);

  cv::waitKey();
  return 0;
}

Histogram1D.h


#if !defined HISTOGRAM  
#define HISTOGRAM  
  
#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <iostream>  
  
using namespace std;  
using namespace cv;  
  
  
class Histogram1D  
{  
private:  
      
    //直方图的点数  
    int histSize[1];  
    //直方图的范围  
    float hranges[2];  
    //指向该范围的指针  
    const float* ranges[1];  
    //通道  
    int channels[1];  
      
  
public:  
    //构造函数  
    Histogram1D()  
    {  
         histSize[0] = 256;  
         hranges[0] = 0.0;  
         hranges[1] = 255.0;  
         ranges[0] = hranges;  
         channels[0] = 0;  
  
    }  
  
    Mat getHistogram(const Mat &image)  
    {  
        Mat hist;  
        //计算直方图函数  
        //参数为:源图像(序列)地址,输入图像的个数,通道数,掩码,输出结果,直方图维数,每一维的大小,每一维的取值范围  
        calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);  
        //这个函数虽然有很多参数,但是大多数时候,只会用于灰度图像或者彩色图像  
        //但是,允许通过指明一个多通道图像使用多幅图像  
        //第6个参数指明了直方图的维数  
        return hist;  
    }  
  
    Mat getHistogramImage(const Mat &image)  
    {  
        //首先计算直方图  
        Mat hist = getHistogram(image);  
  
        //获取最大值和最小值  
        double maxVal = 0;  
        double minVal = 0;  
        //minMaxLoc用来获得最大值和最小值,后面两个参数为最小值和最大值的位置,0代表不需要获取  
        minMaxLoc(hist,&minVal,&maxVal,0,0);  
        //展示直方图的画板:底色为白色  
        Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));  
  
        //将最高点设为bin总数的90%  
        //int hpt = static_cast<int>(0.9*histSize[0]);  
        int hpt = static_cast<int>(histSize[0]);  
        //为每一个bin画一条线  
        for(int h = 0; h < histSize[0];h++)  
        {  
            float binVal = hist.at<float>(h);  
            int intensity = static_cast<int>(binVal*hpt/maxVal);  
            //int intensity = static_cast<int>(binVal);  
            line(histImg,Point(h,histSize[0]),Point(h,histSize[0]-intensity),Scalar::all(0));  
              
        }  
        return histImg;  
    }  
  
    Mat applyLookUp(const Mat& image,const Mat& lookup)  
    {  
        Mat result;  
        LUT(image,lookup,result);  
        return result;  
    }  
  
  
    Mat strech(const Mat &image,int minValue = 0)  
    {  
        //首先计算直方图  
        Mat hist = getHistogram(image);  
        //左边入口  
        int imin = 0;  
        for(;imin< histSize[0];imin++)  
        {  
            cout<<hist.at<float>(imin)<<endl;  
            if(hist.at<float>(imin) > minValue)  
                break;  
  
        }  
        //右边入口  
        int imax = histSize[0]-1;  
        for(;imax >= 0; imax--)  
        {  
            if(hist.at<float>(imax) > minValue)  
                break;  
        }  
  
        //创建查找表  
        int dim(256);  
        Mat lookup(1,&dim,CV_8U);  
          
        for(int 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) = static_cast<uchar>(255.0*(i-imin)/(imax-imin)+0.5);  
            }  
        }  
        Mat result;  
        result = applyLookUp(image,lookup);  
        return result;  
  
    }  
    Mat equalize(const Mat &image)  
    {  
        Mat result;  
        equalizeHist(image,result);  
        return result;  
    }  
  
};  
#endif

形状特征

#include "stdafx.h"
#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include <cv.h>  
#include <cxcore.h>  
#include <highgui.h>  
#include <iostream>  
using namespace std;  
  
int main()  
{  
    IplImage *src = cvLoadImage("c:\\img\\f.jpg",0);  
    IplImage *image = cvCreateImage(cvGetSize(src),8,3);   
    image = cvCloneImage(src);  
    cvNamedWindow("src",1);  
    cvNamedWindow("dst",1);  
    cvShowImage("src",src);  
  
    CvMemStorage *storage = cvCreateMemStorage(0);  

    CvSeq * seq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);  
    CvSeq * tempSeq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);  
    //新图,将轮廓绘制到dst  
    IplImage *dst = cvCreateImage(cvGetSize(src),8,3);   
    cvZero(dst);//赋值为0  
    double length,area;  
  
    //获取轮廓  
    int cnt = cvFindContours(src,storage,&seq);//返回轮廓的数目  
    cout<<"number of contours   "<<cnt<<endl;  
      
    //计算边界序列的参数 长度 面积 矩形 最小矩形   
    //并输出每个边界的参数  
    CvRect rect;  
    CvBox2D box;  
    double axislong,axisShort;//长轴和短轴  
    double temp1= 0.0,temp2 = 0.0;  
    double Rectangle_degree;//矩形度  
    double long2short;//体态比  
    double x0,y0;  
    long sumX  = 0 ,sumY = 0;  
    double sum =0.0;  
    int i,j,m,n;  
    unsigned char* ptr;  
  
    double UR;//区域重心到轮廓的平均距离  
    double PR;//区域重心到轮廓点的均方差  
    CvPoint * contourPoint;   
    int count = 0;  
    double CDegree;//圆形性  
  
    CvPoint *A,*B,*C;  
    double AB,BC,AC;  
    double cosA,sinA;  
    double tempR,inscribedR;  
      
    for (tempSeq = seq;tempSeq != NULL; tempSeq = tempSeq->h_next)  
    {  
        //tempSeq = seq->h_next;  
        length = cvArcLength(tempSeq);  
        area = cvContourArea(tempSeq);  
        cout<<"Length = "<<length<<endl;  
        cout<<"Area = "<<area<<endl;  
        cout<<"num of point "<<tempSeq->total<<endl;  
        //外接矩形  
        rect = cvBoundingRect(tempSeq,1);  
          
  
        //绘制轮廓和外接矩形  
        cvDrawContours(dst,tempSeq,CV_RGB(255,0,0),CV_RGB(255,0,0),0);  
        cvRectangleR(dst,rect,CV_RGB(0,255,0));  
        cvShowImage("dst",dst);  
        //cvWaitKey();  
  
        //绘制轮廓的最小外接圆   
        CvPoint2D32f center;//亚像素精度 因此需要使用浮点数  
        float radius;  
        cvMinEnclosingCircle(tempSeq,¢er,&radius);  
        cvCircle(dst,cvPointFrom32f(center),cvRound(radius),CV_RGB(100,100,100));  
        cvShowImage("dst",dst);  
        //cvWaitKey();  
  
        //寻找近似的拟合椭圆 可以使斜椭圆  
        CvBox2D ellipse = cvFitEllipse2(tempSeq);  
        cvEllipseBox(dst,ellipse,CV_RGB(255,255,0));  
        cvShowImage("dst",dst);  
        //cvWaitKey();  
  
  
        //绘制外接最小矩形  
        CvPoint2D32f pt[4];  
        box = cvMinAreaRect2(tempSeq,0);  
        cvBoxPoints(box,pt);  
        for(int i = 0;i<4;++i){  
            cvLine(dst,cvPointFrom32f(pt[i]),cvPointFrom32f(pt[((i+1)%4)?(i+1):0]),CV_RGB(0,0,255));  
        }  
        cvShowImage("dst",dst);  
        //cvWaitKey();  
  
  
        //下面开始分析图形的形状特征   
        //长轴 短轴  
        temp1 = sqrt(pow(pt[1].x -pt[0].x,2) + pow(pt[1].y -pt[0].y,2));  
        temp2 = sqrt(pow(pt[2].x -pt[1].x,2) + pow(pt[2].y -pt[1].y,2));  
  
        if (temp1 > temp2)  
        {  
            axislong = temp1;  
            axisShort=temp2;  
        }   
        else  
        {  
            axislong = temp2;  
            axisShort=temp1;  
        }  
          
        cout<<"long axis: "<<axislong<<endl;  
        cout<<"short axis: "<<axisShort<<endl;  
        //矩形度 轮廓面积和最小外接矩形面积(可以是斜矩形)之比  
        Rectangle_degree = (double)area/(axisShort*axislong);  
  
        cout<<"Rectangle degree :"<<Rectangle_degree<<endl;  
        //体态比or长宽比 最下外接矩形的长轴和短轴的比值  
        long2short = axislong/axisShort;  
        cout<<"ratio of long axis to short axis: "<<long2short<<endl;  
        //球状性 由于轮廓的内切圆暂时无法求出先搁置   
        //先求内切圆半径 枚举任意轮廓上的三个点,半径最小的就是内切圆的半径  
        //以下的最大内切圆半径求法有误 待改进  
          
        /* 
        for (int i = 0 ; i< tempSeq->total -2;i++) 
        { 
            for (int j= i+1; j<tempSeq->total-1;j++) 
            { 
                for (int m = j+1; m< tempSeq->total; m++) 
                { 
                    //已知圆上三点,求半径 
                    A = (CvPoint*)cvGetSeqElem(tempSeq ,i); 
                    B = (CvPoint*)cvGetSeqElem(tempSeq ,j); 
                    C = (CvPoint*)cvGetSeqElem(tempSeq,m); 
                    AB = sqrt(pow((double)A->x - B->x,2)+ pow((double)A->y - B->y,2)); 
                    AC =sqrt(pow((double)A->x - C->x,2) + pow((double)A->y - C->y,2)); 
                    BC = sqrt(pow((double)B->x - C->x,2)+ pow((double)B->y - C->y,2)); 
 
                    cosA = ((B->x - A->x)*(C->x - A->x) + (B->y - A->y)*(C->y - A->y))/(AB*AC); 
                    sinA = sqrt(1 - pow(cosA,2)); 
                    tempR = BC/(2*sinA); 
 
                    if (m == 2) 
                    { 
                        inscribedR = tempR; 
                    }  
                    else 
                    { 
                        if (tempR < inscribedR) 
                        { 
                            inscribedR = tempR; 
                        } 
                    } 
 
 
                } 
            } 
        } 
 
        //输出最大内切圆半径 
        cout<<"radius of max inscribed circle  "<<inscribedR<<endl; 
        */  
        //圆形性 假设轮廓内是实心的  
        //球区域中心x0 y0  
        sumX = 0;  
        sumY = 0;  
        src = cvCloneImage(image);  
        for (int i = 0 ; i< src->height;i++)  
        {  
            for (int j = 0; j< src->width;j++)  
            {  
                ptr = (unsigned char *)src->imageData + i*src->widthStep + j;  
                if ((*ptr) > 128)  
                {  
                    sumX += (long)j;  
                    sumY += (long)i;  
                }  
  
            }  
        }  
        x0 = sumX/area;  
        y0 = sumY/area;  
        cout<<"center of gravity "<<x0<<"   "<<y0<<endl;  
     //求区域到重心的平均距离  
        sum = 0;  
        count = 0;  
        for (m = 0 ; m< tempSeq->total;m++)  
        {  
            contourPoint = (CvPoint*)cvGetSeqElem(tempSeq,m);  
            sum += sqrt(pow(contourPoint->x - x0,2)+ pow(contourPoint->y - y0,2));  
            count++;  
        }  
        UR = sum/count;  
        cout<<"mean distance to center of gravity"<<UR<<endl;  
        //求区域重心到轮廓点的均方差  
        sum = 0;  
        for (m = 0 ; m< tempSeq->total;m++)  
        {  
            contourPoint = (CvPoint*)cvGetSeqElem(tempSeq,m);  
            temp1 = sqrt(pow(contourPoint->x - x0,2)+ pow(contourPoint->y - y0,2));  
            sum += pow(temp1 - UR,2);  
        }  
        PR = sum/count;  
        cout<<"mean square error of distance to center of gravity"<<PR<<endl;  
        //圆形性   
        CDegree= UR/PR;  
        cout<<"degree of circle "<<CDegree<<endl;  
        //中心距  
          
        cvWaitKey(0);  
    }  
      
      
    cvReleaseImage(&src);  
    cvReleaseImage(&dst);  
    cvReleaseMemStorage(&storage);    
      
    return 0;  
}  



使用均值漂移算法查找物体


#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#include "Histogram1D.h"

#include <iostream>
#include <vector>


#include "ContentFinder.h"
#include "colorhistogram.h"

int main()
{
  //读取參考图像
  cv::Mat image= cv::imread("f:\\img\\ball.jpg");
  if (!image.data)
    return 0; 

  //定义查找物体
  cv::Mat imageROI= image(cv::Rect(85,200,64,64));
  cv::rectangle(image, cv::Rect(85,200,64,64),cv::Scalar(0,0,255));

  //显示參考图像
  cv::namedWindow("第一张图片,标记篮球位置");
  cv::imshow("第一张图片,标记篮球位置",image);

  //获得色度直方图
  ColorHistogram hc;
  cv::MatND colorhist= hc.getHueHistogram(imageROI);

  //读入目标图像
  image= cv::imread("f:\\img\\ball2.jpg");

  //显示目标图像
  cv::namedWindow("第二张图片");
  cv::imshow("第二张图片",image);

  //将RGB图像图像转换为HSV图像
  cv::Mat hsv;
  cv::cvtColor(image, hsv, CV_BGR2HSV);

  //分离图像通道
  vector<cv::Mat> v;
  cv::split(hsv,v);

  //消除饱和度较低的像素点
  int minSat=65;
  cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
  cv::namedWindow("第二张图片消除饱和度较低的像素点");
  cv::imshow("第二张图片消除饱和度较低的像素点",v[1]);

  //进行直方图反投影
  ContentFinder finder;
  finder.setHistogram(colorhist);
  finder.setThreshold(0.3f);
  int ch[1]={0};
  cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);

  cv::namedWindow("第二张图片进行直方图反投影");
  cv::imshow("第二张图片进行直方图反投影",result);

  //利用位运算消除低饱和度像素
  cv::bitwise_and(result,v[1],result);
  cv::namedWindow("第二张图片利用位运算进一步消除低饱和度像素点");
  cv::imshow("第二张图片利用位运算进一步消除低饱和度像素点",result);

  // 得到反投影直方图概率图像
  finder.setThreshold(-1.0f);
  result= finder.find(hsv,0.0f,180.0f,ch,1);
  cv::bitwise_and(result,v[1],result);
  cv::namedWindow("第二张图片处理后的二值图像");
  cv::imshow("第二张图片处理后的二值图像",result);

  cv::Rect rect(85,200,64,64);
  cv::rectangle(image, rect, cv::Scalar(0,0,255));

  cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
  cout << "均值漂移迭代次数 = " << cv::meanShift(result,rect,criteria) << endl;

  cv::rectangle(image, rect, cv::Scalar(0,255,0));

  //展示结果图
  cv::namedWindow("查找结果,红框为第一幅图中篮球位置,绿框为现位置");
  cv::imshow("查找结果,红框为第一幅图中篮球位置,绿框为现位置",image);

  cv::waitKey();
  return 0;
}

    #if!defined CONTENTFINDER  
    #define CONTENTFINDER  
      
    #include <opencv2/core/core.hpp>  
    #include <opencv2/highgui/highgui.hpp>  
    #include <opencv2/imgproc/imgproc.hpp>  
      
    using namespace cv;  
      
    class ContentFinder  
    {  
    private:  
        float hranges[2];  
        const float* ranges[3];  
        int channels[3];  
        float threshold;  
        Mat histogram;  
    public:  
        ContentFinder():threshold(-1.0f)  
        {  
            //所有通道的范围相同  
            ranges[0] = hranges;  
            ranges[1] = hranges;   
            ranges[2] = hranges;  
        }  
      
        //设置门限参数[0,1]  
        void setThreshold(float t)  
        {  
            threshold = t;  
        }  
      
        //获取门限参数  
        float getThreshold()  
        {  
            return threshold;  
        }  
      
        //设置参考的直方图  
        void setHistogram(const Mat& h)  
        {  
            histogram = h;  
            normalize(histogram,histogram,1.0);  
        }  
      
        //简单的利用反向投影直方图寻找  
        Mat find(const Mat& image)  
        {  
            Mat result;  
            hranges[0] = 0.0;  
            hranges[1] = 255.0;  
            channels[0] = 0;  
            channels[1] = 1;  
            channels[2] = 2;  
      
            calcBackProject(&image,1,channels,histogram,result,ranges,255.0);  
            if (threshold>0.0)  
            {  
                cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);  
            }  
      
            return result;  
        }  
      
        //复杂的利用反向投影直方图,增加了一些参数  
        Mat find(const Mat &image,float minValue,float maxValue,int *channels,int dim)  
        {  
            Mat result;  
            hranges[0] = minValue;  
            hranges[1] = maxValue;  
            for(int i = 0;i < dim;i++)  
            {  
                this->channels[i] = channels[i];  
            }  
            calcBackProject(&image,1,channels,histogram,result,ranges,255.0);  
            if(threshold >0.0)  
                cv::threshold(result,result, 255*threshold,255,THRESH_BINARY);  
            return result;  
      
        }  
    };  
    #endif  


#if!defined COLORHISTOGRAM  
    #define COLORHISTOGRAM  
      
    #include <opencv2/core/core.hpp>  
    #include <opencv2/imgproc/imgproc.hpp>  
      
    using namespace cv;  
      
    class ColorHistogram  
    {  
    private:  
        int histSize[3];  
        float hranges[2];  
        const float* ranges[3];  
        int channels[3];  
    public:  
      
        //构造函数  
        ColorHistogram()  
        {  
            histSize[0]= histSize[1]= histSize[2]= 256;  
            hranges[0] = 0.0;  
            hranges[1] = 255.0;  
            ranges[0] = hranges;  
            ranges[1] = hranges;  
            ranges[2] = hranges;  
            channels[0] = 0;  
            channels[1] = 1;  
            channels[2] = 2;  
        }  
      
        //计算彩色图像直方图  
        Mat getHistogram(const Mat& image)  
        {  
            Mat hist;  
      
            //BGR直方图  
            hranges[0]= 0.0;      
            hranges[1]= 255.0;  
            channels[0]= 0;   
            channels[1]= 1;   
            channels[2]= 2;   
      
            //计算  
            calcHist(&image,1,channels,Mat(),hist,3,histSize,ranges);  
            return hist;  
        }  
      
        //计算颜色的直方图  
        Mat getHueHistogram(const Mat &image)  
        {  
            Mat hist;  
            Mat hue;  
            //转换到HSV空间  
            cvtColor(image,hue,CV_BGR2HSV);  
      
            //设置1维直方图使用的参数  
            hranges[0] = 0.0;  
            hranges[1] = 180.0;  
            channels[0] = 0;  
            //计算直方图  
            calcHist(&hue,1,channels,Mat(),hist,1,histSize,ranges);  
            return hist;  
      
        }  
      
        //减少颜色  
        Mat colorReduce(const Mat &image,int div = 64)  
        {  
            int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));  
            uchar mask = 0xFF<<n;  
            Mat_<Vec3b>::const_iterator it = image.begin<Vec3b>();  
            Mat_<Vec3b>::const_iterator itend = image.end<Vec3b>();  
            //设置输出图像  
            Mat result(image.rows,image.cols,image.type());  
            Mat_<Vec3b>::iterator itr = result.begin<Vec3b>();  
            for(;it != itend;++it,++itr)  
            {  
                (*itr)[0] = ((*it)[0]&mask) + div/2;  
                (*itr)[1] = ((*it)[1]&mask) + div/2;  
                (*itr)[2] = ((*it)[2]&mask) + div/2;  
            }  
            return result;  
        }  
      
    };  
      
      
    #endif  

#if !defined HISTOGRAM  
#define HISTOGRAM  
  
#include <opencv2/core/core.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <iostream>  
  
using namespace std;  
using namespace cv;  
  
  
class Histogram1D  
{  
private:  
      
    //直方图的点数  
    int histSize[1];  
    //直方图的范围  
    float hranges[2];  
    //指向该范围的指针  
    const float* ranges[1];  
    //通道  
    int channels[1];  
      
  
public:  
    //构造函数  
    Histogram1D()  
    {  
         histSize[0] = 256;  
         hranges[0] = 0.0;  
         hranges[1] = 255.0;  
         ranges[0] = hranges;  
         channels[0] = 0;  
  
    }  
  
    Mat getHistogram(const Mat &image)  
    {  
        Mat hist;  
        //计算直方图函数  
        //参数为:源图像(序列)地址,输入图像的个数,通道数,掩码,输出结果,直方图维数,每一维的大小,每一维的取值范围  
        calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);  
        //这个函数虽然有很多参数,但是大多数时候,只会用于灰度图像或者彩色图像  
        //但是,允许通过指明一个多通道图像使用多幅图像  
        //第6个参数指明了直方图的维数  
        return hist;  
    }  
  
    Mat getHistogramImage(const Mat &image)  
    {  
        //首先计算直方图  
        Mat hist = getHistogram(image);  
  
        //获取最大值和最小值  
        double maxVal = 0;  
        double minVal = 0;  
        //minMaxLoc用来获得最大值和最小值,后面两个参数为最小值和最大值的位置,0代表不需要获取  
        minMaxLoc(hist,&minVal,&maxVal,0,0);  
        //展示直方图的画板:底色为白色  
        Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));  
  
        //将最高点设为bin总数的90%  
        //int hpt = static_cast<int>(0.9*histSize[0]);  
        int hpt = static_cast<int>(histSize[0]);  
        //为每一个bin画一条线  
        for(int h = 0; h < histSize[0];h++)  
        {  
            float binVal = hist.at<float>(h);  
            int intensity = static_cast<int>(binVal*hpt/maxVal);  
            //int intensity = static_cast<int>(binVal);  
            line(histImg,Point(h,histSize[0]),Point(h,histSize[0]-intensity),Scalar::all(0));  
              
        }  
        return histImg;  
    }  
  
    Mat applyLookUp(const Mat& image,const Mat& lookup)  
    {  
        Mat result;  
        LUT(image,lookup,result);  
        return result;  
    }  
  
  
    Mat strech(const Mat &image,int minValue = 0)  
    {  
        //首先计算直方图  
        Mat hist = getHistogram(image);  
        //左边入口  
        int imin = 0;  
        for(;imin< histSize[0];imin++)  
        {  
            cout<<hist.at<float>(imin)<<endl;  
            if(hist.at<float>(imin) > minValue)  
                break;  
  
        }  
        //右边入口  
        int imax = histSize[0]-1;  
        for(;imax >= 0; imax--)  
        {  
            if(hist.at<float>(imax) > minValue)  
                break;  
        }  
  
        //创建查找表  
        int dim(256);  
        Mat lookup(1,&dim,CV_8U);  
          
        for(int 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) = static_cast<uchar>(255.0*(i-imin)/(imax-imin)+0.5);  
            }  
        }  
        Mat result;  
        result = applyLookUp(image,lookup);  
        return result;  
  
    }  
    Mat equalize(const Mat &image)  
    {  
        Mat result;  
        equalizeHist(image,result);  
        return result;  
    }  
  
};  
#endif


通过直方图比较检索相似图片

CompareHist(),是比较两个统计直方图的分布,总共有四个方法,被定义如下:

#define CV_COMP_CORREL 0
#define CV_COMP_CHISQR 1
#define CV_COMP_INTERSECT2
#define CV_COMP_BHATTACHARYYA3

而这些方法分别为相关系数,卡方,交集法以及在做常态分布比对的Bhattacharyya距离,这些方法都是用来做统计直方图的相似度比较的方法,而且,都是根据统计学的概念,这边就简单的拿来用灰阶统计直方图来比较,而这部份的比较方式,是由图形的色彩结构来着手,下面就简单的用三种情况来分析它们距离比较的方式

    #include "opencv2/highgui/highgui.hpp"  
    #include "opencv/cv.hpp"  
      
    //画直方图用  
    int HistogramBins = 256;  
    float HistogramRange1[2]={0,255};  
    float *HistogramRange[1]={&HistogramRange1[0]};  
      
    /* 
     * imagefile1: 
     * imagefile2: 
     * method: could be CV_COMP_CHISQR, CV_COMP_BHATTACHARYYA, CV_COMP_CORREL, CV_COMP_INTERSECT 
     */  
    int CompareHist(const char* imagefile1, const char* imagefile2)  
    {  
        IplImage *image1=cvLoadImage(imagefile1, 0);  
        IplImage *image2=cvLoadImage(imagefile2, 0);  
      
        CvHistogram *Histogram1 = cvCreateHist(1, &HistogramBins, CV_HIST_ARRAY,HistogramRange);  
        CvHistogram *Histogram2 = cvCreateHist(1, &HistogramBins, CV_HIST_ARRAY,HistogramRange);  
      
        cvCalcHist(&image1, Histogram1);  
        cvCalcHist(&image2, Histogram2);  
      
        cvNormalizeHist(Histogram1, 1);  
        cvNormalizeHist(Histogram2, 1);  
      
        // CV_COMP_CHISQR,CV_COMP_BHATTACHARYYA这两种都可以用来做直方图的比较,值越小,说明图形越相似  
        printf("CV_COMP_CHISQR : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_CHISQR));  
        printf("CV_COMP_BHATTACHARYYA : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_BHATTACHARYYA));  
      
      
        // CV_COMP_CORREL, CV_COMP_INTERSECT这两种直方图的比较,值越大,说明图形越相似  
        printf("CV_COMP_CORREL : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_CORREL));  
        printf("CV_COMP_INTERSECT : %.4f\n", cvCompareHist(Histogram1, Histogram2, CV_COMP_INTERSECT));  
      
        cvReleaseImage(&image1);  
        cvReleaseImage(&image2);  
        cvReleaseHist(&Histogram1);  
        cvReleaseHist(&Histogram2);  
        return 0;  
    }  
      
    int main(int argc, char* argv[])  
    {  
        CompareHist(argv[1], argv[2]);  
        //CompareHist("d:\\camera.jpg", "d:\\camera1.jpg");  
        system("pause");
        return 0;  
    }  

图1


图2

结果

亮度变换

#include "opencv\\cv.h"
#include "opencv\\highgui.h"
/*
src and dst are grayscale, 8-bit images;
Default input value: 
           [low, high] = [0,1]; X-Direction
           [bottom, top] = [0,1]; Y-Direction
           gamma ;
if adjust successfully, return 0, otherwise, return non-zero.
*/
int ImageAdjust(IplImage* src, IplImage* dst, 
    double low, double high,   // X方向:low and high are the intensities of src
    double bottom, double top, // Y方向:mapped to bottom and top of dst
    double gamma )
{
if( low<0 && low>1 && high <0 && high>1&&
bottom<0 && bottom>1 && top<0 && top>1 && low>high)
        return -1;
    double low2 = low*255;
    double high2 = high*255;
    double bottom2 = bottom*255;
    double top2 = top*255;
    double err_in = high2 - low2;
    double err_out = top2 - bottom2;
    int x,y;
    double val;
    // intensity transform
    for( y = 0; y < src->height; y++)
    {
        for (x = 0; x < src->width; x++)
        {
            val = ((uchar*)(src->imageData + src->widthStep*y))[x]; 
            val = pow((val - low2)/err_in, gamma) * err_out + bottom2;
            if(val>255) val=255; if(val<0) val=0; // Make sure src is in the range [low,high]
            ((uchar*)(dst->imageData + dst->widthStep*y))[x] = (uchar) val;
        }
    }
    return 0;
}
int main( int argc, char** argv ) 
{
    IplImage *src = 0, *dst = 0;
    
  src=cvLoadImage("f:\\img\\c2.jpg", 0); // force to gray image
  if(src==0)return 0;
        
    
    cvNamedWindow( "src", 1 );
    cvNamedWindow( "result", 1 );
    
    // Image adjust
    dst = cvCloneImage(src);
    // 输入参数 [0,0.5] 和 [0.5,1], gamma=1
if( ImageAdjust( src, dst, 0, 0.5, 0.5, 1, 1)!=0) return -1;
    
    cvShowImage( "src", src );
    cvShowImage( "result", dst );
    cvWaitKey(0);
    cvDestroyWindow("src");
    cvDestroyWindow("result");
    cvReleaseImage( &src );
    cvReleaseImage( &dst );
    
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值