1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
| // // Created by zj on 19-11-24. //
#include "opencv2/highgui.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/imgproc.hpp" #include <iostream> #include <array>
using namespace std; using namespace cv;
const static array<Scalar, 3> colors = { Scalar(255, 0, 0), Scalar(0, 255, 0), Scalar(0, 0, 255) };
/** * 计算颜色直方图,图像取值固定为[0, 255] * @param src CV_8UC1或CV_8UC3大小图像 * @param histograms 直方图向量 * @param bins 直方图大小 */ void calc_color_hist(const Mat &src, vector<Mat> &histograms, int bins) { int channels = src.channels(); vector<Mat> img_planes; if (channels == 3) { split(src, img_planes); } else { // gray img_planes.emplace_back(src); }
float range[] = {0, 256}; //the upper boundary is exclusive const float *histRange = {range}; bool uniform = true, accumulate = false;
for (int i = 0; i < channels; i++) { Mat hist; calcHist(&img_planes[i], 1, nullptr, Mat(), hist, 1, &bins, &histRange, uniform, accumulate); // cout << hist.type() << endl; histograms.emplace_back(hist); } }
/** * 绘制直方图,首先对直方图标准化,再按比例绘制线条 */ void draw_color_hist(const vector<Mat> &histograms, Mat &histImage, int bins) { int hist_w = 512, hist_h = 400; histImage = Mat(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));
int bin_w = cvRound((double) hist_w / bins); for (int i = 0; i < histograms.size(); i++) { Mat hist = histograms[i]; Mat hist_tmp; // 标准化直方图,取值为[0.0, hist_h] normalize(hist, hist_tmp, 0, histImage.rows, NORM_MINMAX, -1, Mat()); // cout << hist_tmp.type() << endl; Scalar color = colors[i % colors.size()]; for (int i = 1; i < bins; i++) { line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(hist_tmp.at<float>(i - 1))), Point(bin_w * (i), hist_h - cvRound(hist_tmp.at<float>(i))), color, 2, 8, 0); } } }
/** * 手动计算颜色直方图 * @param src CV_8UC1或CV_8UC3 * @param histograms 颜色直方图向量 * @param bins 直方图大小 */ void calc_color_hist_manully(const Mat &src, vector<Mat> &histograms, int bins) { int channels = src.channels(); float ranges[] = {0.0, 256.0}; // 计算每个bin的取值范围 double range = (ranges[1] - ranges[0]) / bins;
// 分离图像 vector<Mat> img_planes; if (channels == 1) { img_planes.emplace_back(src); } else { // 分离3通道 split(src, img_planes); }
// 遍历所有通道,计算颜色直方图 for (int i = 0; i < channels; i++) { Mat hist = Mat::zeros(1, bins, CV_32F); auto *hdata = hist.ptr<float>(0);
Mat plane = img_planes[i]; // 遍历所有像素,从左到右,从上到下 for (int y = 0; y < plane.rows; y++) { auto *pdata = plane.ptr<uchar>(y);
for (int x = 0; x < plane.cols; x++) { hdata[cvFloor(static_cast<int>(pdata[x]) / range)] += 1; } } histograms.emplace_back(hist); } }
/** * 绘制直方图,首先手动对直方图标准化,再按比例绘制线条 * @param histograms 直方图列表 * @param histImage 绘制直方图 * @param bins 直方图大小 */ void draw_color_hist_manully(const vector<Mat> &histograms, Mat &histImage, int bins) { int hist_w = 512, hist_h = 400; histImage = Mat(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));
auto channels = histograms.size();
// 手动标准化直方图 vector<Mat> hist_tmps; for (int i = 0; i < channels; i++) { double min, max; cv::minMaxLoc(histograms[i], &min, &max); Mat hist_tmp = Mat::zeros(histograms[i].size(), CV_32F);
auto *hdata = histograms[i].ptr<float>(0); auto *hdata_tmp = hist_tmp.ptr<float>(0); for (int j = 0; j < bins; j++) { hdata_tmp[j] = static_cast<float>(hist_h * (hdata[j] - min) / (max - min)); } hist_tmps.emplace_back(hist_tmp); }
int bin_w = cvRound(1.0 * hist_w / bins); for (int i = 0; i < channels; i++) { Scalar color = colors[i % colors.size()]; auto *hdata = hist_tmps[i].ptr<float>(0);
for (int j = 1; j < bins; j++) { line(histImage, Point(bin_w * (j - 1), cvRound(hist_h - hdata[j - 1])), Point(bin_w * (j), cvRound(hist_h - hdata[j])), color, 2, 8, 0); } } }
int main(int argc, char **argv) { CommandLineParser parser(argc, argv, "{@input | ../lena.jpg | input image}"); Mat src = imread(parser.get<String>("@input"), IMREAD_COLOR); if (src.empty()) { return -1; }
// Mat gray; // cvtColor(src, gray, COLOR_BGR2GRAY); // src = gray;
int bins = 256; vector<Mat> histograms, histograms2; Mat histImage, histImage2;
// 调用OpenCV提供的直方图计算和标准化函数 double t1 = (double) getTickCount(); calc_color_hist(src, histograms, bins); draw_color_hist(histograms, histImage, bins); // 手动计算和标准化直方图 double t2 = (double) getTickCount(); calc_color_hist_manully(src, histograms2, bins); draw_color_hist_manully(histograms2, histImage2, bins); double t3 = (double) getTickCount();
double time1 = (t2 - t1) / getTickFrequency(); double time2 = (t3 - t2) / getTickFrequency(); cout << time1 << endl; cout << time2 << endl;
// 判断绘制图像是否相等 cv::Mat diff = histImage != histImage2; cout << sum(diff) << endl;
imshow("Source image", src); imshow("OpenCV API", histImage); imshow("手动实现", histImage2); waitKey();
return 0; }
|