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


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



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




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



参考代码:

#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 255

void 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_GRAYSCALE
	if (!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"值,包括0
	size_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");
	}

	// 获取图像中的最大、最小像素值/或者指定间隔范围的最大最小值,3
	if (0 != flat_min || 0 != flat_max)
		gray_quantiles(pImgData, img_size, flat_min, flat_max, &min, &max,nchannel);
	else
		gray_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;
}


  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值