文章目录
一、多功能色彩调整
1.1、亮度
1.2、对比度
1.3、饱和度
1.4、高光
1.5、暖色调
1.6、阴影
1.7、漫画效果
1.8、白平衡-灰度世界
1.9、白平衡-完美反射
1.10、浮雕
1.11、羽化
1.12、锐化
1.13、颗粒感
二、实战案例
2.1、主函数
2.2、函数定义
更多详细信息请看:OpenCV专栏:翟天保Steven
一、多功能色彩调整
1.1、亮度
//--------------------------------------------------------------------------------
// 亮度与对比度
cv::Mat Brightness(cv::Mat src, float brightness, int contrast)
{
cv::Mat dst;
dst = cv::Mat::zeros(src.size(), src.type()); //新建空白模板:大小/类型与原图像一致,像素值全0。
int height = src.rows; //获取图像高度
int width = src.cols; //获取图像宽度
float alpha = brightness; //亮度(0~1为暗,1~正无穷为亮)
float beta = contrast; //对比度
cv::Mat template1;
src.convertTo(template1, CV_32F); //将CV_8UC1转换为CV32F1数据格式。
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
if (src.channels() == 3)
{
float b = template1.at<cv::Vec3f>(row, col)[0]; //获取通道的像素值(blue)
float g = template1.at<cv::Vec3f>(row, col)[1]; //获取通道的像素值(green)
float r = template1.at<cv::Vec3f>(row, col)[2]; //获取通道的像素值(red)
//cv::saturate_cast<uchar>(vaule):需注意,value值范围必须在0~255之间。
dst.at<cv::Vec3b>(row, col)[0] = cv::saturate_cast<uchar>(b * alpha + beta); //修改通道的像素值(blue)
dst.at<cv::Vec3b>(row, col)[1] = cv::saturate_cast<uchar>(g * alpha + beta); //修改通道的像素值(green)
dst.at<cv::Vec3b>(row, col)[2] = cv::saturate_cast<uchar>(r * alpha + beta); //修改通道的像素值(red)
}
else if (src.channels() == 1)
{
float v = src.at<uchar>(row, col); //获取通道的像素值(单)
dst.at<uchar>(row, col) = cv::saturate_cast<uchar>(v * alpha + beta); //修改通道的像素值(单)
//saturate_cast<uchar>:主要是为了防止颜色溢出操作。如果color<0,则color等于0;如果color>255,则color等于255。
}
}
}
return dst;
}
1.2、对比度
//--------------------------------------------------------------------------------
// 亮度与对比度
cv::Mat Brightness(cv::Mat src, float brightness, int contrast)
{
cv::Mat dst;
dst = cv::Mat::zeros(src.size(), src.type()); //新建空白模板:大小/类型与原图像一致,像素值全0。
int height = src.rows; //获取图像高度
int width = src.cols; //获取图像宽度
float alpha = brightness; //亮度(0~1为暗,1~正无穷为亮)
float beta = contrast; //对比度
cv::Mat template1;
src.convertTo(template1, CV_32F); //将CV_8UC1转换为CV32F1数据格式。
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
if (src.channels() == 3)
{
float b = template1.at<cv::Vec3f>(row, col)[0]; //获取通道的像素值(blue)
float g = template1.at<cv::Vec3f>(row, col)[1]; //获取通道的像素值(green)
float r = template1.at<cv::Vec3f>(row, col)[2]; //获取通道的像素值(red)
//cv::saturate_cast<uchar>(vaule):需注意,value值范围必须在0~255之间。
dst.at<cv::Vec3b>(row, col)[0] = cv::saturate_cast<uchar>(b * alpha + beta); //修改通道的像素值(blue)
dst.at<cv::Vec3b>(row, col)[1] = cv::saturate_cast<uchar>(g * alpha + beta); //修改通道的像素值(green)
dst.at<cv::Vec3b>(row, col)[2] = cv::saturate_cast<uchar>(r * alpha + beta); //修改通道的像素值(red)
}
else if (src.channels() == 1)
{
float v = src.at<uchar>(row, col); //获取通道的像素值(单)
dst.at<uchar>(row, col) = cv::saturate_cast<uchar>(v * alpha + beta); //修改通道的像素值(单)
//saturate_cast<uchar>:主要是为了防止颜色溢出操作。如果color<0,则color等于0;如果color>255,则color等于255。
}
}
}
return dst;
}
1.3、饱和度
//--------------------------------------------------------------------------------
// 饱和度
cv::Mat Saturation(cv::Mat src, int saturation)
{
float Increment = saturation * 1.0f / 100;
cv::Mat temp = src.clone();
int row = src.rows;
int col = src.cols;
for (int i = 0; i < row; ++i)
{
uchar *t = temp.ptr<uchar>(i);
uchar *s = src.ptr<uchar>(i);
for (int j = 0; j < col; ++j)
{
uchar b = s[3 * j];
uchar g = s[3 * j + 1];
uchar r = s[3 * j + 2];
float max = max3(r, g, b);
float min = min3(r, g, b);
float delta, value;
float L, S, alpha;
delta = (max - min) / 255;
if (delta == 0)
continue;
value = (max + min) / 255;
L = value / 2;
if (L < 0.5)
S = delta / value;
else
S = delta / (2 - value);
if (Increment >= 0)
{
if ((Increment + S) >= 1)
alpha = S;
else
alpha = 1 - Increment;
alpha = 1 / alpha - 1;
t[3 * j + 2] =static_cast<uchar>( r + (r - L * 255) * alpha);
t[3 * j + 1] = static_cast<uchar>(g + (g - L * 255) * alpha);
t[3 * j] = static_cast<uchar>(b + (b - L * 255) * alpha);
}
else
{
alpha = Increment;
t[3 * j + 2] = static_cast<uchar>(L * 255 + (r - L * 255) * (1 + alpha));
t[3 * j + 1] = static_cast<uchar>(L * 255 + (g - L * 255) * (1 + alpha));
t[3 * j] = static_cast<uchar>(L * 255 + (b - L * 255) * (1 + alpha));
}
}
}
return temp;
}
1.4、高光
//--------------------------------------------------------------------------------
// 高光
cv::Mat HighLight(cv::Mat src, int highlight)
{
// 生成灰度图
cv::Mat gray = cv::Mat::zeros(src.size(), CV_32FC1);
cv::Mat f = src.clone();
f.convertTo(f, CV_32FC3);
std::vector<cv::Mat> pics;
split(f, pics);
gray = 0.299f*pics[2] + 0.587*pics[2] + 0.114*pics[0];
gray = gray / 255.f;
// 确定高光区
cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type());
thresh = gray.mul(gray);
// 取平均值作为阈值
cv::Scalar t = mean(thresh);
cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
mask.setTo(255, thresh >= t[0]);
// 参数设置
int max = 4;
float bright = highlight / 100.0f / max;
float mid = 1.0f + max * bright;
// 边缘平滑过渡
cv::Mat midrate = cv::Mat::zeros(src.size(), CV_32FC1);
cv::Mat brightrate = cv::Mat::zeros(src.size(), CV_32FC1);
for (int i = 0; i < src.rows; ++i)
{
uchar *m = mask.ptr<uchar>(i);
float *th = thresh.ptr<float>(i);
float *mi = midrate.ptr<float>(i);
float *br = brightrate.ptr<float>(i);
for (int j = 0; j < src.cols; ++j)
{
if (m[j] == 255)
{
mi[j] = mid;
br[j] = bright;
}
else {
mi[j] = (mid - 1.0f) / t[0] * th[j] + 1.0f;
br[j] = (1.0f / t[0] * th[j])*bright;
}
}
}
// 高光提亮,获取结果图
cv::Mat result = cv::Mat::zeros(src.size(), src.type());
for (int i = 0; i < src.rows; ++i)
{
float *mi = midrate.ptr<float>(i);
float *br = brightrate.ptr<float>(i);
uchar *in = src.ptr<uchar>(i);
uchar *r = result.ptr<uchar>(i);
for (int j = 0; j < src.cols; ++j)
{
for (int k = 0; k < 3; ++k)
{
float temp = pow(float(in[3 * j + k]) / 255.f, 1.0f / mi[j])*(1.0 / (1 - br[j]));
if (temp > 1.0f)
temp = 1.0f;
if (temp < 0.0f)
temp = 0.0f;
&