ImageEnhance.h
#pragma once
#include <iostream>
#include <io.h>
#include <fstream>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
using namespace std;
#define Openmp_Threads1 2
#define Openmp_Threads2 4
struct ImageEnhanceParam
{
int doImageEnhance = 1;
float ThresholdRatio = 0.1;
float BrightnessScale = 0.96;
int winSize = 1;
double maxCG = 3.5;
};
void PrintCostTime(const char* str, double& t1, double& t2);
string SplitString(const string& filePath);
void getFiles(string path, vector<string>& files);
Mat PerfectReflectionAlgorithm(Mat& src, const float& ThresholdRatio);
Mat PerfectReflectionAlgorithmFast(Mat& src, const float& ThresholdRatio, const float& BrightnessScale);
Mat PerfectReflectionAlgorithmFast2(Mat& src, const float& ThresholdRatio, const float& BrightnessScale);
Mat AutoColorEqualization(Mat& src, ImageEnhanceParam& IEP);
Mat Intergral_2(const Mat& image);
int getVarianceMeanFast(const Mat &_image, Mat &_mean, Mat &_std, int d);
bool getVarianceMean(Mat& src, Mat& meanDst, Mat& varianceDst, int winSize);
bool adaptContrastEnhancement(Mat& src, Mat& dst, int winSize, double maxCG);
int ACE(const Mat &_image, Mat &_result, int _d, int _Scale, double _MaxCG);
void AutoContrastEnhancement(Mat& src, Mat& dst, ImageEnhanceParam& IEP);
int ImageEnhance(Mat& src, Mat& dst, ImageEnhanceParam& IEP);
ImageEnhance.cpp
#include "ImageEnhance.h"
#define eps 1e-5
void PrintCostTime(const char* str, double& t1, double& t2) {
double t = (t2 - t1) * 1000 / cv::getTickFrequency();
printf("%s ===> %.2f ms\n", str, t);
}
string SplitString(const string& filePath)
{
string c = "\\";
int pos1 = filePath.find_last_of(c);
string imgName(filePath.substr(pos1 + 1));
return imgName;
}
void getFiles(string path, vector<string>& files)
{
intptr_t hFile = 0;
struct _finddata_t fileinfo;
string p;
char* files_format[2] = { "\\*.jpg" ,"\\*.png" };
for (int i = 0; i < sizeof(files_format) / sizeof(char*); i++) {
p.assign(path).append(files_format[i]);
hFile = _findfirst(p.c_str(), &fileinfo);
if (hFile != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
}
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
}
}
Mat PerfectReflectionAlgorithm(Mat& src, const float& ThresholdRatio) {
int row = src.rows;
int col = src.cols;
Mat dst(row, col, CV_8UC3);
int HistRGB[767] = { 0 };
int MaxVal = 0;
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
MaxVal = max(MaxVal, (int)src.at<Vec3b>(i, j)[0]);
MaxVal = max(MaxVal, (int)src.at<Vec3b>(i, j)[1]);
MaxVal = max(MaxVal, (int)src.at<Vec3b>(i, j)[2]);
int sum = src.at<Vec3b>(i, j)[0] + src.at<Vec3b>(i, j)[1] + src.at<Vec3b>(i, j)[2];
HistRGB[sum]++;
}
}
int Threshold = 0;
int sum = 0;
for (int i = 766; i >= 0; i--) {
sum += HistRGB[i];
if (sum > row * col * ThresholdRatio) {
Threshold = i;
break;
}
}
int AvgB = 0;
int AvgG = 0;
int AvgR = 0;
int cnt = 0;
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
int sumP = src.at<Vec3b>(i, j)[0] + src.at<Vec3b>(i, j)[1] + src.at<Vec3b>(i, j)[2];
if (sumP > Threshold) {
AvgB += src.at<Vec3b>(i, j)[0];
AvgG += src.at<Vec3b>(i, j)[1];
AvgR += src.at<Vec3b>(i, j)[2];
cnt++;
}
}
}
AvgB /= cnt;
AvgG /= cnt;
AvgR /= cnt;
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
int Blue = src.at<Vec3b>(i, j)[0] * MaxVal / AvgB;
int Green = src.at<Vec3b>(i, j)[1] * MaxVal / AvgG;
int Red = src.at<Vec3b>(i, j)[2] * MaxVal / AvgR;
if (Red > 255) {
Red = 255;
}
else if (Red < 0) {
Red = 0;
}
if (Green > 255) {
Green = 255;
}
else if (Green < 0) {
Green = 0;
}
if (Blue > 255) {
Blue = 255;
}
else if (Blue < 0) {
Blue = 0;
}
dst.at<Vec3b>(i, j)[0] = Blue;
dst.at<Vec3b>(i, j)[1] = Green;
dst.at<Vec3b>(i, j)[2] = Red;
}
}
return dst;
}
Mat PerfectReflectionAlgorithmFast(Mat& src, const float& ThresholdRatio, const float& BrightnessScale) {
int row = src.rows;
int col = src.cols;
Mat dst(row, col, CV_8UC3);
int HistRGB[767] = { 0 };
int MaxVal = 0;
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < row; i++) {
const uchar* uc_pixel1 = src.ptr<uchar>(i);
for (int j = 0; j < col; j++) {
MaxVal = max(MaxVal, static_cast<int>(uc_pixel1[0]));
MaxVal = max(MaxVal, static_cast<int>(uc_pixel1[1]));
MaxVal = max(MaxVal, static_cast<int>(uc_pixel1[2]));
int sum = uc_pixel1[0] + uc_pixel1[1] + uc_pixel1[2];
HistRGB[sum]++;
uc_pixel1 += 3;
}
}
int Threshold = 0;
int sum2 = 0;
for (int i = 766; i >= 0; i--) {
sum2 += HistRGB[i];
if (sum2 > row * col * ThresholdRatio) {
Threshold = i;
break;
}
}
float AvgB = 0;
float AvgG = 0;
float AvgR = 0;
int pixCount = 0;
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < row; i++) {
const uchar* uc_pixel2 = src.ptr<uchar>(i);
for (int j = 0; j < col; j++) {
int sumP = uc_pixel2[0] + uc_pixel2[1] + uc_pixel2[2];
if (sumP > Threshold) {
AvgB += uc_pixel2[0];
AvgG += uc_pixel2[1];
AvgR += uc_pixel2[2];
pixCount++;
}
uc_pixel2 += 3;
}
}
if (pixCount == 0 || MaxVal == 0) {
dst = src.clone();
return dst;
}
AvgB /= pixCount;
AvgG /= pixCount;
AvgR /= pixCount;
AvgB += eps;
AvgG += eps;
AvgR += eps;
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < row; i++) {
const uchar* uc_pixel3 = src.ptr<uchar>(i);
uchar* dst_pixel = dst.ptr<uchar>(i);
for (int j = 0; j < col; j++) {
float Blue = BrightnessScale * static_cast<float>(uc_pixel3[0]) * MaxVal / AvgB;
float Green = BrightnessScale * static_cast<float>(uc_pixel3[1]) * MaxVal / AvgG;
float Red = BrightnessScale * static_cast<float>(uc_pixel3[2]) * MaxVal / AvgR;
if (Red > 255) { Red = 255; }
else if (Red < 0) { Red = 0; }
if (Green > 255) { Green = 255; }
else if (Green < 0) { Green = 0; }
if (Blue > 255) { Blue = 255; }
else if (Blue < 0) { Blue = 0; }
dst_pixel[0] = static_cast<uchar>(Blue);
dst_pixel[1] = static_cast<uchar>(Green);
dst_pixel[2] = static_cast<uchar>(Red);
uc_pixel3 += 3;
dst_pixel += 3;
}
}
return dst;
}
Mat PerfectReflectionAlgorithmFast2(Mat& src, const float& ThresholdRatio, const float& BrightnessScale)
{
int histRGBSum[255 * 3 + 1] = { 0 };
cv::Mat dst = cv::Mat::zeros(src.size(), src.type());
uchar maxValue[3] = { 0 };
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < src.rows; i++)
{
const uchar *ptrSrc = src.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++)
{
int sum = *(ptrSrc + 3 * j) + *(ptrSrc + 3 * j + 1) + *(ptrSrc + 3 * j + 2);
histRGBSum[sum]++;
maxValue[0] = std::max(maxValue[0], *(ptrSrc + 3 * j));
maxValue[1] = std::max(maxValue[1], *(ptrSrc + 3 * j + 1));
maxValue[2] = std::max(maxValue[2], *(ptrSrc + 3 * j + 2));
}
}
double sum = 0.0;
int thresholdValue = 0;
for (int i = 765; i >= 0; i--)
{
sum += histRGBSum[i];
if (sum > src.rows * src.cols * ThresholdRatio)
{
thresholdValue = i;
break;
}
}
double avgB = 0.0;
double avgG = 0.0;
double avgR = 0.0;
int pixCount = 0;
for (int i = 0; i < src.rows; i++)
{
const uchar *ptrSrc = src.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++)
{
int sum = *(ptrSrc + 3 * j) + *(ptrSrc + 3 * j + 1) + *(ptrSrc + 3 * j + 2);
if (sum > thresholdValue)
{
avgB += *(ptrSrc + 3 * j);
avgG += *(ptrSrc + 3 * j + 1);
avgR += *(ptrSrc + 3 * j + 2);
pixCount++;
}
}
}
if (pixCount == 0 || maxValue[0] == 0 || maxValue[1] == 0 || maxValue[2] == 0) {
dst = src.clone();
return dst;
}
avgB /= pixCount;
avgG /= pixCount;
avgR /= pixCount;
avgB += eps;
avgG += eps;
avgR += eps;
#pragma omp parallel for num_threads(Openmp_Threads1)
for (int i = 0; i < src.rows; i++)
{
const uchar *ptrSrc = src.ptr<uchar>(i);
uchar *ptrDst = dst.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++)
{
double blue = BrightnessScale * (double)*(ptrSrc + 3 * j) / avgB * maxValue[0];
double green = BrightnessScale * (double)*(ptrSrc + 3 * j + 1) / avgG * maxValue[1];
double red = BrightnessScale * (double)*(ptrSrc + 3 * j + 2) / avgR * maxValue[2];
blue = std::min(std::max((double)0, blue), (double)255);
green = std::min(std::max((double)0, green), (double)255);
red = std::min(std::max((double)0, red), (double)255);
*(ptrDst + 3 * j) = (uchar)blue;
*(ptrDst + 3 * j + 1) = (uchar)green;
*(ptrDst + 3 * j + 2) = (uchar)red;
}
}
return dst;
}
Mat AutoColorEqualization(Mat& src, ImageEnhanceParam& IEP)
{
const float ThresholdRatio = IEP.ThresholdRatio;
const float BrightnessScale = IEP.BrightnessScale;
Mat dst = PerfectReflectionAlgorithmFast(src, ThresholdRatio, BrightnessScale);
return dst;
}
void AutoContrastEnhancement(Mat& src, Mat& dst, ImageEnhanceParam& IEP)
{
int winSize = IEP.winSize;
double maxCG = IEP.maxCG;
if (!adaptContrastEnhancement(src, dst, winSize, maxCG))
{
dst = src.clone();
}
}
bool getVarianceMean(Mat &src, Mat &meansDst, Mat &varianceDst, int winSize)
{
if (winSize % 2 == 0)
{
cout << "winSize shoule be an odd number" << endl;
return false;
}
double t1 = cv::getTickCount();
Mat copyBorder_yChannels;
int copyBorderSize = (winSize - 1) / 2;
copyMakeBorder(src, copyBorder_yChannels, copyBorderSize, copyBorderSize, copyBorderSize, copyBorderSize, BORDER_REFLECT);
int for_row = copyBorder_yChannels.rows - copyBorderSize;
int for_col = copyBorder_yChannels.cols - copyBorderSize;
double t2 = cv::getTickCount();
PrintCostTime("getVarianceMean:", t1, t2);
double t3 = cv::getTickCount();
double t4 = cv::getTickCount();
PrintCostTime("getVarianceMean22222:", t3, t4);
for (int i = copyBorderSize; i < for_row; i++)
{
float* variance_pixel = varianceDst.ptr<float>(i - copyBorderSize);
float* mean_pixel = meansDst.ptr<float>(i - copyBorderSize);
for (int j = copyBorderSize; j < for_col; j++)
{
Mat temp = copyBorder_yChannels(Rect(j - copyBorderSize, i - copyBorderSize, winSize, winSize));
Scalar mean;
Scalar dev;
meanStdDev(temp, mean, dev);
*variance_pixel = dev.val[0];
*mean_pixel = mean.val[0];
variance_pixel++;
mean_pixel++;
}
}
return true;
}
int getVarianceMeanFast(const Mat &_image, Mat &_mean, Mat &_std, int d)
{
if (_image.channels() == 1)
{
_mean.create(_image.size(), CV_64FC1);
_std.create(_image.size(), CV_64FC1);
}
else if (_image.channels() == 3)
{
_mean.create(_image.size(), CV_64FC3);
_std.create(_image.size(), CV_64FC3);
}
Mat image_big;
copyMakeBorder(_image, image_big, d + 1, d + 1, d + 1, d + 1, BORDER_REFLECT_101);
image_big.convertTo(image_big, _mean.type());
Mat image_big_2 = image_big.mul(image_big);
Mat Intergral_image1 = Intergral_2(image_big);
Mat Intergral_image2 = Intergral_2(image_big_2);
int N = (2 * d + 1)*(2 * d + 1);
int c = _image.channels();
int nr = _image.rows;
int nc = _image.cols*c;
#pragma omp parallel for num_threads(Openmp_Threads2)
for (int i = 0; i < nr; i++)
{
double* outData1 = _mean.ptr<double>(i);
double* outData2 = _std.ptr<double>(i);
double* inDataUp1 = Intergral_image1.ptr<double>(i);
double* inDataUp2 = Intergral_image2.ptr<double>(i);
double* inDataDown1 = Intergral_image1.ptr<double>(i + 2 * d + 1);
double* inDataDown2 = Intergral_image2.ptr<double>(i + 2 * d + 1);
for (int j = 0; j < nc; j++)
{
double sumi1 = inDataDown1[j + (2 * d + 1)*c] + inDataUp1[j] - inDataUp1[j + (2 * d + 1)*c] - inDataDown1[j];
double sumi2 = inDataDown2[j + (2 * d + 1)*c] + inDataUp2[j] - inDataUp2[j + (2 * d + 1)*c] - inDataDown2[j];
outData1[j] = sumi1 / N;
outData2[j] = (sumi2 - sumi1*outData1[j]) / N;
}
}
cv::sqrt(_std, _std);
return 0;
}
Mat Intergral_2(const Mat& image)
{
Mat result;
Mat image_2;
if (image.channels() == 1)
{
image.convertTo(image_2, CV_64FC1);
result.create(image.size(), CV_64FC1);
}
else if (image.channels() == 3)
{
image.convertTo(image_2, CV_64FC3);
result.create(image.size(), CV_64FC3);
}
int c = image_2.channels();
int nr = image_2.rows;
int nc = image_2.cols*c;
for (int i = 0; i < nr; i++)
{
const double* inData = image_2.ptr<double>(i);
double* outData = result.ptr<double>(i);
if (i != 0)
{
const double* outData_up = result.ptr<double>(i - 1);
for (int j = 0; j < nc; j++)
{
if (j >= c)
{
outData[j] = inData[j] + outData_up[j] + outData[j - c] - outData_up[j - c];
}
else
{
outData[j] = inData[j] + outData_up[j];
}
}
}
else
{
for (int j = 0; j < nc; j++)
{
if (j >= c)
{
outData[j] = inData[j] + outData[j - c];
}
else
{
outData[j] = inData[j];
}
}
}
}
return result;
}
bool adaptContrastEnhancement(Mat &src, Mat &dst, int winSize, double maxCg)
{
if (!src.data)
{
return false;
}
Mat ycc;
cvtColor(src, ycc, COLOR_RGB2YCrCb);
vector<Mat> channels(3);
split(ycc, channels);
Mat localMeansMatrix(src.rows, src.cols, CV_64FC1);
Mat localVarianceMatrix(src.rows, src.cols, CV_64FC1);
getVarianceMeanFast(channels[0], localMeansMatrix, localVarianceMatrix, winSize);
Mat temp = channels[0].clone();
Scalar mean;
Scalar dev;
meanStdDev(temp, mean, dev);
float meansGlobal = mean.val[0];
Mat enhanceMatrix(src.rows, src.cols, CV_8UC1);
#pragma omp parallel for num_threads(Openmp_Threads2)
for (int i = 0; i < src.rows; i++)
{
double* localVarianceMatrix_pixel = localVarianceMatrix.ptr<double>(i);
double* localMeansMatrix_pixel = localMeansMatrix.ptr<double>(i);
uchar* temp_pixel = temp.ptr<uchar>(i);
uchar* enhanceMatrix_pixel = enhanceMatrix.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++)
{
if (localVarianceMatrix_pixel[j] >= 0.01)
{
double cg = 0.2 * meansGlobal / (localVarianceMatrix_pixel[j] + eps);
double cgs = cg > maxCg ? maxCg : cg;
cgs = cgs < 1 ? 1 : cgs;
int e = localMeansMatrix_pixel[j] + cgs* (temp_pixel[j] - localMeansMatrix_pixel[j]);
if (e > 255) { e = 255; }
else if (e < 0) { e = 0; }
enhanceMatrix_pixel[j] = static_cast<uchar>(e);
}
else
{
enhanceMatrix_pixel[j] = temp_pixel[j];
}
}
}
channels[0] = enhanceMatrix;
merge(channels, ycc);
cvtColor(ycc, dst, COLOR_YCrCb2RGB);
return true;
}
int ACE(const Mat &_image, Mat &_result, int _d, int _Scale, double _MaxCG)
{
Mat ycc;
cvtColor(_image, ycc, COLOR_RGB2YCrCb);
vector<Mat> channels(3);
split(ycc, channels);
Mat localmean, localstd;
getVarianceMeanFast(channels[0], localmean, localstd, _d);
if (channels[0].channels() == 1)
{
_result.create(_image.size(), CV_64FC1);
}
else if (channels[0].channels() == 3)
{
_result.create(_image.size(), CV_64FC3);
}
Mat mean_m, std_m;
meanStdDev(channels[0], mean_m, std_m);
double std[3];
std[0] = std_m.at<double>(0, 0);
std[1] = std_m.at<double>(1, 0);
std[2] = std_m.at<double>(2, 0);
int c = channels[0].channels();
int nr = _image.rows;
int nc = _image.cols*c;
double CG;
#pragma omp parallel for num_threads(Openmp_Threads2)
for (int i = 0; i < nr; i++)
{
double* meanData = localmean.ptr<double>(i);
double* stdData = localstd.ptr<double>(i);
const uchar* imageData = channels[0].ptr<uchar>(i);
double* outData = _result.ptr<double>(i);
for (int j = 0; j < nc; j++)
{
CG = std[j % c] / stdData[j];
if (CG > _MaxCG)
CG = _MaxCG;
outData[j] = meanData[j] + _Scale*CG*(int(imageData[j]) - meanData[j]);
}
}
_result.convertTo(_result, CV_8UC1);
channels[0] = _result;
merge(channels, ycc);
cvtColor(ycc, _result, COLOR_YCrCb2RGB);
return 0;
}
int ImageEnhance(Mat& src, Mat& dst, ImageEnhanceParam& IEP)
{
if (src.empty()) {
return -1;
}
Mat dst1 = AutoColorEqualization(src, IEP);
AutoContrastEnhancement(dst1, dst, IEP);
cv::imwrite("result08.jpg", dst);
cv::imshow("origin", src);
cv::imshow("dst1", dst1);
cv::imshow("result", dst);
cv::waitKey(0);
return 0;
}
main.cpp
#include "ImageEnhance.h"
int main()
{
Mat src = imread("E:\\ImageProcess\\ImageEnhance\\secha\\5_u_1.jpg");
Mat dst;
ImageEnhanceParam IEP;
IEP.doImageEnhance = 1;
IEP.ThresholdRatio = 0.1;
IEP.BrightnessScale = 0.96;
IEP.winSize = 1;
IEP.maxCG = 3.5;
if (IEP.doImageEnhance == 0) {
dst = src.clone();
}
else {
int ieFlag = ImageEnhance(src, dst, IEP);
if (ieFlag == -1) {
cout << "load image error..." << endl;
}
}
}