Opencv C++图像处理:亮度+对比度+饱和度+高光+暖色调+阴影+漫画效果+白平衡+浮雕+羽化+锐化+颗粒感

本文主要是介绍Opencv C++图像处理:亮度+对比度+饱和度+高光+暖色调+阴影+漫画效果+白平衡+浮雕+羽化+锐化+颗粒感,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、多功能色彩调整
    • 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;elseS = delta / (2 - value);if (Increment >= 0){if ((Increment + S) >= 1)alpha = S;elsealpha = 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;uchar utemp = uchar(255*temp);r[3 * j + k] = utemp;}}}return result;
}

1.5、暖色调

在这里插入图片描述

//--------------------------------------------------------------------------------
// 暖色调
cv::Mat ColorTemperature(cv::Mat src, int warm)
{cv::Mat result = src.clone();int row = src.rows;int col = src.cols;int level = warm/2;for (int i = 0; i < row; ++i){uchar* a = src.ptr<uchar>(i);uchar* r = result.ptr<uchar>(i);for (int j = 0; j < col; ++j){int R,G,B;// R通道R = a[j * 3 + 2];R = R + level;if (R > 255) {r[j * 3 + 2] = 255;}else if (R < 0) {r[j * 3 + 2] = 0;}else {r[j * 3 + 2] = R;}// G通道G = a[j * 3 + 1];G = G + level;if (G > 255) {r[j * 3 + 1] = 255;}else if (G < 0) {r[j * 3 + 1] = 0;}else {r[j * 3 + 1] = G;}// B通道B = a[j * 3];B = B - level;if (B > 255) {r[j * 3] = 255;}else if (B < 0) {r[j * 3] = 0;}else {r[j * 3] = B;}}}return result;
}

1.6、阴影

在这里插入图片描述

//--------------------------------------------------------------------------------
// 阴影
cv::Mat Shadow(cv::Mat src, int shadow)
{// 生成灰度图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 = (1.0f - gray).mul(1.0f - 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 = shadow / 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;uchar utemp = uchar(255*temp);r[3 * j + k] = utemp;}}}return result;
}

1.7、漫画效果

在这里插入图片描述

//--------------------------------------------------------------------------------
// 漫画效果
cv::Mat Cartoon(cv::Mat src, double clevel, int d, double sigma, int size)
{// 中值滤波cv::Mat m;cv::medianBlur(src, m, 7);// 提取轮廓cv::Mat c;clevel = cv::max(40., cv::min(80., clevel));cv::Canny(m, c, clevel, clevel *3);// 轮廓膨胀加深cv::Mat k = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2, 2));cv::dilate(c, c, k);// 反转c = c / 255;c = 1 - c;// 类型转化cv::Mat cf;c.convertTo(cf, CV_32FC1);// 均值滤波cv::blur(cf, cf, cv::Size(5, 5));// 双边滤波cv::Mat srcb;d = cv::max(0, cv::min(10, d));sigma = cv::max(10., cv::min(250., sigma));cv::bilateralFilter(src, srcb, d, sigma, sigma);size = cv::max(10, cv::min(25, size));cv::Mat temp = srcb / size;temp = temp * size;// 通道合并cv::Mat c3;cv::Mat cannyChannels[] = { cf, cf, cf };cv::merge(cannyChannels, 3, c3);// 类型转化cv::Mat tempf;temp.convertTo(tempf, CV_32FC3);// 图像相乘cv::multiply(tempf, c3, tempf);// 类型转化tempf.convertTo(temp, CV_8UC3);return temp;
}

1.8、白平衡-灰度世界

在这里插入图片描述

//--------------------------------------------------------------------------------
// 白平衡-灰度世界
cv::Mat WhiteBalcane_Gray(cv::Mat src)
{cv::Mat result = src.clone();if (src.channels() != 3){std::cout << "The number of image channels is not 3." << std::endl;return result;}// 通道分离std::vector<cv::Mat> Channel;cv::split(src, Channel);// 计算通道灰度值均值double Bm = cv::mean(Channel[0])[0];double Gm = cv::mean(Channel[1])[0];double Rm = cv::mean(Channel[2])[0];double Km = (Bm + Gm + Rm) / 3;// 通道灰度值调整Channel[0] *= Km / Bm;Channel[1] *= Km / Gm;Channel[2] *= Km / Rm;// 合并通道cv::merge(Channel, result);return result;
}

1.9、白平衡-完美反射

在这里插入图片描述

//--------------------------------------------------------------------------------
// 白平衡-完美反射
cv::Mat WhiteBalcane_PRA(cv::Mat src)
{cv::Mat result = src.clone();if (src.channels() != 3){std::cout << "The number of image channels is not 3." << std::endl;return result;}// 通道分离std::vector<cv::Mat> Channel;cv::split(src, Channel);// 定义参数int row = src.rows;int col = src.cols;int RGBSum[766] = { 0 };uchar maxR, maxG, maxB;// 计算单通道最大值for (int i = 0; i < row; ++i){uchar *b = Channel[0].ptr<uchar>(i);uchar *g = Channel[1].ptr<uchar>(i);uchar *r = Channel[2].ptr<uchar>(i);for (int j = 0; j < col; ++j){int sum = b[j] + g[j] + r[j];RGBSum[sum]++;maxB = cv::max(maxB, b[j]);maxG = cv::max(maxG, g[j]);maxR = cv::max(maxR, r[j]);}}// 计算最亮区间下限Tint T = 0;int num = 0;int K = static_cast<int>(row * col * 0.1);for (int i = 765; i >= 0; --i){num += RGBSum[i];if (num > K){T = i;break;}}// 计算单通道亮区平均值double Bm = 0.0, Gm = 0.0, Rm = 0.0;int count = 0;for (int i = 0; i < row; ++i){uchar *b = Channel[0].ptr<uchar>(i);uchar *g = Channel[1].ptr<uchar>(i);uchar *r = Channel[2].ptr<uchar>(i);for (int j = 0; j < col; ++j){int sum = b[j] + g[j] + r[j];if (sum > T){Bm += b[j];Gm += g[j];Rm += r[j];count++;}}}Bm /= count;Gm /= count;Rm /= count;// 通道调整Channel[0] *= maxB / Bm;Channel[1] *= maxG / Gm;Channel[2] *= maxR / Rm;// 合并通道cv::merge(Channel, result);return result;
}

1.10、浮雕

在这里插入图片描述

//--------------------------------------------------------------------------------
// 浮雕
cv::Mat Relief(cv::Mat src)
{CV_Assert(src.channels() == 3);int row = src.rows;int col = src.cols;cv::Mat temp = src.clone();for (int i = 1; i < row-1; ++i){uchar *s1 = src.ptr<uchar>(i - 1);uchar *s2 = src.ptr<uchar>(i + 1);uchar *t = temp.ptr<uchar>(i);for (int j = 1; j < col-1; ++j){for (int k = 0; k < 3; ++k){int RGB = s1[3 * (j - 1) + k] - s2[3 * (j + 1) + k] + 128;if (RGB < 0)RGB = 0;if (RGB > 255)RGB = 255;t[3*j+k] =(uchar)RGB;}}}return temp;
}

1.11、羽化

在这里插入图片描述

//--------------------------------------------------------------------------------
// 羽化
cv::Mat Eclosion(cv::Mat src, cv::Point center, float level)
{if (level > 0.9)level = 0.9f;float diff = (1-level) * (src.rows / 2 * src.rows / 2 + src.cols / 2 * src.cols / 2);cv::Mat result = src.clone();for (int i = 0; i < result.rows; ++i){for (int j = 0; j < result.cols; ++j){float dx = float(center.x - j);float dy = float(center.y - i);float ra = dx * dx + dy * dy;float m = ((ra-diff) / diff * 255)>0? ((ra - diff) / diff * 255):0;int b = result.at<cv::Vec3b>(i, j)[0];int g = result.at<cv::Vec3b>(i, j)[1];int r = result.at<cv::Vec3b>(i, j)[2];b = (int)(b+ m);g = (int)(g + m);r = (int)(r + m);result.at<cv::Vec3b>(i, j)[0] = (b > 255 ? 255 : (b < 0 ? 0 : b));result.at<cv::Vec3b>(i, j)[1] = (g > 255 ? 255 : (g < 0 ? 0 : g));result.at<cv::Vec3b>(i, j)[2] = (r > 255 ? 255 : (r < 0 ? 0 : r));}}return result;
}

1.12、锐化

在这里插入图片描述

//--------------------------------------------------------------------------------
// 锐化
cv::Mat Sharpen(cv::Mat input, int percent, int type)
{cv::Mat result;cv::Mat s = input.clone();cv::Mat kernel;switch (type){case 0:kernel = (cv::Mat_<int>(3, 3) <<0, -1, 0,-1, 4, -1,0, -1, 0);case 1:kernel = (cv::Mat_<int>(3, 3) <<-1, -1, -1,-1, 8, -1,-1, -1, -1);default:kernel = (cv::Mat_<int>(3, 3) <<0, -1, 0,-1, 4, -1,0, -1, 0);}cv::filter2D(s, s, s.depth(), kernel);result = input + s * 0.01 * percent;return result;
}

1.13、颗粒感

在这里插入图片描述

//--------------------------------------------------------------------------------
// 颗粒感
cv::Mat Grainy(cv::Mat src, int level)
{int row = src.rows;int col = src.cols;if (level > 100)level = 100;if (level < 0)level = 0;cv::Mat result = src.clone();for (int i = 0; i < row; ++i){uchar *t = result.ptr<uchar>(i);for (int j = 0; j < col; ++j){for (int k = 0; k < 3; ++k){int temp = t[3 * j + k];temp += ((rand() % (2 * level)) - level);if (temp < 0)temp = 0;if (temp > 255)temp = 255;t[3 * j + k] = temp;}}}return result;
}

二、实战案例

2.1、主函数

在这里插入图片描述

#include<opencv2\opencv.hpp>
//using namespace cv;
//using namespace std;#define max2(a,b) (a>b?a:b)
#define max3(a,b,c) (a>b?max2(a,c):max2(b,c))
#define min2(a,b) (a<b?a:b)
#define min3(a,b,c) (a<b?min2(a,c):min2(b,c))//函数申明
cv::Mat Brightness(cv::Mat src, float brightness, int contrast);				//亮度+对比度。
cv::Mat Saturation(cv::Mat src, int saturation);								//饱和度
cv::Mat HighLight(cv::Mat src, int highlight);									//高光
cv::Mat ColorTemperature(cv::Mat src, int warm);								//暖色调
cv::Mat Shadow(cv::Mat src, int shadow);										//阴影cv::Mat Sharpen(cv::Mat input, int percent, int type);							//图像锐化
cv::Mat Grainy(cv::Mat src, int level);											//颗粒感
cv::Mat Cartoon(cv::Mat src, double clevel, int d, double sigma, int size);		//漫画效果
cv::Mat WhiteBalcane_PRA(cv::Mat src);											//白平衡-完美反射算法(效果偏白)
cv::Mat WhiteBalcane_Gray(cv::Mat src);											//白平衡-灰度世界算法(效果偏蓝)
cv::Mat Relief(cv::Mat src);													//浮雕
cv::Mat Eclosion(cv::Mat src, cv::Point center, float level);					//羽化int main(int argc, char* argv[])
{//(1)读取图像std::string img_path = "test.jpg";cv::Mat src = cv::imread(img_path, 1);//(2)判断图像是否读取成功if (!src.data){std::cout << "can't read image!" << std::endl;return -1;}float brightness_value = 1;		//[0, 10]			亮度。暗~亮:[0, 1] ~ [1, 10]int contrast_value = 0;			//[-100, 100]		对比度。int saturation_value = 0;		//[-100, 100]		饱和度。int highlight_value = 0;		//[-100, 100]		高光。int warm_value = 0;				//[-100, 100]		暖色调。int shadow_value = 0;			//[-100, 100]		阴影。int sharpen_value = 0;			//[-100, 100]		锐化。[-1000000, 1000000]int grainy_value = 0;			//[0, 100]			颗粒感。int eclosion_flag = 0;			//[0, 1]			羽化。int cartoon_flag = 0;			//[0, 1]			漫画效果。clevel阈值40-80,d阈值0-10,sigma阈值10-250,size阈值10-25int reflect_flag = 0;			//[0, 1]			白平衡-完美反射。int world_flag = 0;				//[0, 1]			白平衡-灰度世界。int relief_flag = 0;			//[0, 1]			浮雕。cv::Mat dst = src.clone();if (brightness_value != 1)dst = Brightness(dst, brightness_value, 0);if (contrast_value != 0)dst = Brightness(dst, 1, contrast_value);if (saturation_value != 0)dst = Saturation(dst, saturation_value);if (highlight_value != 0)dst = HighLight(dst, highlight_value);if (warm_value != 0)dst = ColorTemperature(dst, warm_value);if (shadow_value != 0)dst = Shadow(dst, shadow_value);if (sharpen_value != 0)dst = Sharpen(dst, sharpen_value, 0);if (grainy_value != 0)dst = Grainy(dst, grainy_value);if (cartoon_flag != 0)dst = Cartoon(dst, 80, 5, 150, 20);		//clevel阈值40-80,d阈值0-10,sigma阈值10-250,size阈值10-25说if (reflect_flag != 0)dst = WhiteBalcane_PRA(dst);if (world_flag != 0)dst = WhiteBalcane_Gray(dst);if (relief_flag != 0)dst = Relief(dst);if (eclosion_flag != 0)dst = Eclosion(dst, cv::Point(src.cols / 2, src.rows / 2), 0.8f);//(4)显示图像cv::imshow("src", src);cv::imshow("锐化", dst);cv::waitKey(0);		//等待用户任意按键后结束暂停功能return 0;
}

2.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;
}//--------------------------------------------------------------------------------
// 饱和度
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;elseS = delta / (2 - value);if (Increment >= 0){if ((Increment + S) >= 1)alpha = S;elsealpha = 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;
}//--------------------------------------------------------------------------------
// 高光
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;uchar utemp = uchar(255*temp);r[3 * j + k] = utemp;}}}return result;
}//--------------------------------------------------------------------------------
// 暖色调
cv::Mat ColorTemperature(cv::Mat src, int warm)
{cv::Mat result = src.clone();int row = src.rows;int col = src.cols;int level = warm/2;for (int i = 0; i < row; ++i){uchar* a = src.ptr<uchar>(i);uchar* r = result.ptr<uchar>(i);for (int j = 0; j < col; ++j){int R,G,B;// R通道R = a[j * 3 + 2];R = R + level;if (R > 255) {r[j * 3 + 2] = 255;}else if (R < 0) {r[j * 3 + 2] = 0;}else {r[j * 3 + 2] = R;}// G通道G = a[j * 3 + 1];G = G + level;if (G > 255) {r[j * 3 + 1] = 255;}else if (G < 0) {r[j * 3 + 1] = 0;}else {r[j * 3 + 1] = G;}// B通道B = a[j * 3];B = B - level;if (B > 255) {r[j * 3] = 255;}else if (B < 0) {r[j * 3] = 0;}else {r[j * 3] = B;}}}return result;
}//--------------------------------------------------------------------------------
// 阴影
cv::Mat Shadow(cv::Mat src, int shadow)
{// 生成灰度图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 = (1.0f - gray).mul(1.0f - 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 = shadow / 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;uchar utemp = uchar(255*temp);r[3 * j + k] = utemp;}}}return result;
}//--------------------------------------------------------------------------------
// 漫画效果
cv::Mat Cartoon(cv::Mat src, double clevel, int d, double sigma, int size)
{//(1)中值滤波cv::Mat m;cv::medianBlur(src, m, 7);//(2)提取轮廓cv::Mat c;clevel = cv::max(40., cv::min(80., clevel));cv::Canny(m, c, clevel, clevel *3);//(3)轮廓膨胀cv::Mat k = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2, 2));cv::dilate(c, c, k);//(4)图像反转c = c / 255;c = 1 - c;//(5)均值滤波cv::Mat cf;c.convertTo(cf, CV_32FC1);				// 类型转换		cv::blur(cf, cf, cv::Size(5, 5));		//(6)双边滤波cv::Mat srcb;d = cv::max(0, cv::min(10, d));sigma = cv::max(10., cv::min(250., sigma));cv::bilateralFilter(src, srcb, d, sigma, sigma);size = cv::max(10, cv::min(25, size));cv::Mat temp = srcb / size;temp = temp * size;//(7)通道合并cv::Mat c3;cv::Mat cannyChannels[] = { cf, cf, cf };cv::merge(cannyChannels, 3, c3);//(8)图像相乘cv::Mat tempf;temp.convertTo(tempf, CV_32FC3);		// 类型转换cv::multiply(tempf, c3, tempf);			tempf.convertTo(temp, CV_8UC3);			// 类型转换return temp;
}//--------------------------------------------------------------------------------
// 白平衡-灰度世界
cv::Mat WhiteBalcane_Gray(cv::Mat src)
{//(1)3通道处理cv::Mat result = src.clone();if (src.channels() != 3){std::cout << "The number of image channels is not 3." << std::endl;return result;}//(2)通道分离std::vector<cv::Mat> Channel;cv::split(src, Channel);//(3)计算通道灰度值均值double Bm = cv::mean(Channel[0])[0];double Gm = cv::mean(Channel[1])[0];double Rm = cv::mean(Channel[2])[0];double Km = (Bm + Gm + Rm) / 3;//(4)通道灰度值调整Channel[0] *= Km / Bm;Channel[1] *= Km / Gm;Channel[2] *= Km / Rm;//(5)通道合并cv::merge(Channel, result);return result;
}//--------------------------------------------------------------------------------
// 白平衡-完美反射
cv::Mat WhiteBalcane_PRA(cv::Mat src)
{//(1)3通道处理cv::Mat result = src.clone();if (src.channels() != 3){std::cout << "The number of image channels is not 3." << std::endl;return result;}//(2)通道分离std::vector<cv::Mat> Channel;cv::split(src, Channel);//(3)计算单通道最大值int row = src.rows;int col = src.cols;int RGBSum[766] = { 0 };uchar maxR, maxG, maxB;for (int i = 0; i < row; ++i){uchar *b = Channel[0].ptr<uchar>(i);uchar *g = Channel[1].ptr<uchar>(i);uchar *r = Channel[2].ptr<uchar>(i);for (int j = 0; j < col; ++j){int sum = b[j] + g[j] + r[j];RGBSum[sum]++;maxB = cv::max(maxB, b[j]);maxG = cv::max(maxG, g[j]);maxR = cv::max(maxR, r[j]);}}//(4)计算最亮区间下限Tint T = 0;int num = 0;int K = static_cast<int>(row * col * 0.1);for (int i = 765; i >= 0; --i){num += RGBSum[i];if (num > K){T = i;break;}}//(5)计算单通道亮区平均值double Bm = 0.0, Gm = 0.0, Rm = 0.0;int count = 0;for (int i = 0; i < row; ++i){uchar *b = Channel[0].ptr<uchar>(i);uchar *g = Channel[1].ptr<uchar>(i);uchar *r = Channel[2].ptr<uchar>(i);for (int j = 0; j < col; ++j){int sum = b[j] + g[j] + r[j];if (sum > T){Bm += b[j];Gm += g[j];Rm += r[j];count++;}}}Bm /= count;Gm /= count;Rm /= count;//(6)通道调整Channel[0] *= maxB / Bm;Channel[1] *= maxG / Gm;Channel[2] *= maxR / Rm;//(7)通道合并cv::merge(Channel, result);return result;
}//--------------------------------------------------------------------------------
// 浮雕
cv::Mat Relief(cv::Mat src)
{CV_Assert(src.channels() == 3);int row = src.rows;int col = src.cols;cv::Mat temp = src.clone();for (int i = 1; i < row-1; ++i){uchar *s1 = src.ptr<uchar>(i - 1);uchar *s2 = src.ptr<uchar>(i + 1);uchar *t = temp.ptr<uchar>(i);for (int j = 1; j < col-1; ++j){for (int k = 0; k < 3; ++k){int RGB = s1[3 * (j - 1) + k] - s2[3 * (j + 1) + k] + 128;if (RGB < 0)RGB = 0;if (RGB > 255)RGB = 255;t[3*j+k] =(uchar)RGB;}}}return temp;
}//--------------------------------------------------------------------------------
// 羽化
cv::Mat Eclosion(cv::Mat src, cv::Point center, float level)
{if (level > 0.9)level = 0.9f;float diff = (1-level) * (src.rows / 2 * src.rows / 2 + src.cols / 2 * src.cols / 2);cv::Mat result = src.clone();for (int i = 0; i < result.rows; ++i){for (int j = 0; j < result.cols; ++j){float dx = float(center.x - j);float dy = float(center.y - i);float ra = dx * dx + dy * dy;float m = ((ra-diff) / diff * 255)>0? ((ra - diff) / diff * 255):0;int b = result.at<cv::Vec3b>(i, j)[0];int g = result.at<cv::Vec3b>(i, j)[1];int r = result.at<cv::Vec3b>(i, j)[2];b = (int)(b+ m);g = (int)(g + m);r = (int)(r + m);result.at<cv::Vec3b>(i, j)[0] = (b > 255 ? 255 : (b < 0 ? 0 : b));result.at<cv::Vec3b>(i, j)[1] = (g > 255 ? 255 : (g < 0 ? 0 : g));result.at<cv::Vec3b>(i, j)[2] = (r > 255 ? 255 : (r < 0 ? 0 : r));}}return result;
}//--------------------------------------------------------------------------------
// 锐化
cv::Mat Sharpen(cv::Mat input, int percent, int type)
{cv::Mat result;cv::Mat s = input.clone();cv::Mat kernel;switch (type){case 0:kernel = (cv::Mat_<int>(3, 3) <<0, -1, 0,-1, 4, -1,0, -1, 0);case 1:kernel = (cv::Mat_<int>(3, 3) <<-1, -1, -1,-1, 8, -1,-1, -1, -1);default:kernel = (cv::Mat_<int>(3, 3) <<0, -1, 0,-1, 4, -1,0, -1, 0);}cv::filter2D(s, s, s.depth(), kernel);result = input + s * 0.01 * percent;return result;
}//--------------------------------------------------------------------------------
// 颗粒感
cv::Mat Grainy(cv::Mat src, int level)
{int row = src.rows;int col = src.cols;if (level > 100)level = 100;if (level < 0)level = 0;cv::Mat result = src.clone();for (int i = 0; i < row; ++i){uchar *t = result.ptr<uchar>(i);for (int j = 0; j < col; ++j){for (int k = 0; k < 3; ++k){int temp = t[3 * j + k];temp += ((rand() % (2 * level)) - level);if (temp < 0)temp = 0;if (temp > 255)temp = 255;t[3 * j + k] = temp;}}}return result;
}

这篇关于Opencv C++图像处理:亮度+对比度+饱和度+高光+暖色调+阴影+漫画效果+白平衡+浮雕+羽化+锐化+颗粒感的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/356859

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

防近视护眼台灯什么牌子好?五款防近视效果好的护眼台灯推荐

在家里,灯具是属于离不开的家具,每个大大小小的地方都需要的照亮,所以一盏好灯是必不可少的,每个发挥着作用。而护眼台灯就起了一个保护眼睛,预防近视的作用。可以保护我们在学习,阅读的时候提供一个合适的光线环境,保护我们的眼睛。防近视护眼台灯什么牌子好?那我们怎么选择一个优秀的护眼台灯也是很重要,才能起到最大的护眼效果。下面五款防近视效果好的护眼台灯推荐: 一:六个推荐防近视效果好的护眼台灯的

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现