对于黯淡的彩色图像调节效果.........
对于暗淡图像的调节,灰度直方图基本均匀碾平:
对于过度饱和图像的效果,右上角展示了直返图的效果,可以看见基本均匀碾平了:
参考代码:
#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;
}