图像处理做分量颜色直方图

2018-10-31 17:15:20 Jaster_wisdom 阅读数 10503

本篇博客主要介绍利用opencv工具提取一幅图像中的颜色直方图特征。所谓颜色直方图,指的是一幅图像中的颜色分布,与图像中的特定的物体无关,只是用来表示人的眼睛观察到的图像中的颜色分布情况,例如说,一幅图中红色占了多少比例,绿色占了多少比例等。

我们知道,计算机色彩显示器采用R、G、B相加混色的原理,通过发射出三种不同强度的电子束,使屏幕内侧覆盖的红、绿、蓝磷光材料发光而产生色彩。在RGB颜色空间中,任意色光F都可以用RGB三色不同分量的相加混合而成。也就是说,图像中每个像素的颜色值都可以用一个三元值(R,G,B)来表示,例如(0,0,0)表示黑色,(0,0,255)表示蓝色,……每个分量的大小从0~255.

那么,我们可以知道一张彩色图像可以由R、G、B三个通道堆叠而成。可以想象成将三张大小相同的纸叠放,然后人眼从上往下看到的图像就是原来的彩色图像。这里,假设图像的分辨率为720*576,那么每个通道的长为720,宽为576,单位是像素,总的像素个数就是3*720*576. 

下面具体介绍颜色直方图,横轴表示bins,也就是颜色分为多少块。假设bins=256,也就是横坐标上每个点表示一个数值,纵坐标表示一幅图中有多少个像素点的值是该数值。如果bins=16,也就是把颜色范围0~255从小到大分为16块,然后图像中落在每块对应的范围的像素点的个数就是每块对应的直方图的高度。

因为opencv默认一张图像是由R、G、B三个通道堆叠形成的,所以首先提取出每个通达的直方图,最后将三个直方图揉在一块。

其中,最主要的就是一个calcHist函数,void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )
参数较多。第一个参数表示输入的图像;第二个参数表示输入的图像个数,一般是 1;第三个参数表示需要处理的是图像的第几个通道;第四个参数表示掩模,一般是Mat();第五个参数是要输出的直方图数组;第六个参数表示需要统计的直方图通道个数,一般是1;第七个参数histSize表示划分的区间数,即bins的个数第八个参数表示像素的变化范围;后两个参数分别表示是否归一化,和是否累计计算像素个数。

重点关注我标红的参数即可。

代码展示:

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

int main()
{
	//读取本地的一张图片
	Mat srcimage = imread("F:\\6030.png");
	imshow("原图", srcimage);
	int channels = 0;
	int histsize[] = { 256 };
	float midranges[] = { 0,255 };
	const float *ranges[] = { midranges };
	MatND  dsthist;    //要输出的直方图
	//重点关注calcHist函数,即为计算直方图的函数
	calcHist(&srcimage, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false);  

	Mat b_drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
	double g_dhistmaxvalue;
	minMaxLoc(dsthist, 0, &g_dhistmaxvalue, 0, 0);
	for (int i = 0;i < 256;i++) {
		//这里的dsthist.at<float>(i)就是每个bins对应的纵轴的高度
		int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue)); 
		line(b_drawImage, Point(i, b_drawImage.rows - 1), Point(i, b_drawImage.rows - 1 - value), Scalar(255, 0, 0));
	}
	imshow("B通道直方图", b_drawImage);

	channels = 1;
	calcHist(&srcimage, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false);
	Mat g_drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
	for (int i = 0;i < 256;i++) {
		int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue));
		line(g_drawImage, Point(i, g_drawImage.rows - 1), Point(i, g_drawImage.rows - 1 - value), Scalar(0, 255, 0));
	}
	imshow("G通道直方图", g_drawImage);

	channels = 2;
	calcHist(&srcimage, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false);
	Mat r_drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
	for (int i = 0;i < 256;i++) {
		int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue));
		line(r_drawImage, Point(i, r_drawImage.rows - 1), Point(i, r_drawImage.rows - 1 - value), Scalar(0, 0, 255));
	}
	imshow("R通道直方图", r_drawImage);

	add(b_drawImage, g_drawImage, r_drawImage);   //将三个直方图叠在一块
	imshow("RGB直方图", r_drawImage);
	waitKey(0);
	return 0;
}

结果展示:

 

 

 

接下来,绘制HSV颜色空间的直方图。有了RGB颜色直方图,还要HSV干什么。下面介绍HSV颜色空间模型。

HSV是一种将RGB色彩空间中的点在倒圆锥体中的表示方法。HSV即色相(Hue)、饱和度(Saturation)、亮度(Value),色相即颜色的基本属性,饱和度指色彩的纯度,越高则色彩越纯,亮度指色彩的明亮程度。

在RGB颜色空间中,三种颜色分量的取值与所生成的颜色之间的联系并不直观。而HSV颜色空间,更类似于人类感觉颜色的方式,封装了关于颜色的信息:“这是什么颜色?深浅如何?明暗如何?”

HSV颜色空间规定的取值范围: H:0~360,S:0~1,V:0~1

在opencv中,H:0~180,S:0~255,V:0~255, H/2,S*255,V*255

也就是说,这里H通道的取值是和其他两个通道有所不同的,它的范围是0~180,因为如果为了堆叠三个通道,强行设置256个bins,那么在第180~255个bins上,H通道上会出现为空的情况。

HSV颜色空间特征的提取方法和RGB类似,关键一点就是要将原图像转化为HSV颜色空间的图像,之后再对三个通道分别进行直方图绘制操作即可。

代码展示:

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

int main()
{
	Mat srcimage = imread("F:\\6030.png");
	imshow("原图", srcimage);
	Mat srcimageHSV;
	//图像转化HSV颜色空间图像
	cvtColor(srcimage, srcimageHSV, COLOR_BGR2HSV);
	imshow("HSV空间图像", srcimageHSV);
	int channels = 0;
	int histsize[] = { 256 };
	float midranges[] = { 0,255 };
	const float *ranges[] = { midranges };
	MatND  dsthist;
	calcHist(&srcimageHSV, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false);
	Mat b_drawImage = Mat::zeros(Size(256, 256), CV_8UC3);

	double g_dhistmaxvalue;   
	minMaxLoc(dsthist, 0, &g_dhistmaxvalue, 0, 0);
	for (int i = 0;i < 256;i++) {
		int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue));
		line(b_drawImage, Point(i, b_drawImage.rows - 1), Point(i, b_drawImage.rows - 1 - value), Scalar(255, 0, 0));
	}
	imshow("H通道直方图", b_drawImage);
	
	channels = 1;
	calcHist(&srcimageHSV, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false);
	Mat g_drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
	for (int i = 0;i < 256;i++) {
		int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue));
		line(g_drawImage, Point(i, g_drawImage.rows - 1), Point(i, g_drawImage.rows - 1 - value), Scalar(0, 255, 0));
	}
	imshow("S通道直方图", g_drawImage);

	channels = 2;
	calcHist(&srcimageHSV, 1, &channels, Mat(), dsthist, 1, histsize, ranges, true, false);
	Mat r_drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
	for (int i = 0;i < 256;i++) {
		int value = cvRound(256 * 0.9 *(dsthist.at<float>(i) / g_dhistmaxvalue));
		line(r_drawImage, Point(i, r_drawImage.rows - 1), Point(i, r_drawImage.rows - 1 - value), Scalar(0, 0, 255));
	}
	imshow("V通道直方图", r_drawImage);
	add(b_drawImage, g_drawImage, r_drawImage);
	imshow("HSV直方图", r_drawImage);
	waitKey(0);
	return 0;
}

结果展示:

 

 

 

 

 

说明:HSV三个通道的直方图绘制,我为了区分,用了红绿蓝三种颜色,和RGB通道没有关系。颜色可以自己在程序中更改。其实每个通道都是从不同的角度(色相、饱和度、亮度)来分析图像的。

2018-09-14 13:32:59 gongyi_yf 阅读数 1384

计算颜色直方图时,横坐标是颜色空间,纵坐标是该颜色的像素点的数量。对于常用的RGB一共有2563=16777216种,这样横坐标过于庞杂,而且很多横坐标对应的像素点数量很少,整个直方图会很稀疏。
因此在实际中,计算颜色直方图需要将颜色空间划分为若干个小的颜色区间。对于每个颜色通道(R,G,B),每32个划分到一个bin里面,这样每个颜色通道有8bins,全部的RGB颜色空间就分成了83=512个bins。也就是直方图的横坐标共512个刻度。

2012-10-25 19:38:21 guoyk1990 阅读数 21451

       直方图匹配,又称直方图规定化,即变换原图的直方图为规定的某种形式的直方图,从而使两幅图像具有类似的色调和反差。直方图匹配属于非线性点运算。

       直方图规定化的原理:对两个直方图都做均衡化,变成相同的归一化的均匀直方图,以此均匀直方图为媒介,再对参考图像做均衡化的逆运算

       以下是算法实现(C#)

    /// <summary>
    /// 直方图匹配
    /// </summary>
    /// <param name="srcBmp">原始图像</param>
    /// <param name="matchingBmp">匹配图像</param>
    /// <param name="dstBmp">处理后图像</param>
    /// <returns>处理成功 true 失败 false</returns>
    public static bool HistogramMatching(Bitmap srcBmp, Bitmap matchingBmp, out Bitmap dstBmp) {
        if (srcBmp == null || matchingBmp == null) {
            dstBmp = null;
            return false;
        }
        dstBmp = new Bitmap(srcBmp);
        Bitmap tempSrcBmp = new Bitmap(srcBmp);
        Bitmap tempMatchingBmp = new Bitmap(matchingBmp);
        double[] srcCpR = null;
        double[] srcCpG = null;
        double[] srcCpB = null;
        double[] matchCpB = null;
        double[] matchCpG = null;
        double[] matchCpR = null;
        //分别计算两幅图像的累计概率分布
        getCumulativeProbabilityRGB(tempSrcBmp, out srcCpR, out srcCpG, out srcCpB);
        getCumulativeProbabilityRGB(tempMatchingBmp, out matchCpR, out matchCpG, out matchCpB);

        double diffAR = 0, diffBR = 0, diffAG = 0, diffBG = 0, diffAB = 0, diffBB = 0;
        byte kR = 0, kG = 0, kB = 0;
        //逆映射函数
        byte[] mapPixelR = new byte[256];
        byte[] mapPixelG = new byte[256];
        byte[] mapPixelB = new byte[256];
        //分别计算RGB三个分量的逆映射函数
        //R
        for (int i = 0; i < 256; i++) {
            diffBR = 1;
            for (int j = kR; j < 256; j++) {
                //找到两个累计分布函数中最相似的位置
                diffAR = Math.Abs(srcCpR[i] - matchCpR[j]);
                if (diffAR - diffBR < 1.0E-08) {//当两概率之差小于0.000000001时可近似认为相等
                    diffBR = diffAR;
                    //记录下此时的灰度级
                    kR = (byte)j;
                }
                else {
                    kR = (byte)Math.Abs(j - 1);
                    break;
                }
            }
            if (kR == 255) {
                for (int l = i; l < 256; l++) {
                    mapPixelR[l] = kR;
                }
                break;
            }
            mapPixelR[i] = kR;
        }
        //G
        for (int i = 0; i < 256; i++) {
            diffBG = 1;
            for (int j = kG; j < 256; j++) {
                diffAG = Math.Abs(srcCpG[i] - matchCpG[j]);
                if (diffAG - diffBG < 1.0E-08) {
                    diffBG = diffAG;
                    kG = (byte)j;
                }
                else {
                    kG = (byte)Math.Abs(j - 1);
                    break;
                }
            }
            if (kG == 255) {
                for (int l = i; l < 256; l++) {
                    mapPixelG[l] = kG;
                }
                break;
            }
            mapPixelG[i] = kG;
        }
        //B
        for (int i = 0; i < 256; i++) {
            diffBB = 1;
            for (int j = kB; j < 256; j++) {
                diffAB = Math.Abs(srcCpB[i] - matchCpB[j]);
                if (diffAB - diffBB < 1.0E-08) {
                    diffBB = diffAB;
                    kB = (byte)j;
                }
                else {
                    kB = (byte)Math.Abs(j - 1);
                    break;
                }
            }
            if (kB == 255) {
                for (int l = i; l < 256; l++) {
                    mapPixelB[l] = kB;
                }
                break;
            }
            mapPixelB[i] = kB;
        }
        //映射变换
        BitmapData bmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        unsafe {
            byte* ptr = null;
            for (int i = 0; i < dstBmp.Height; i++) {
                ptr = (byte*)bmpData.Scan0 + i * bmpData.Stride;
                for (int j = 0; j < dstBmp.Width; j++) {
                    ptr[j * 3 + 2] = mapPixelR[ptr[j * 3 + 2]];
                    ptr[j * 3 + 1] = mapPixelG[ptr[j * 3 + 1]];
                    ptr[j * 3] = mapPixelB[ptr[j * 3]];
                }
            }
        }
        dstBmp.UnlockBits(bmpData);
        return true;
    }

    /// <summary>
    /// 计算各个图像分量的累计概率分布
    /// </summary>
    /// <param name="srcBmp">原始图像</param>
    /// <param name="cpR">R分量累计概率分布</param>
    /// <param name="cpG">G分量累计概率分布</param>
    /// <param name="cpB">B分量累计概率分布</param>
    private static void getCumulativeProbabilityRGB(Bitmap srcBmp, out double[] cpR, out double[] cpG, out double[] cpB) {
        if (srcBmp == null) {
            cpB = cpG = cpR = null;
            return;
        }
        cpR = new double[256];
        cpG = new double[256];
        cpB = new double[256];
        int[] hR = null;
        int[] hG = null;
        int[] hB = null;
        double[] tempR = new double[256];
        double[] tempG = new double[256];
        double[] tempB = new double[256];
        getHistogramRGB(srcBmp, out hR, out hG, out hB);
        int totalPxl = srcBmp.Width * srcBmp.Height;
        for (int i = 0; i < 256; i++) {
            if (i != 0) {
                tempR[i] = tempR[i - 1] + hR[i];
                tempG[i] = tempG[i - 1] + hG[i];
                tempB[i] = tempB[i - 1] + hB[i];
            }
            else {
                tempR[0] = hR[0];
                tempG[0] = hG[0];
                tempB[0] = hB[0];
            }
            cpR[i] = (tempR[i] / totalPxl);
            cpG[i] = (tempG[i] / totalPxl);
            cpB[i] = (tempB[i] / totalPxl);
        }
    }

    /// <summary>
    /// 获取图像三个分量的直方图数据
    /// </summary>
    /// <param name="srcBmp">图像</param>
    /// <param name="hR">R分量直方图数据</param>
    /// <param name="hG">G分量直方图数据</param>
    /// <param name="hB">B分量直方图数据</param>
    public static void getHistogramRGB(Bitmap srcBmp, out int[] hR, out int[] hG, out int[] hB) {
        if (srcBmp == null) {
            hR = hB = hG = null;
            return;
        }
        hR = new int[256];
        hB = new int[256];
        hG = new int[256];
        BitmapData bmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        unsafe {
            byte* ptr = null;
            for (int i = 0; i < srcBmp.Height; i++) {
                ptr = (byte*)bmpData.Scan0 + i * bmpData.Stride;
                for (int j = 0; j < srcBmp.Width; j++) {
                    hB[ptr[j * 3]]++;
                    hG[ptr[j * 3 + 1]]++;
                    hR[ptr[j * 3 + 2]]++;
                }
            }
        }
        srcBmp.UnlockBits(bmpData);
        return;
    }


效果图:


补充:

假设pr(r)是原始图像灰度分布的概率密度函数,pz(z)是希望得到的概率密度函数,如何建立这两个概率密度函数之间的联系就是直方图规定化处理的关键。这两个概率密度函数直接进行转换是比较困难的,这里我们需要一个“桥梁”来做一个转换。这个“桥梁”就是直方图均衡化中所使用的累计概率密度函数。

所以第一步就是将两个图像都做均衡化处理。


由于两幅图像都做了均衡化处理,所以,pr(r)和pz(z)具有同样的均匀密度。(2)的逆过程为

这样,如果用原始图像中得到的均匀灰度级s来代替逆过程中的u,其结果灰度级就是所要求的概率密度函数pz(z)的灰度级。



2020-04-07 12:53:39 qq_41650371 阅读数 386

实验题目:

编程实现灰度和彩色图像的直方图均衡化处理。要求给出原始图像的直方图、均衡化图像及其直方图和直方图均衡化时所用的灰度级变换曲线图。注意彩色图像需要编程实现图像由RGB色彩空间到HSI的变换,然后在亮度通道上进行直方图均衡化。

源码:

对灰度图像的处理,给出原始图像的直方图、均衡化图像及其直方图和直方图均衡化时所用的灰度级变换曲线图

// 灰度图像
clear 
%读入彩色图像将其灰度化
pic=imread('grey.png');	
imshow(pic)		%显示出来
title('输入的灰色png图像')

%绘制直方图
[m,n]=size(pic);	%测量图像尺寸参数
GP=zeros(1,256);	%预创建存放灰度出现概率的向量
N=zeros(1,256);
for k=0:255
    N(k+1) = length(find(pic==k));    
    GP(k+1)=length(find(pic==k))/(m*n); 	% 计算每级灰度出现的概率,将其存入 GP 中相应位置
end
figure,bar(0:255,GP,'b')	%绘制直方图title('原图像直方图')
xlabel('灰度值') 
ylabel('出现概率')

%直方图均衡化
CF=zeros(1,256);
for i=1:256
    for j=1:i
        CF(i)=GP(j)+CF(i);	%计算 Sk
    end
end
S2=round((CF*256)+0.5);	%将 Sk 归到相近级的灰度
for i=1:256
    GPeq(i)=sum(GP(find(S2==i)));	%计算现有每个灰度级出现的概率
end
figure,bar(0:255,GPeq,'b')	%显示均衡化后的直方图title('均衡化后的直方图')
xlabel('灰度值') 
ylabel('出现概率')

%图像均衡化
PA=pic;
for i=0:255
PA(find(pic==i))=S2(i+1);	%将各个像素归一化后的灰度值赋给这个像素
end
figure,imshow(PA)	

%绘制灰度级变换曲线
figure
plot(0:255,S2,'r')
xlabel('均值化前')
ylabel('均值化后')
grid on
legend('灰度级变换曲线');

对彩色图像的处理,给出原始图像的直方图、均衡化图像及其直方图和直方图均衡化时所用的灰度级变换曲线图。注意彩色图像需要编程实现图像由RGB色彩空间到HSI的变换,然后在亮度通道上进行直方图均衡化。

// 彩色图像
function pic2()
clear;
pic=imread('color.jpg');	%读入 JPG 彩色图像文件
imshow(pic)		%显示出来
title('原图像');

[M,N,D] = size(pic);
%提取单通道分量
pic=double(pic);
r=pic(:,:,1);
g=pic(:,:,2);
b=pic(:,:,3);
%实现转换
angle=acos(0.5*((r-g)+(r-b))./(sqrt((r-g).^2+(r-b).*(g-b))));
if (b>=g)
    H = 2*pi-angle;
else
    H = angle;
end
H=H/(2*pi);
S=1-3.*(min(min(r,g),b))./(r+g+b);
H(S==0)=0;
I=(r+g+b)/3;
I=uint8(I);%范围不超[0,255]
%统计像素亮度
INumPixel = zeros(1,256);%用长度为256的一维数组统计各灰度值的数目
for i = 1:M  
    for j = 1: N  
        INumPixel(I(i,j) + 1) = INumPixel(I(i,j) + 1) + 1;  
    end  
end  
%亮度直方图
GP = zeros(1,256);  
for i = 1:256  
    GP(i) = INumPixel(i) / (M * N * 1.0);  
end  
figure,bar(0:255,GP,'b')	%绘制直方图title('原图像直方图')
xlabel('亮度值') 
ylabel('出现概率')

%直方图均衡化
S2 = zeros(1,256);  
for i = 1:256  
    if i == 1  
        S2(i) = GP(i);  
    else  
        S2(i) = S2(i - 1) + GP(i);  
    end  
end  
                                %S2乘上最大灰度255并且向上取整  
S2 = uint8(255 .* S2 + 0.5);  
                                %将原图像各个位置灰度值映射到新值  
for i = 1:M  
    for j = 1: N  
        Inew(i,j) = S2(I(i,j)+1);  
    end 
end
for i=1:256
    GPeq(i)=sum(GP(find(S2==i)));	%计算现有每个灰度级出现的概率
end
figure
plot(0:255,GPeq,'b')	%显示均衡化后的直方图title('均衡化后的直方图')
xlabel('亮度值') 
ylabel('出现概率')

%绘制亮度级变换曲线
figure
plot(0:1:255,S2,'r')
xlabel('均值化前')
ylabel('均值化后')
grid on
legend('亮度级变换曲线');

I=Inew;
H=H*2*pi;
I=double(I);
S=double(S);
H=double(H);
[m,n]=size(H);
%转换
for i = 1:m
    for j = 1:n
        if (0<=H(i,j))&(H(i,j)<2*pi/3)
            B(i,j)=I(i,j).*(1-S(i,j));
            R(i,j)=I(i,j).*(1+S(i,j).*cos(H(i,j))./cos(pi/3-H(i,j)));
            G(i,j)=3*I(i,j)-(R(i,j)+B(i,j));
        end
        if (2*pi/3<=H(i,j))&(H(i,j)<4*pi/3)
            H(i,j)=H(i,j)-2*pi/3;
            R(i,j)=I(i,j).*(1-S(i,j));
            G(i,j)=I(i,j).*(1+S(i,j).*cos(H(i,j)-2*pi/3)./cos(pi-H(i,j)));
            B(i,j)=3*I(i,j)-(R(i,j)+G(i,j));
        end
        if (4*pi/3<=H(i,j))& (H(i,j)<2*pi)
            H(i,j)=H(i,j)-4*pi/3;
            G(i,j)=I(i,j).*(1-S(i,j));
            B(i,j)=I(i,j).*(1+S(i,j).*cos(H(i,j)-4*pi/3)./cos(5*pi/3-H(i,j)));
            R(i,j)=3*I(i,j)-(G(i,j)+B(i,j));
        end
    end
end

output3=cat(3,R,G,B);
output3=uint8(output3);
figure
imshow(output3)

实验结果

灰度图像
灰度原图 在这里插入图片描述
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述

彩色图像
在这里插入图片描述晋亨话之秋

在这里插入图片描述 在这里插入图片描述
在这里插入图片描述

通过原始图像和均衡化后的图像对比,原始图像直方图与均衡化后直方图对比,均衡化后的直方图灰度值更加平均,在整幅图像中不再集中。

2016-05-07 18:16:58 qq404752007 阅读数 7384

图像处理大型科普——图像直方图


偶尔跟在浙大读研的湖大一姐谈起轮廓识别,她正在做一个能把衣服分类成不同款式的应用,衣服平摊好,然后用手机一扫,马上就能告诉你这是T-SHIRT还是裙子,什么?你敢说这玩意鸡肋?T-SHIRT和裙子当然容易分别,但那些

莲蓬裙、连衣裙、连体裙、半截裙、背带裙、连体裤、热裤、短裤、超短裤、齐X裤、打底裤、靴裤、吊带裤你真的分得清楚么?

分不清的先去一旁罚歌一首

继续说正事

学姐大概是这么做的,先灰度,然后获取高频分量,然后根据高频分量二值化,然后获取轮廓的点集,然后通过直方图跟数据库里的预设模型数据做相似度匹配,最后匹配度高的是结果。

慢着,直方图能做匹配??

话刚码完,学姐刷的一下甩我一篇英文论文:“一起看看?”(论文的链接甩这:Shape-context.pdf

在度娘、谷神以及有道哥的帮助下大致理解了文中的意思,也让我对直方图这个概念有了更深的认识

对于刚刚接触matlab图像处理的来说,一谈起直方图马上就想到了用imhist()绘制图像的直方图

又或者是用histeq()均衡直方图,让图片对比度更加高,甚至去除图片中的白噪(之前也写过一篇去除雾霾白噪的:MATLAB图像处理:一分钟去除图片中的雾霾

尽管matlab中的直方图有很多现成的应用,但千万别把直方图的定义限制在这几个函数里了,难不成人家Word\Excel里的直方图就不是直方图了么?

N种直方图区别和分类

联想到各种报表、文案中的直方图,我觉得我们之所以使用直方图,主要是希望能有分类统计的功能,
对于图像处理中的直方图,对像素进行分类统计是我们最常使用的一种方式

于是得到灰度值的直方图:

关于这张图,也是有很多地方可以用的到的

比如说我们可以利用图像的细长峰值判断图像的主要信息集中在哪个灰度上,

利用图像两峰之间的面积判断背景色在哪个灰度上,

也可以用峰谷判断噪点所在的灰度,

同样可以利用该图中的灰度期望给出一个理想的二值化分界值。

甚至在手绘(最近有点迷上painter手绘。。。)中决定绘画中的主色调,背景色,和高亮点所对应的色彩范围,大大减少配色的难度。

倘若把这个直方图量化开来,变成一个size = 256的[gray_value ,count_num]形式的矩阵,完全可以把它当做这张图片特有的指纹,一个256维的向量,无论这张图片旋转多少角度,这个向量都不会改变,是的,终于绕回来了,任意一张图片都可以对应这样的一个向量,并且维度都是256哦,至于向量,管他多少维的,都是一条直线,那么两条直线的相似度不就是直线的夹角咯,(此处不贴公式,想一探究竟的请自行百度“n维向量的夹角”)哈哈,这不就完美的解决了图片旋转前后的匹配的问题么~~

做事要严谨,我一开始的确以为这就是直方图的匹配方式的,不过仔细一想,256维的向量角度。。。。。这计算量,这精确度,还得经受环境光源的影响,这么苛刻的匹配环境用在识别衣服款式上,几乎是得不到回报的

回到上文中提到的论文中,还得怪我们对直方图的认识太肤浅狭隘,我们一直都在灰度值的直方图中打转转,用统计的思想去碰概率,然而直方图不仅是统计,还可以分类,不仅是灰度值的直方图,也可以是别的什么的直方图啊。


前方高能,集中注意

这里特别提到的,是用于分类 轮廓点集 的一种直方图:

a和b是两张图的轮廓点集,c是一个坐标系,把一个圆按照离圆心的距离、和正北的角度两个指标分成60个块(角度分成12类区域,距离分成5类区域,12*5=60),然后遍历点集的每一个点,以当前遍历的点为C坐标的圆心,把其他点按60个区域划分,变成一个size = 60的素组,保存着每个块中的轮廓点数量,现在每个点都对应了一个关于其他点的直方图,就像d、e、f三张图一样,60个小方块,点的数量越多,颜色越深。

最后一步,比较a、b两两点间的相似度,获取匹配度最高的那个点与之对应,最终得运行结果就像图g中一样,无论图像旋转,缩放,还是光源的角度强弱,都不会对结果造成有效影响,最大程度上保证了匹配的精度。

这是一个很好的例子,来说明直方图在分类上的特别能力,我觉得在之后的应用中, 我们也可以通过制定分类指标的方式,把抽象的数据通过直方图分成大类,再进行简单的统计运算,最大化的发挥直方图在图相处理领域分类统计的作用。

希望大家通过这篇博文可以对身边的事物有更多奇葩的使用想法和理解方式,老是用别人的东西也没意思噻,反正这老外的思维我是服了


OVER~~