Opencv图像识别从零到精通(8)-----灰度直方图

其实刚开始的时候,看很多的书和教程讲绘图和彩色图像等,但是我觉得还是先学会灰度直方图,因为灰度的dims是1,如果dims是3的就是彩色,同时知道前面将的彩色图像的像素访问,相信很快就可以迁移过去的。

 一、换个角度认识图像(直方图)

第一个就是当我们面对图像的时候,我们面对的是抽象的矩阵,如下图,下面是0-255的灰度图像的表示,密密麻麻的

那么我们做的直方图,其实就是对这些像素值的统计,看下图,其中Bin是条数,数据和范围是对图的解释,一看就懂

二、准备知识

如果想绘制出来直方图,先要知道几个函数

(1) Point类数据结构表示了二维坐标系下的点

Point point=Point(1,2);

(2)calcHist()绘制直方图

void calcHist(const Mat* arrays, intnarrays, const int* channels, InputArray mask, OutputArray
hist, int dims,const int* histSize, const float** ranges, bool uniform=true, boolaccumulate=false );

参数解释:

  • arrays:输入的图像的指针,可以是多幅图像,所有的图像必须有同样的深度(CV_8U orCV_32F)。同时一副图像可以有多个channes。
  • narrays:输入的图像的个数。
  • channels:用来计算直方图的channes的数组。比如输入是2副图像,第一副图像有0,1,2共三个channel,第二幅图像只有0一个channel,那么输入就一共有4个channes,如果int channels[3] = {3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计算直方图。
  • mask:掩码。如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和arrays[i]的大小相同,值为1的点将用来计算直方图。
  • hist:计算出来的直方图
  • dims:计算出来的直方图的维数。
  • histSize:在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个数。
  • ranges:用来进行统计的范围。比如 float rang1[] = {0, 20};float rang2[] = {30, 40};  const float*rangs[] = {rang1, rang2};那么就是对0,20和30,40范围的值进行统计。
  • uniform:每一个竖条的宽度是否相等。
  • accumulate:  是否累加。如果为true,在下次计算的时候不会首先清空hist。

画直线,在图像img中画一条颜色为color,粗细为thickness,类型为lineType的直线

(3)line() rectangle()画出直方图

void line(Mat& img, Point pt1, Pointpt2, const Scalar& color, int thickness=1,
         int lineType=8, int shift=0)
//两点确认一条直线。
//lineType:直线类型
//shift:坐标小数点维数

//画一个单一的实矩形
void rectangle(Mat& img, Point pt1,Point pt2, const Scalar& color, int thickness=1,
                 int lineType=8, int shift=0)
//一条对角线的两个顶点可确定一个矩形
//pt1和pt2互为对顶点
//thickness为负值表示矩形为实矩形

三、绘制一维灰度直方图

<span style="font-size:18px;">#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;

void Help()
{
	printf("\n\n\t\t\t欢迎来到直方图的世界!\n");
	printf("\n\n  ----------------------------------------------------------------------------\n");
}

int main()
{
	Mat srcImage = imread("lena.jpg", 0);
	imshow("原图",srcImage);
	if(!srcImage.data) {cout << "fail to load image" << endl; 	return 0;}
	system("color 1F");
	Help();

	MatND dstHist;       // 在cv中用CvHistogram *hist = cvCreateHist
	int dims = 1;
	float hranges[2] = {0, 255};
	const float *ranges[1] = {hranges};   // 这里需要为const类型
	int size = 256;
	int channels = 0;
	//计算图像的直方图
	calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges);    // cv 中是cvCalcHist
	int scale = 1;
	Mat dstImage(size * scale, size, CV_8U, Scalar(0));
	//获取最大值和最小值
	double minValue = 0;
	double maxValue = 0;
	minMaxLoc(dstHist,&minValue, &maxValue, 0, 0);  //  在cv中用的是cvGetMinMaxHistValue
	//绘制出直方图
	int hpt = saturate_cast<int>(0.9 * size);
	for(int i = 0; i < 256; i++)
	{
		float binValue = dstHist.at<float>(i);           //   注意hist中是float类型
		int realValue = saturate_cast<int>(binValue * hpt/maxValue);
		//rectangle(dstImage,Point(i*scale, size - 1), Point((i+1)*scale - 1, size - realValue), Scalar(255));
		line(dstImage,Point(i*scale,size-1),Point((i+1)*scale-1,size-realValue),Scalar(255));
	}
	imshow("一维直方图", dstImage);
	waitKey(0);
	return 0;
}</span>

其实我们有时候想改变量化,上面的表示是256,我们可以50 100都可以,这里用过滑块的知识,所以不想多讲,只是提供一个别人写的参考,尊重原作者,写的很好

<span style="font-size:18px;">#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <ctype.h>
using namespace std;
using namespace cv;

IplImage *src = 0;
IplImage *histimg = 0;
CvHistogram *hist = 0;

int hdims = 50;     // 划分HIST的初始个数,越高越精确

//滚动条函数
void HIST(int t)
{
     float hranges_arr[] = {0,255};
     float* hranges = hranges_arr;
     int bin_w;
     int bin_u;
     float max;
     int i;
     char string[10];
     CvFont font;
     cvInitFont( &font, CV_FONT_HERSHEY_PLAIN,1, 1, 0, 1, 8);//字体结构初始化
	if(hdims==0)
	{
		printf("直方图条数不能为零!\n");
	}
	else
	{
	   hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );  // 创建直方图
       histimg = cvCreateImage(cvSize(800,512),8,3);
       cvZero( histimg );
       cvCalcHist( &src, hist, 0, 0 ); // 计算直方图
	   cvGetMinMaxHistValue(hist,NULL,&max,NULL,NULL);//寻找最大值及其位置
      	 //printf("max_val:%f \n",max_val);
         cvZero( histimg );

       double bin_w =(double) histimg->width / hdims;  // hdims: 条的个数,则 bin_w 为条的宽度
       double bin_u = (double)histimg->height/ max;  //// max: 最高条的像素个数,则 bin_u 为单个像素的高度

       // 画直方图
	   for(int i=0;i<hdims;i++)
      {
          CvPoint p0=cvPoint(i*bin_w,histimg->height);
          int val=cvGetReal1D(hist->bins,i);
          CvPoint p1=cvPoint((i+1)*bin_w,histimg->height-cvGetReal1D(hist->bins,i)*bin_u);
          cvRectangle(histimg,p0,p1,cvScalar(0,255),1,8,0);
       }
	   //画纵坐标刻度(像素个数)
	   int kedu=0;
	   for(int i=1;kedu<max;i++)
	   {
		   kedu=i*max/10;
		   itoa(kedu,string,10);//把一个整数转换为字符串
		   //在图像中显示文本字符串
           cvPutText(histimg, string , cvPoint(0,histimg->height-kedu*bin_u), &font, CV_RGB(0,255,255));
	   }
	   //画横坐标刻度(像素灰度值)
	   kedu=0;
	   for(int i=1;kedu<256;i++)
	  {
		   kedu=i*20;
		   itoa(kedu,string,10);//把一个整数转换为字符串
		   //在图像中显示文本字符串
           cvPutText(histimg, string , cvPoint(kedu*(histimg->width / 256),histimg->height), &font, CV_RGB(255,0,0));
	  }

    cvShowImage( "Histogram", histimg );
	}
}

int main( int argc, char** argv )
{
	argc=2;
	argv[1]="lena.jpg";

    if( argc != 2 || (src=cvLoadImage(argv[1], 0)) == NULL)  // force to gray image
        return -1;

    cvNamedWindow( "src", 1);
	cvShowImage( "src", src);
	cvNamedWindow( "Histogram", 1 );

    cvCreateTrackbar( "hdims", "src", &hdims, 256, HIST );
    HIST(0);
    cvWaitKey(0);

    cvDestroyWindow("src");
    cvDestroyWindow("Histogram");
    cvReleaseImage( &src );
    cvReleaseImage( &histimg );
    cvReleaseHist ( &hist );

    return 0;
}</span>

三、彩色直方图

这里不想过多的介绍,以后讲彩色图像的时候会具体的说

四、matlab辅助

一个imhist()函数就搞定了

<span style="font-size:18px;">clear;
%%读入图像
a=imread('cameraman.tif');
imhist(a);
title('原始cameraman图像的直方图');</span>

时间: 2024-11-02 13:21:12

Opencv图像识别从零到精通(8)-----灰度直方图的相关文章

Opencv图像识别从零到精通(27)---grabcut

这是基于图论的分割方法,所以开始就先介绍了 Graph cuts,然后再到Grab cut   一. Graph cuts Graph cuts是一种十分有用和流行的能量优化算法,在计算机视觉领域普遍应用于前背景分割(Image segmentation).立体视觉(stereo vision).抠图(Image matting)等. 此类方法把图像分割问题与图的最小割(min cut)问题相关联.首先用一个无向图G=<V,E>表示要分割的图像,V和E分别是顶点(vertex)和边(edge)

Opencv图像识别从零到精通(10)-----直方图均衡化与直方图拉伸

 一.直方图均衡化 直方图均衡化是灰度变换的一个重要应用,广泛应用在图像增强处理中,它是以累计分布函数变换为基础的直方图修正法,可以产生一幅灰度级分布具有均匀概率密度的图像,扩展了像素的取值动态范围.许多图像的灰度值是非均匀分布的,其中灰度值集中在一个小区间内的图像是很常见的,直方图均衡化是一种通过重新均匀地分布各灰度值来增强图像对比度的方法,经过直方图均衡化的图像对二值化阈值选取十分有利.一般来说,直方图修正能提高图像的主观质量,因此在处理艺术图像时非常有用.直方图均衡化处理的中心思想是把原始

Opencv图像识别从零到精通(26)---分水岭

分水岭是区域分割三个方法的最后一个,对于前景背景的分割有不错的效果. 分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭.分水岭的概念和形成可以通过模拟浸入过程来说明.在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭. 分水岭

Opencv图像识别从零到精通(30)---重映射,仿射变换

一.序言 面对图像处理的时候,我们会旋转缩放图像,例如前面所提高的resize 插值改变,也是几何变换: 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标.这时就需要灰度级差值将映射的新坐标匹配到输出像素之间.最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿.这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值. 除了插值算法感觉只要了解就可以了,图像处理中比较需要理

Opencv图像识别从零到精通(29)-----图像金字塔,向上上下采样,resize插值

金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似.我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低 一.两个金字塔 高斯金字塔(Gaussianpyramid): 用来向下采样,主要的图像金字塔 拉普拉斯金字塔(Laplacianpyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用. 高斯金字塔不同(DoG)又称为拉普拉斯金字塔,给出计算方式前,先加强一下定义 记得在上面我

Opencv图像识别从零到精通(33)----moravec角点、harris角点

一.角点 图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints).特征点(feature points) 被大量用于解决物体识别,图像识别.图像匹配.视觉跟踪.三维重建等一系列的问题.我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析.如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值. 图像特征类型可以被分为如下三种: <1>边缘                   

Opencv图像识别从零到精通(24)------漫水填充,种子填充,区域生长、孔洞填充

可以说从这篇文章开始,就结束了图像识别的入门基础,来到了第二阶段的学习.在平时处理二值图像的时候,除了要进行形态学的一些操作,还有有上一节讲到的轮廓连通区域的面积周长标记等,还有一个最常见的就是孔洞的填充,opencv这里成为漫水填充,其实也可以叫种子填充,或者区域生长,基本的原理是一样的,但是应用的时候需要注意一下,种子填充用递归的办法,回溯算法,漫水填充使用堆栈,提高效率,同时还提供了一种方式是扫描行.经常用来填充孔洞,现在来具体看看. 漫水填充:也就是用一定颜色填充联通区域,通过设置可连通

Opencv图像识别从零到精通(31)----图像修补,分离合并通道

一.图像修复简介 图像修复是图像复原中的一个重要内容,其目的是利用图像现有的信息来恢复丢失的信息.可用于旧照片中丢失信息的恢复,视频文字去除以及视频错误隐藏等.简言之,图像修复就是对图像上信息缺损区域进行信息填充的过程,其目的就是为了对有信息缺损的图像进行复原,并且使得观察者无法察觉到图像曾经缺损或者已经修复      图像修复技术简单来说,就是利用那些被破坏区域的边缘,即是边缘的颜色和结构,繁殖和混合到损坏的图像中,来进行修复图像 目前存在两大类图像修复技术:一类是用于修复小尺度缺损的数字图像

Opencv图像识别从零到精通(32)----直方图对比,模版匹配,方向投影

0.预备知识 归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内. 函数原型: <span style="font-size:18px;">void normalize(InputArray src,OutputArray dst, double alpha=1,doublebeta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() ) </span>