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

xiaoxiao2021-02-28  111

一、先来看一个通过图像遍历进行图像降彩色(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; }

转载请注明原文地址: https://www.6miu.com/read-49343.html

最新回复(0)