1 颜色矩原理
颜色矩(color moments)是由Stricker和Orengo[1]所提出的一种非常简单而有效的颜色特征。这种方法的数学基础在于图像中任何的颜色分布均可以用它的矩来表示。此外,由于颜色分布信息主要集中在低阶矩中,因此仅采用颜色的一阶矩(mean)、二阶矩(variance)和三阶矩(skewness)就足以表达图像的颜色分布。与颜色直方图相比,该方法的另一个好处在于无需对特征进行向量化。因此,图像的颜色矩一共只需要9个分量(3个颜色分量,每个分量上3个低阶矩),与其他的颜色特征相比是非常简洁的。在实际应用中为避免低次矩较弱的分辨能力,颜色矩常和其它特征结合使用,而且一般在使用其它特征前起到过滤缩小范围(narrow down)的作用。
三种颜色矩的数学定义:
其中,
pi,j
表示彩色图像第i个颜色通道分量中第j个图像像素值,N表示单通道图像中总的像素数。
- 一阶矩表示第i个颜色通道上所有像素的均值
- 二阶矩表示第i 颜色通道上所有像素的标准差
- 三阶矩表示第i个通道上所有像素的斜度(skewness)的三次方根。
图像的三个分量的前三阶颜色矩组成一个9维颜色特征向量。
2 颜色矩的C++实现
在网上找了很久也没有找到公开的用C++实现的颜色矩代码,OpenCV里面也没有现成的函数可以调用。因为项目需要,只能参考公式和其他版本(Matlab、Python)的颜色矩实现代码自己用C++写一个。但由于很久没用OpenCV了,写出来并调通还花了不少时间。本人水平有限,如有更好的C++实现代码希望交流学习。废话不多说,下面是颜色矩的C++实现(软件环境Windows10+VS2013+OpenCV2.4.9):
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
double calc3orderMom(Mat &channel) //计算三阶矩
{
uchar *p;
double mom=0;
double m = mean(channel)[0]; //计算单通道图像的均值
int nRows = channel.rows;
int nCols = channel.cols;
if (channel.isContinuous()) //连续存储有助于提升图像扫描速度
{
nCols *= nRows;
nRows = 1;
}
for (int i = 0; i < nRows; i++) //计算立方和
{
p = channel.ptr<uchar>(i);
for (int j = 0; j < nCols; j++)
mom += pow((p[j] - m) ,3);
}
float temp;
temp = cvCbrt((float)(mom / (nRows*nCols))); //求均值的立方根
mom = (double)temp;
return mom;
}
//计算9个颜色矩:3个通道的1、2、3阶矩
double *colorMom(Mat &img)
{
double *Mom = new double[9]; //存放9个颜色矩
Mat hsvimg;
if (img.channels() != 3)
cout << "Error,input image must be a color image" << endl;
Mat r(img.rows, img.cols, CV_8U);
Mat g(img.rows, img.cols, CV_8U);
Mat b(img.rows, img.cols, CV_8U);
Mat channels[] = { b, g, r };
split(img, channels);
Mat tmp_m, tmp_sd;
//计算b通道的颜色矩
meanStdDev(b, tmp_m, tmp_sd);
Mom[0] = tmp_m.at<double>(0, 0);
Mom[3] = tmp_sd.at<double>(0, 0);
Mom[6] = calc3orderMom(b);
// cout << Mom[0] << " " << Mom[1] << " " << Mom[2] << " " << endl;
//计算g通道的颜色矩
meanStdDev(g, tmp_m, tmp_sd);
Mom[1] = tmp_m.at<double>(0, 0);
Mom[4] = tmp_sd.at<double>(0, 0);
Mom[7] = calc3orderMom(g);
// cout << Mom[3] << " " << Mom[4] << " " << Mom[5] << " " << endl;
//计算r通道的颜色矩
meanStdDev(r, tmp_m, tmp_sd);
Mom[2] = tmp_m.at<double>(0, 0);
Mom[5] = tmp_sd.at<double>(0, 0);
Mom[8] = calc3orderMom(r);
// cout << Mom[6] << " " << Mom[7] << " " << Mom[8] << " " << endl;
return Mom;//返回颜色矩数组
}
//测试代码
int main()
{
Mat src = imread("purple.jpg");
if (src.data == NULL)
cout << "image load failed!";
double *Mom ;
Mom = colorMom(src);
cout << "Color moments:";
for (int i = 0; i < 9; i++)//输出颜色矩
cout << Mom[i] << " ";
cout << endl;
waitKey();
}
运行结果:
参考文献
1.stricker M ,Orengo M.similarity of col0rimages[J].Proc.SPIE St0rage and Retrieval f0r Image and Video Dtabases,1995,242O:381—392
2. 颜色特征提取(二)——颜色矩
3. 颜色矩原理及Python实现