数字图像处理,一种简单的颜色平衡算法

来源:互联网 发布:淘宝一分钱秒杀怎么买 编辑:程序博客网 时间:2024/05/16 05:17


对于黯淡的彩色图像调节效果.........



对于暗淡图像的调节,灰度直方图基本均匀碾平




对于过度饱和图像的效果,右上角展示了直返图的效果,可以看见基本均匀碾平了:



参考代码:

#include "string"#include "vector"#include <windows.h>#include <opencv2/legacy/legacy.hpp>#include <opencv2/nonfree/nonfree.hpp>//opencv_nonfree模块:包含一些拥有专利的算法,如SIFT、SURF函数源码。 #include "opencv2/core/core.hpp"#include "opencv2/features2d/features2d.hpp"#include "opencv2/highgui/highgui.hpp"#include <opencv2/nonfree/features2d.hpp>using namespace cv;using namespace std;/// 全局变量的声明与初始化const int alpha_slider_max = 50;//滑条最大值unsigned char *grayimg;unsigned char *colorimg;int smin, smax;           // 饱和率Mat m_gImg;Mat m_gImgSrc;bool isGray;#define MAX_UCHAR_PIXEL 255void on_trackbar_gray(int, void*);void on_trackbar_color(int, void*);static void gray_quantiles(const unsigned char *pImgData, size_t img_size,size_t flat_min, size_t flat_max,unsigned char *pMinPixel, unsigned char *pMaxPixel, int nchannel);static void gray_minmax(const unsigned char *pImgData, size_t img_size,unsigned char *pMinPixel, unsigned char *pMaxPixel, int nchannel);static unsigned char *gray_rescale(unsigned char *pImgData, size_t img_size,unsigned char min, unsigned char max, int nchannel);unsigned char *gray_balance(unsigned char *pImgData, size_t img_size,size_t flat_min, size_t flat_max, int nchannel = 0);void my_image_enhancement();unsigned char *color_balance(unsigned char *pImgData, size_t size,size_t flat_min, size_t flat_max);bool draw_hist(Mat &img);Mat getHistogramImage(const Mat& hist, Size imgSize);int main(int argc, char **argv){//const char* img_path = "oct.bmp";//const char* img_path = "colors_large.png";//const char* img_path = "input_0.png";//const char* img_path = "a1.jpg";//const char* img_path = "seed.tif";const char* img_path = "CT.bmp";m_gImgSrc = imread(img_path, CV_LOAD_IMAGE_ANYCOLOR);//CV_LOAD_IMAGE_COLOR,CV_LOAD_IMAGE_GRAYSCALEif (!m_gImgSrc.data) { printf("Error loading src1 \n"); return -1; }imshow("原效果图", m_gImgSrc);if (m_gImgSrc.channels() == 1)isGray = true;//全局变量my_image_enhancement();return EXIT_SUCCESS;}/*** @brief 获得指定间隔下(flat_min,flat_max)的最大最小像素值** @param pImgData, 输入/输出图像数据* @param img_size, 图像图像数据的大小(高度*宽度)* @param flat_min、flat_max ,需要展平的像素数量* @param pMinPixel、 pMaxPixel,保存限定像素值间隔下(flat_min,flat_max)的最大最小值,如果为NULL则忽视*/static void gray_quantiles(const unsigned char *pImgData, size_t img_size,size_t flat_min, size_t flat_max,unsigned char *pMinPixel, unsigned char *pMaxPixel, int nchannel){int Channels = 1;if (!isGray)Channels = 3;// 直方图必须包含所有可能的"unsigned char"值,包括0size_t h_size = MAX_UCHAR_PIXEL + 1;size_t histogram[MAX_UCHAR_PIXEL + 1];size_t i;// 计算累积直方图memset(histogram, 0x00, h_size * sizeof(size_t));for (i = 0; i < img_size; i++)histogram[(size_t)pImgData[i*Channels+nchannel]] += 1;for (i = 1; i < h_size; i++)histogram[i] += histogram[i - 1];//累积直方图// 获得新的最大最小像素值if (NULL != pMinPixel) {// 正向遍历累积直方图// 找到第一个大于nb_min的像素值 i = 0;while (i < h_size && histogram[i] <= flat_min)i++;//当前位置的值即为所需最小像素值*pMinPixel = (unsigned char)i;}if (NULL != pMaxPixel) {// 反向遍历累积直方图// 找到第一个小于等于(size - flat_max)的像素值 i = h_size - 1;while (i < h_size && histogram[i] >(img_size - flat_max))i--;// 如果i不是在直方图末尾// 调到临近的下一个位置,该位置的值 > (size - nb_max)if (i < h_size - 1)i++;*pMaxPixel = (unsigned char)i;}return;}/*** @brief,获取图像数据的最大最小像素,在pMinPixel、pMaxPixel中** @param pimgdata,输入图像数据* @param img_size,图像数组的大小* @param pMinPixel、pMaxPixel,保留图像数据的最大最小值,如果为NULL则忽视*/static void gray_minmax(const unsigned char *pImgData, size_t img_size,unsigned char *pMinPixel, unsigned char *pMaxPixel, int nchannel){unsigned char min_pixel, max_pixel;size_t i;int Channels = 1;if (!isGray)Channels = 3;//计算图像数据中的最小和最大像素值min_pixel = pImgData[0];max_pixel = pImgData[0];for (i = 1; i < img_size; i++) {if (pImgData[i*Channels + nchannel] < min_pixel)min_pixel = pImgData[i*Channels + nchannel];if (pImgData[i*Channels + nchannel] > max_pixel)max_pixel = pImgData[i*Channels + nchannel];}//将结果保存在指针中,并返回if (NULL != pMinPixel)*pMinPixel = min_pixel;if (NULL != pMaxPixel)*pMaxPixel = max_pixel;return;}/*** @brief 重新调整图像数据** 该函数原地操作,根据指定的两个边界(flat_min, flat_max)重新调整像素值(全部增强)* 并且小于flat_min的原像素置0,大于flat_max的原像素置为 MAX_UCHAR_PIXEL,即使之饱和.** @param pImgData,输入/输出图像数据* @param img_size, 输入图像数据的尺寸* @param flat_min, flat_max 需要被碾平(使其饱和或者衰减至最小值)的最小最大像素值** @return pImgData,调整后的图像像素值*/static unsigned char *gray_rescale(unsigned char *pImgData, size_t img_size,unsigned char flat_min, unsigned char flat_max, int nchannel){size_t i;int Channels = 1;if (!isGray)Channels = 3;if (flat_max < flat_min){for (i = 0; i < img_size; i++)pImgData[i * Channels + nchannel ] = MAX_UCHAR_PIXEL / 2;//全部取一样的值---图像变灰}else {// 建立转换表 unsigned char norm[MAX_UCHAR_PIXEL + 1];for (i = 0; i < flat_min; i++)//小于flat_min的置零(变黑)norm[i] = 0;for (i = flat_min; i < flat_max; i++)norm[i] = (unsigned char)((i - flat_min) * MAX_UCHAR_PIXEL/ (double)(flat_max - flat_min) + 0.5);for (i = flat_max; i < MAX_UCHAR_PIXEL + 1; i++)//大于flat_max的置255(变白)norm[i] = MAX_UCHAR_PIXEL;// 根据表重新调整图像数据for (i = 0; i < img_size; i++)pImgData[i * Channels + nchannel] = norm[(size_t)pImgData[i * Channels + nchannel]];}return pImgData;}/*** @brief 图像的颜色平衡核心函数*/unsigned char *gray_balance(unsigned char *pImgData, size_t img_size,size_t flat_min, size_t flat_max, int nchannel){unsigned char min, max;// 数据的合理性检查if (NULL == pImgData) {//无数据fprintf(stderr, "错误:该指针为NULL!\n");abort();}if (flat_min + flat_max > img_size) {flat_min = (img_size - 1) / 2;flat_max = (img_size - 1) / 2;fprintf(stderr, "需要被碾平的像素数目太大\n");fprintf(stderr, "使用(size - 1) / 2\n");}// 获取图像中的最大、最小像素值/或者指定间隔范围的最大最小值,3if (0 != flat_min || 0 != flat_max)gray_quantiles(pImgData, img_size, flat_min, flat_max, &min, &max,nchannel);elsegray_minmax(pImgData, img_size, &min, &max, nchannel);//获得图像数据的最小最大值// 重新平衡像素(void)gray_rescale(pImgData, img_size, min, max, nchannel);return pImgData;}void my_image_enhancement(){smin = 0;smax = 0;//初始化饱和率if (isGray)//灰度图{/// 创建窗体namedWindow("灰度调节效果图", 1);/// 在创建的窗体中创建2个滑动条控件createTrackbar("smin(0-100(此时最大50)):", "灰度调节效果图", &smin, alpha_slider_max, on_trackbar_gray);createTrackbar("smax(0-100(此时最大50)):", "灰度调节效果图", &smax, alpha_slider_max, on_trackbar_gray);/// 结果在回调函数中显示on_trackbar_gray(smin, 0);on_trackbar_gray(smax, 0);waitKey(0);destroyWindow("灰度调节效果图");}else{/// 创建窗体namedWindow("彩色调节效果图", 1);/// 在创建的窗体中创建2个滑动条控件createTrackbar("smin(0-100(此时最大50)):", "彩色调节效果图", &smin, alpha_slider_max, on_trackbar_color);createTrackbar("smax(0-100(此时最大50)):", "彩色调节效果图", &smax, alpha_slider_max, on_trackbar_color);/// 结果在回调函数中显示on_trackbar_color(smin, 0);on_trackbar_color(smax, 0);waitKey(0);destroyWindow("彩色调节效果图");}return;}unsigned char *color_balance(unsigned char *pImgData, size_t size,size_t flat_min, size_t flat_max){(void)gray_balance(pImgData, size, flat_min, flat_max,0);(void)gray_balance(pImgData, size, flat_min, flat_max,1);(void)gray_balance(pImgData, size, flat_min, flat_max,2);return pImgData;}//opencv的回调函数void on_trackbar_gray(int, void*){double ss_min = (double)(smin);double ss_max = (double)(smax);m_gImg = m_gImgSrc.clone();//深复制(重新开辟了内存,注意区分浅复制)grayimg = m_gImg.data;;     //输入/输出数据size_t img_size = m_gImg.rows * m_gImg.cols;//执行算法grayimg = gray_balance(grayimg, img_size,img_size * (ss_min / 100.0),img_size * (ss_max / 100.0));//显示图像m_gImg.data = grayimg;draw_hist(m_gImg);imshow("灰度调节效果图", m_gImg);}//opencv的回调函数void on_trackbar_color(int, void*){double ss_min = (double)(smin);double ss_max = (double)(smax);m_gImg = m_gImgSrc.clone();//深复制(重新开辟了内存,注意区分浅复制)colorimg = m_gImg.data;;     //输入/输出数据size_t img_size = m_gImg.rows * m_gImg.cols;//执行算法colorimg = color_balance(colorimg, img_size,img_size * (ss_min / 100.0),img_size * (ss_max / 100.0));//显示图像m_gImg.data = colorimg;draw_hist(m_gImg);imshow("彩色调节效果图", m_gImg);}bool draw_hist(Mat &src){if (src.data == NULL)return false;if (!isGray){/// 分割成3个单通道图像 ( R, G 和 B )vector<Mat> rgb_planes;split(src, rgb_planes);/// 设定bin数目int histSize = 255;/// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true;bool accumulate = false;Mat r_hist, g_hist, b_hist;/// 计算直方图:calcHist(&rgb_planes[0], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate);calcHist(&rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate);calcHist(&rgb_planes[2], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImageR(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));Mat histImageG(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));Mat histImageB(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));/// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(r_hist, r_hist, 0, histImageR.rows, NORM_MINMAX, -1, Mat());normalize(g_hist, g_hist, 0, histImageG.rows, NORM_MINMAX, -1, Mat());normalize(b_hist, b_hist, 0, histImageB.rows, NORM_MINMAX, -1, Mat());/// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImageR, Point(bin_w*(i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(r_hist.at<float>(i))),Scalar(0, 0, 255), 2, 8, 0);line(histImageG, Point(bin_w*(i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(g_hist.at<float>(i))),Scalar(0, 255, 0), 2, 8, 0);line(histImageB, Point(bin_w*(i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);}/// 显示直方图namedWindow("calcHist Demo R", CV_WINDOW_AUTOSIZE);imshow("calcHist Demo R", histImageR);namedWindow("calcHist Demo G", CV_WINDOW_AUTOSIZE);imshow("calcHist Demo G", histImageG);namedWindow("calcHist Demo B", CV_WINDOW_AUTOSIZE);imshow("calcHist Demo B", histImageB);//waitKey(1000);}else{/// 设定bin数目int histSize = 255;/// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true;bool accumulate = false;Mat hist;/// 计算直方图:calcHist(&src, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));/// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());/// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(hist.at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(hist.at<float>(i))),Scalar(0, 255, 255), 1, 8, 0);}/// 显示直方图namedWindow("calcHist gray Demo", CV_WINDOW_AUTOSIZE);imshow("calcHist gray Demo", histImage);//waitKey(1000);}return true;}


3 0
原创粉丝点击