您的位置:首页 > 理论基础

关于图像降色彩后,彩色直方图统计与实际像素值不匹配问题

2017-05-06 10:54 513 查看


一、先来看一个通过图像遍历进行图像降彩色(Color Reduce)的例子:


遍历图像的最基本方式:at<typename>(i,j)

Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。下面我们通过一个图像处理中的实际来说明它的用法。

在实际应用中,我们很多时候需要对图像降色彩,因为256*256*256实在太多了,在图像颜色聚类或彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,我们的思路是将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。
void colorReduce(Mat& image, int div)
{
//split(image, channelsRGB);

for (int i = 0; i<image.rows; i++)
{
for (int j = 0; j<image.cols; j++)
{
image.at<Vec3b>(i, j)[0] = image.at<Vec3b>(i, j)[0] / div*div + div / 2;
image.at<Vec3b>(i, j)[1] = image.at<Vec3b>(i, j)[1] / div*div + div / 2;
image.at<Vec3b>(i, j)[2] = image.at<Vec3b>(i, j)[2] / div*div + div / 2;
}
}

}


int main()
{
Mat image = imread("123.jpg");

imshow("input",image);

colorReduce(image, 64);
waitKey(0);
imshow("output", image);

cout << image << endl;

waitKey(0);

return 0;
}




可见图像经过降色彩(Color Reduce)后,像素值只有“32,96,160,224”共四个值。


二、下面再看一个图像彩色直方图统计的例子:

HistogramND.hpp

#include<opencv2\opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;

class HistogramND{
private:
Mat image;//源图像
int hisSize[1], hisWidth, hisHeight;//直方图的大小,宽度和高度
float range[2];//直方图取值范围

const float *ranges;

//Mat channelsRGB[3];//分离的BGR通道

vector<Mat> channelsRGB;

MatND outputRGB[3];//输出直方图分量
public:
HistogramND(){
hisSize[0] = 256;
hisWidth = 400;
hisHeight = 400;
range[0] = 0.0;
range[1] = 255.0;

ranges = &range[0];
}

//导入图片
bool importImage(String path){
image = imread(path);
//bool importImage(Mat path){
image = imread(path);
//	path.copyTo(image);
if (!image.data)
return false;
return true;
}

//分离通道
void splitChannels(){
split(image, channelsRGB);
//cout << channelsRGB.at(0) << endl;
}

//计算直方图
void getHistogram(){
//calcHist(&channelsRGB[0], 1, 0, Mat(), outputRGB[0], 1, hisSize, &ranges);
//calcHist(&channelsRGB[1], 1, 0, Mat(), outputRGB[1], 1, hisSize, &ranges);
//calcHist(&channelsRGB[2], 1, 0, Mat(), outputRGB[2], 1, hisSize, &ranges);

calcHist(&channelsRGB.at(0), 1, 0, Mat(), outputRGB[0], 1, hisSize, &ranges);
calcHist(&channelsRGB.at(1), 1, 0, Mat(), outputRGB[1], 1, hisSize, &ranges);
calcHist(&channelsRGB.at(2), 1, 0, Mat(), outputRGB[2], 1, hisSize, &ranges);

//cout << channelsRGB[0] << endl;
//输出各个bin的值
for (int i = 0; i < hisSize[0]; ++i){
cout << i << "   B:" << outputRGB[0].at<float>(i);
cout << "   G:" << outputRGB[1].at<float>(i);
cout << "   R:" << outputRGB[2].at<float>(i) << endl;
}
}

//显示直方图
void displayHisttogram(){
Mat rgbHist[3];
for (int i = 0; i < 3; i++)
{
rgbHist[i] = Mat(hisWidth, hisHeight, CV_8UC3, Scalar::all(0));
}
normalize(outputRGB[0], outputRGB[0], 0, hisWidth - 20, NORM_MINMAX);
normalize(outputRGB[1], outputRGB[1], 0, hisWidth - 20, NORM_MINMAX);
normalize(outputRGB[2], outputRGB[2], 0, hisWidth - 20, NORM_MINMAX);
for (int i = 0; i < hisSize[0]; i++)
{
int val = saturate_cast<int>(outputRGB[0].at<float>(i));
rectangle(rgbHist[0], Point(i * 2 + 10, rgbHist[0].rows), Point((i + 1) * 2 + 10, rgbHist[0].rows - val), Scalar(0, 0, 255), 1, 8);
val = saturate_cast<int>(outputRGB[1].at<float>(i));
rectangle(rgbHist[1], Point(i * 2 + 10, rgbHist[1].rows), Point((i + 1) * 2 + 10, rgbHist[1].rows - val), Scalar(0, 255, 0), 1, 8);
val = saturate_cast<int>(outputRGB[2].at<float>(i));
rectangle(rgbHist[2], Point(i * 2 + 10, rgbHist[2].rows), Point((i + 1) * 2 + 10, rgbHist[2].rows - val), Scalar(255, 0, 0), 1, 8);
}

cv::imshow("R", rgbHist[0]);
imshow("G", rgbHist[1]);
imshow("B", rgbHist[2]);
imshow("image", image);
}
};

// int main(){
// string path = "1.jpg";
// HistogramND hist;
// if (!hist.importImage(path)){
// cout << "Import Error!" << endl;
// return -1;
// }
// hist.splitChannels();
// hist.getHistogram();
// hist.displayHisttogram();
// waitKey(0);
// return 0;
// }

//void run(Mat path){
void run(String path){
HistogramND hist;
if (!hist.importImage(path)){
cout << "Import Error!" << endl;
//return -1;
}
hist.splitChannels();
hist.getHistogram();

hist.displayHisttogram();
waitKey(0);
}

该程序通过split分离BRG三通道,再用calcHist来对三通道直方图进行统计。将此头文件include到刚才的cpp文件中,并对Main函数进行相应修改:
int main()
{
Mat image = imread("123.jpg");
imshow("input",image);
colorReduce(image, 64);

waitKey(0);

imshow("output", image);
imwrite("output.jpg",image);

run("output.jpg");

waitKey(0);
return 0;
}



可见BGR三通道像素值并不是如刚才进行图像降色彩(Color Reduce)后所得的结果,在非“32,96,160,224”也不为0。


三、分析问题:

(1)设想1:统计直方图程序错误
将HistogramND.hpp run函数中统计直方图部分和显示直方图
hist.getHistogram();

hist.displayHisttogram();
屏蔽掉之后,再打印B通道像素值发现其在非“32,96,160,224”也不为0。
于是怀疑是图像色彩三通道分离split的问题。
(2)设想2:色彩三通道分离split问题

为了对比,直接在cpp文件中main函数中用split做三通道分离,再打印B通道像素值又发现其为“32,96,160,224”这四个值。
于是怀疑是HistogramND.hpp文件预处理的问题。
(3)设想3:HistogramND.hpp文件预处理问题

将HistogramND.hpp预处理和定义中多次修改后对比后发现此处不存在问题。
于是怀疑是main函数中存在问题。
(4)设想4:cpp文件中main函数问题

经多次验证后发现,在得到输出Mat
image后,在用hpp文件对图像进行处理时,是先在cpp文件中使用imwrite("output.jpg",image)函数后,再在hpp文件中使用用imread函数将图片读取后进行处理。
而图片存储为jpg格式后,会进行压缩,以至于图像经过
imwrite 再 imread 之后像素值发生了一定变化。

于是确定最终的问题为图片存储为jpg格式后的压缩导致像素值变化。


四、解决问题:

(1)将图片保存为bmp格式

当图片存储为bmp格式时,不会被压缩,图像像素值也不会被改变。
int main()
{
Mat image = imread("123.jpg");

imshow("input",image);
colorReduce(image, 64);

waitKey(0);

imshow("output", image);
imwrite("output.bmp", image);

run("output.bmp");

waitKey(0);
return 0;
}


然后再用hpp文件中的直方图统计方法得到如下结果:



(2)程序中读取图像文件直接修改为读取Mat

//导入图片
//bool importImage(String path){
// image = imread(path);
bool importImage(Mat path){
path.copyTo(image);
if (!image.data)
return false;
return true;
}
void run(Mat path){
//void run(String path){
HistogramND hist;
if (!hist.importImage(path)){
cout << "Import Error!" << endl;
//return -1;
}
hist.splitChannels();
hist.getHistogram(); hist.displayHisttogram();
waitKey(0);
}
int main()
{
Mat image = imread("123.jpg");
imshow("input",image);

colorReduce(image, 64);

waitKey(0);

imshow("output", image);

run(image);

waitKey(0);
return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息