1 直方图的计算与显示&开操作&漫水填充算法

1 直方图的计算与显示

根据输入的图像计算出一个色相饱和度(hue-saturation)直方图,然后利用网格的方式将该直方图以网格形式显示出来,具体代码如下:
1. 函数介绍

void cvFloodFill(
IplImage *        img,//原图像
CvPoint           seedpoint,//种子点
CvScalar          newVal,//像素被染色的值
CvScalar          loDiff=cvScalarAll(0),//某个像素点不低于相邻像素点减去loDiff
CvScalar          upDiff=cvScalarAll(0),//且不高于相邻像素点加上upDiff,那么该像素点会被染色
CvConnectedComp * comp  =NULL,//如果不为NULL,被设置为被填充区域的统计属性
int               flags =4,//如果为CV_FLOODFILL_FIXED_RANGE,则和种子点比较,而不是和相邻点比较
CvArr*           mask=NULL//控制可以被填充的区域
);

2 具体代码


#include <cv.h>
#include <highgui.h>  
#include <stdlib.h>  
#include <stdio.h> 
#include <math.h>

using namespace std;

CvPoint Current_Point;              //值为255点当前点 全局变量才可通过普通成员引用变更其值

bool find_point(IplImage *img, char val);

int main(int argc, char* argv[]) {
    int threshold_type = CV_THRESH_BINARY;      //阈值类型
    int Last_Area = 0;                          //上一个区域面积       
    int Current_Area = 0;                       //当前区域面积
    double threshold = 65;                      //阈值

    CvPoint Last_Point;                         //值为255点的上一点
    CvConnectedComp comp;                       //被填充区域统计属性
    IplImage *src1, *hsv, *Igray, *Ithreshold, *Itemp, *Iopen, *Imask;      //源图像 HSV格式图像

    Last_Point = cvPoint(0, 0);         //初始化上一点
    Current_Point = cvPoint(0, 0);      //初始化当前点

    if (!(src1 = cvLoadImage("D:\\Template\\OpenCV\\Template46_hue-saturation_Hist\\Debug\\handdd.jpg")))
        return -1;

    //此处调入图像掩码应为单通道
    //if (!(Imask = cvLoadImage("D:\\Template\\OpenCV\\Template46_hue-saturation_Hist\\Debug\\cup2.jpg", CV_LOAD_IMAGE_GRAYSCALE)))
    //  return -2;

    hsv=cvCreateImage(cvGetSize(src1), src1->depth, src1->nChannels);
    Igray = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Ithreshold = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Itemp = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Iopen = cvCreateImage(cvGetSize(src1), src1->depth, 1);
    Imask = cvCreateImage(cvGetSize(src1), src1->depth, 1); //生成手掌掩码图像用

    cvCvtColor(src1, hsv, CV_BGR2HSV);      //源图像->HSV格式图像
    cvCvtColor(src1, Igray, CV_BGR2GRAY);   //源图像->灰度图像

    cvThreshold(Igray, Ithreshold, threshold, 255, threshold_type); //二值阈值化
    //开运算,去除小亮区域,其他联结 NULL:3*3参考点为中心的核
    cvMorphologyEx(Ithreshold, Iopen, Itemp, NULL, CV_MOP_OPEN, 1); 

    cvNamedWindow("src1", 1);
    cvNamedWindow("GRAY_Image", 1);
    cvNamedWindow("THRESHHOLD_Image", 1);
    cvNamedWindow("OPEN_Image", 1);
    cvNamedWindow("FLOOD_FILL", 1);

    cvShowImage("src1", src1);
    cvShowImage("GRAY_Image", Igray);
    cvShowImage("THRESHHOLD_Image", Ithreshold);
    cvShowImage("OPEN_Image", Iopen);
    cvShowImage("FLOOD_FILL", Imask);

    //漫水填充 获得手掌掩码
    cvNamedWindow("FLOOD_FILL", 1);
    cvCopy(Iopen, Imask);           //复制生成手掌掩码

    do
    {
        if (find_point(Imask, 255))    //找像素值为255的像素点
        {

            cout << " X: " << Current_Point.x << " Y: " << Current_Point.y << endl;

            cvFloodFill(Imask, Current_Point, cvScalar(100), cvScalar(0), cvScalar(0),
                &comp, 8 | CV_FLOODFILL_FIXED_RANGE);       //对值为255的点进行漫水填充,值100
            Current_Area = comp.area;                       //当前区域面积

            if (Last_Area<Current_Area)                     //当前区域大于上一区域,上一区域清0
            {
                if (Last_Area>0)
                    cvFloodFill(Imask, Last_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //上一区域赋值0
                cvShowImage("FLOOD_FILL", Imask);
                cvWaitKey(500);

                Last_Area = Current_Area;                               //当前区域赋值给上一区域
                Last_Point = Current_Point;                             //当前点赋值给上一点
                //memcpy(&Last_Point, &Current_Point, sizeof(CvPoint)); //错误,此方法复制无法正常使用掩码
            }
            else                                            //当前区域小于等于上一区域,当前区域清0
            {
                if (Current_Area>0)
                    cvFloodFill(Imask, Current_Point, cvScalar(0), cvScalar(0), cvScalar(0),
                    &comp, 8 | CV_FLOODFILL_FIXED_RANGE);   //当前区域赋值0
                cvShowImage("FLOOD_FILL", Imask);
                cvWaitKey(500);
            }
        }
        else                                                //最后剩余的最大区域赋值255
        {
            cvFloodFill(Imask, Last_Point, cvScalar(255), cvScalar(0), cvScalar(0), &comp, 8 | CV_FLOODFILL_FIXED_RANGE);
            cvShowImage("FLOOD_FILL", Imask);
            cvWaitKey(500);
            //上一区域赋值0
            break;
        }
    } while (true);

    cvSaveImage("Imask.jpg", Imask);

    //色调(hue) 饱和度(saturation) 明度(value)
    IplImage *h_plane = cvCreateImage(cvSize(hsv->width, hsv->height), IPL_DEPTH_8U, 1);
    IplImage *s_plane = cvCreateImage(cvSize(hsv->width, hsv->height), IPL_DEPTH_8U, 1);
    IplImage *v_plane = cvCreateImage(cvSize(hsv->width, hsv->height), IPL_DEPTH_8U, 1);

    IplImage *planes[] = {h_plane,s_plane};                     //色相饱和度数组

    cvCvtPixToPlane(hsv, h_plane, s_plane, v_plane, NULL);      //图像分割
    //cvSplit(hsv, h_plane, s_plane, v_plane, NULL);

    int h_bins = 30, s_bins = 32;                               

    //建立直方图
    CvHistogram *hist;

    int hist_size[] = { h_bins, s_bins };       //对应维数包含bins个数的数组
    float h_ranges[] = { 0, 180 };              //H通道划分范围 饱和度0-180
    float s_ranges[] = { 0, 255 };              //S通道划分范围
    float* ranges[] = { h_ranges, s_ranges };   //划分范围数对, ****均匀bin,range只要最大最小边界
    //创建直方图 (维数,对应维数bins个数,密集矩阵方式存储,划分范围数对,均匀直方图)
    hist = cvCreateHist(2, hist_size, CV_HIST_ARRAY, ranges, 1);

    cvCalcHist(planes, hist, 0, Imask); //计算直方图(图像,直方图结构,不累加,掩码)
    cvNormalizeHist(hist, 1.0);         //直方图归一化

    //绘制可视化直方图
    int scale = 10;
    IplImage* hist_img = cvCreateImage(cvSize(h_bins*scale, s_bins*scale), 8, 3);   //300*320
    cvZero(hist_img);

    //以小灰度块填充图像
    float max_value = 0;
    cvGetMinMaxHistValue(hist, NULL, &max_value, NULL, NULL);       //获取直方图最大值

    for (int h = 0; h < h_bins; h++)
    {
        for (int s = 0; s < s_bins; s++)
        {
            float bin_val = cvQueryHistValue_2D(hist, h, s);    //获取直方图相应bin中的浮点数
            int intensity = cvRound(bin_val * 255 / max_value); //映射到255空间,归一后太小,难辨
            cvRectangle(hist_img, cvPoint(h*scale, s*scale),        
                cvPoint((h + 1)*scale - 1, (s + 1)*scale - 1), 
                CV_RGB(intensity, intensity, intensity), CV_FILLED);
        }
    }

    cvNamedWindow("HIST_Image", 1);
    cvShowImage("HIST_Image", hist_img);

    cvWaitKey();

    cvReleaseHist(&hist);

    cvReleaseImage(&src1);
    cvReleaseImage(&hsv);
    cvReleaseImage(&Igray);
    cvReleaseImage(&Ithreshold);
    cvReleaseImage(&Itemp);
    cvReleaseImage(&Iopen);
    cvReleaseImage(&Imask);
    cvReleaseImage(&h_plane);
    cvReleaseImage(&s_plane);
    cvReleaseImage(&v_plane);
    cvReleaseImage(&hist_img);

    cvDestroyWindow("src1");
    cvDestroyWindow("HIST_Image");
    cvDestroyWindow("GRAY_Image");
    cvDestroyWindow("THRESHHOLD_Image");
    cvDestroyWindow("OPEN_Image");
    cvDestroyWindow("FLOOD_FILL");
    cvDestroyWindow("HIST_Image");

}

/******************遍历图像,指针算法********************/ bool find_point(IplImage *img, char val) {
    char* ptr = NULL;

    if (img->nChannels == 1)
    {
        ptr = img->imageData;
        if (ptr != NULL)
        {
            for (int i = 0; i < img->height; i++)       //矩阵指针行寻址
            {
                ptr = (img->imageData + i*(img->widthStep));   //i 行 j 列
                for (int j = 0; j < img->width; j++)    //矩阵指针列寻址
                {
                    if (ptr[j] == val)              //判断某点像素是否为255
                    {
                        Current_Point.x = j;        /********局部变量此方式 无法实现赋值********/
                        Current_Point.y = i;
                        return true;
                    }
                }
            }
        }
    }
    return false; }

结果图片

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值