OpenCV2 && Qt4 chapter4: couting the pixels with histogram

这一章有点难度。。涉及到好多新知识哟,没选修数字图像处理真是一个错误。。泪奔。。好好总结一下:

一:直方图。(好懂,把每种像素值的个数表示出来。)
 1.直方图计算: cv::calcHist,参见使用说明最好罗。
使用有点难,下面是可以作为参考。 二维指针学得不好呀,自己实现时老是编译出错,哭~~

单通道的例子,下面实现中, 最大值 hRange[1]=255.0的话,像素值为255老是统计不出来,看了帮助文档,百度都无果, 把它改成255.1就可以统计了。不明白呀不明白。求解答呀求解答。

单通道:
int channels[1];
    int histSize[1];
    float hRange[2];
    const float *prange[1];
 channels[0]=0;  // 使用第0th通道。
    histSize[0] = 256;
    hRange[0]=0.0;
    hRange[1]=255.1; //这里如果最大值是255.0  那么255白色点计算不出来。。  加大一点点就可以了。
    prange[0]=hRange;

3通道:
int histSize[3];
float hranges[2];
const float* ranges[3];
int channels[3];
histSize[0]= histSize[1]= histSize[2]= 256;
hranges[0]= 0.0;
// BRG range
hranges[1]= 255.0;
ranges[0]= hranges; // all channels have the same range
ranges[1]= hranges;
ranges[2]= hranges;
channels[0]= 0;
// the three channels
channels[1]= 1;
channels[2]= 2;
}

cv::MatND histo=h.getHistogram(mat2);
cv::MatND 其实就是 cv::Mat 两者是一样的。 可以表示多维图形。一维是线,二维是平面图形,三维是立体,四维。。 个人理解,这里的 histo就是一个一维的撒。
 for(int i=0;i<256;++i)
    {
        cout<<"value "<<i<<"= "<< histo.at<float>(i)<<endl;
    }  //输出来大概就是这样子输出。
2.根据上面的 histo,要画出条形图。
步骤:创建一个普通的图,长高都是固定的。  根据每个点的值,计算出相对值,然后画线。 这个好理解。
cv::MatND hist=getHistogram(image);
    // 条形图Y轴上最高最低点。
    double maxValue,minValue;
    cv::minMaxLoc(hist,&minValue,&maxValue);

    cv::Mat histImg(histSize[0],histSize[0],CV_8U,cv::Scalar(255));

    int hp = 0.9 * histSize[0]; //最高点的高度。

    //画线。
    for(int i=0;i<histSize[0];++i)
    {
        int tmp = hist.at<float>(i) * 1.0 /maxValue * hp; //当前点的实际高度。
        cv::line(histImg,cv::Point(i,histSize[0]),cv::Point(i,histSize[0]-tmp),cv::Scalar(0));
    }
    cv::namedWindow("win_histimage");
    cv::imshow("win_histimage",histImg);
3. 阈值二值化处理threshold。

(错误!!!)threshold函数,在单通道图形中,能设定一个值临界值,同一个灰度的像素点个数大于和小于这个临界值分别做不同的处理。处理根据type值。      type值参考帮助文档。 只能处理单通道图像哟!!!!! 典型应用,是处理传输过程产生的噪声。(注:此理解有误,下面更正。)

更正: threshold函数。 设置一个像素临界值,假设为50, 对于每个像素点,像素值低于和高于临界值,分别做不同的处理。典型应用:噪声的像素值要么很小,要么很大,可以用之来处理。

注: 此函数处理的是像素值,而不是像素值的个数。我想多了。。。  此文章以下的内容,没有再去理解,先记录在此。

cv::Mat mat_threshold;
cv::threshold(mat2,mat_threshold,100,255,cv::THRESH_BINARY); //每个像素值,小于100的置成0, 大于100的置成255.

4. 查找表映射。

cv::LUT 函数。 look-up-transform。 这个挺好懂的,原图所有像素点均进行一个映射。

原像素值以某种规则映射成另外一种像素值。 y=lookup[x];  lookup是Mat格式查找表,x是原像素,y是映射后的像素。

 int mysize=256;
    cv::Mat lookup(1,&mysize,CV_8U);   //一维,总共256个,单通道型
    for(int i=0;i<mysize;++i)
    {
        lookup.at<uchar>(i)=255-i;
    }
cv::Mat Histogram1D::applyLookUp(cv::Mat &image, cv::Mat &lookup)
{
    cv::Mat result;
    cv::LUT(image,lookup,result);
    return result;
}
5. 直方图均衡化。  重点。

cv::equalizeHist(image,result);  作用:增强对比度,使图像易于识别。


原理理解: (自己的感觉)

1>一幅单通道的灰色图,有50个点像素值为100,50个点像素值为101, 其它0~99,102~255的像素值数量均为1.

2>上面的图像应该是一片灰,能看到的基本都是像素值为100~101的灰色点。 很难辩认。

3> 均衡化先设定阈值10.  每种像素点数量小于10像素值置为0, 数量大于10保持不变。

4>此时直方图中,只有两个点有值。 分别是 hist[100]=50  hist[101]=50; 其它都为0.

5> 均衡化大概是扩大这些间隔,变成 hist[75]=50, hist[175]=50.  75像素值略黑,175略白,这样两者的对比度就变大,图像就变得好辩认了。

6> 除了扩大像素间隔, 还会减少各像素bin值的差。  (就是使偏黑点和偏白点尽量保持相同。。)

更多参考 http://www.cnblogs.com/xianglan/archive/2011/08/06/2129410.html 讲得非常清晰。 


二.计算反向投影图。(作用: 计算每个像素点匹配小图的概率。是大图中找出一个区域匹配小图   的前提步骤。)

1. 反向投影图概念。 看了几位前辈博客,有些写得很好,但是有些却是比较笼统,甚至概念都出现偏差,好艰难的理解。。在此总结下我自己理解的概念。

例子:有一张大图 image,是一个人脸。一张小图imageROI,截取了人脸上的嘴巴。反向投影图result。

1>计算反向投影图的输入输出。输入是大图、小图的直方图。输出是image的反向投影图 = result。(大概的意思:人脸这张大图,哪个区域与嘴巴这小图最相接近,这个区域由人脸的反向投影图再进一步去计算)

2> 反向投影图是怎么计算的。

先参考:http://blog.csdn.net/sevencolorfish/article/details/6840719  非常的详细。

我理解的大概步骤: 针对每个像素点 -> 以之为左上角,截取与小图一样大的图,作为临时图 -> 临时图计算直方图,与小图的直方图比较 -> 得出一个对比值 ->将这个对比值填在当前左上角的像素点上。

注意:其一,是直方图与直方图比较,而不是像素点与直方图比较。 其二:对比值不一定是越大越相似,对比值意义根据直方图比较算法不同而不同的。

3> 反向投影图的结果表示了什么。

每个像素点上的对比值,代表了大图中这个位置的点,属于小图的概率。 (对比值越大,概率可能越大,也可能越小。 比较算法决定的)

以上概念好多是猜测的。下面列举一下书本中的内容。



以下是效果图:

1.单通道的反向投影图。

书中结论:The result is the following probability map, with probabilities from bright (low probability) to dark (high probability) of belonging to the reference area
我的结论:书本的反向投影图跟我的刚好黑白对调了,我的结论与之相反,不过无妨,采取的直方图比较算法不同而已。 下图中,右边的概率图,黑点表示大图中此点属于小图的概率低,白点的概率高。

书中的一句话:The preceding result can be disappointing because, in addition to the clouds, other areas have been wrongly detected as well. It is important to understand that the probability function has been extracted from a simple gray-level histogram. Many other pixels in the image share the same intensities as the cloud pixels, and pixels of same intensity are replaced by the same probability value when backprojecting the histogram. One solution to improve the detection result would be to use color information. But in order to do this, we need to modify the call to cv::calBackProject.

再把反向投影图进行阈值二值化处理,过滤掉一些bin值低的点。(不明白这一步的用意,不理解,记录着先。)

得出反向投影图后,怎么查找小图位置: 用mean-shift算法去算。 不能直接从图中看出来。

(小图的矩阵是:360,55,40,50  我用这算法求一下,结果是 360,47,40,50  有无二值化都是一样的。).

代码如下:

    Histogram1D hh;
    cv::MatND myhist = hh.getHistogram(matROI);
    cv::normalize(myhist,myhist,1.0); // 归一化

    int mychan[1]={0};
    float hRange[2]={0.0,255.0};
    const float *pmyrange[1]={hRange};
    cv::Mat result;
    cv::calcBackProject(&mat_jingju_gray,1,mychan,myhist,result,pmyrange,255.0);

    cv::namedWindow("win_backproject");  //概率图,值越大,越白,表示概率越高。
    cv::imshow("win_backproject",result);

    cv::Rect rect(cv::Rect(360,55,40,50));
    cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER, 10, 0.01);
    int ncount = cv::meanShift(result,rect,criteria);
    //cv::rectangle( result,rect,cv::Scalar(255));
    cout<< rect.x <<" "<< rect.y << " "<<rect.width<<" "<<rect.height<<endl;
    cv::Mat thre;
    cv::threshold(result,thre,25,255,cv::THRESH_BINARY); //二值化。零星的置成黑,多的置成白,不明白用意
    cv::namedWindow("win_threshold");  
    cv::imshow("win_threshold",thre);

计算反向投影图前使用了归一化,百度了一下。

图像归一化使得图像可以抵抗几何变换的攻击,它能够找出图像中的那些不变量,从而得知这些图像原本就是一样的或者一个系列的。

三通道的反向投影图:

1.左边是大图和小图。 右边的是反向投影图。与上面的相比,可以看出它白点的亮度变高,说明概率变大。就是直方图的比较精准了一点。(BGR三通道图像)

2> 反向投影图进行阈值二值化处理,如下图所示。(仍然不明白其用意,放着,保留。。)

得出反向投影图后,怎么查找小图位置: 用mean-shift算法去算。 不能直接从图中看出来。

(小图的矩阵是:360,55,40,50  我用这算法求一下,结果是 347,55,40,50        没有二值化).

(小图的矩阵是:360,55,40,50  我用这算法求一下,结果是 358,48,40,50         有二值化).

感觉嘛,BGR三通道还是有些偏差。。

代码如下:

ColorHistogram hc;
    cv::Mat mat_jingju_color = cv::imread("/root/Desktop/photos/waves.jpg");
    if( !mat_jingju_color.data)
    {
        cout<<"failed in read jingju.jpeg"<<endl;
        return -1;
    }

    cv::Mat tmp=mat_jingju_color.clone();
    cv::rectangle( tmp,cv::Rect(360,55,40,50),cv::Scalar(0,0,255));
    cv::namedWindow("win_big_small");
    cv::imshow("win_big_small",tmp);

    mat_jingju_color = hc.ColorReduce(mat_jingju_color,32);
    //cv::namedWindow("win_jingju_color");
  //  cv::imshow("win_jingju_color",mat_jingju_color);

    //感兴趣的子图。
    cv::Mat matROI_color;
    //matROI_color = mat_jingju_color(  cv::Rect(290,150,40,50));
    matROI_color = mat_jingju_color(  cv::Rect(360,55,40,50));
  //  cv::namedWindow("win_ROI_color");
//    cv::imshow("win_ROI_color",matROI_color);


    cv::MatND hist_color = hc.getHistogram(matROI_color);
    cv::normalize(hist_color,hist_color,1.0); // 归一
    ObjectFinder obfind(hist_color);
    cv::Mat backproject;
    backproject = obfind.find(mat_jingju_color);
    /*
    //二值化。数量少变黑。数量多变白。
    cv::threshold(backproject, backproject, 25, 255, cv::THRESH_BINARY);
    cv::namedWindow("win_project_color_thre");
    cv::imshow("win_project_color_thre",backproject);
*/
    cv::Rect rect(cv::Rect(360,55,40,50));
    cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER, 10, 0.01);
    int ncount = cv::meanShift(backproject,rect,criteria);

    cv::rectangle( backproject,rect,cv::Scalar(255));

    cout<< rect.x <<" "<< rect.y << " "<<rect.width<<" "<<rect.height<<endl;


三. meanshift算法。

例子大概的步骤:

1.大图小图都转成HSV。 将大图分解成三幅图,分别是H色调,S饱和度,V亮度。

2. 计算小图的 色调通道 的直方图。

注意,当饱和度太低时,色调会变得不稳定和不真实。所以,饱和度低时,色调就置成0.

处理方法:

1>计算直方图时之前,先对 色调图像遍历一遍, 如果饱和度小,就将色调置成0。   二值化饱和度,与操作(其实1跟2的操作是一样的哟。。)

2> 计算hue的反向投影图之后, cv::bitwise_and(result,v[1],result);  每个像素都与一下饱和度。(饱和度太小置成0)

3.调用meanshift算法。

meanshift的原理:http://blog.csdn.net/crzy_sparrow/article/details/7352994    此前辈博客有记录,但是我还是不明白。先保存着吧。

meanshift的效果总是不理想, CamShift也好不到哪里去。 一定是我程序有些地方没处理好,找了两遍没找着。。代码不贴了,贴个效果图,以后再说吧。

meanShift:(绿框是结果图。  这结果偏差太大了。哎。)

CamShift:(绿框竟然框中了,但是大了点。。)


四:直方图比较算法。

cv::compareHist   五种算法。不想管。。


总结: 花了好多天在这一章,有点小烦。必须赶进度。。明天开始下一章。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值