C++之OpenCV图像高光调整具体流程
作者:翟天保Steven
实现原理
PS中的高光命令是一种校正由于太接近相机闪光灯而有些发白的焦点的方法。在用其他方式采光的图像中,这种调整也可用于使高光区域变暗。要实现图像的高光调整,首先要识别出高光区;再通过对高光区的色彩进行一定变换,使其达到提光或者暗化效果;最后也是最重要的,就是对高光区和非高光区的边缘作平滑处理。
下方介绍具体流程。
具体流程
1)读取识别图像的原图,并转灰度图,再归一化。
// 生成灰度图 cv::Mat gray = cv::Mat::zeros(input.size(), CV_32FC1); cv::Mat f = input.clone(); f.convertTo(f, CV_32FC3); vector<cv::Mat> pics; split(f, pics); gray = 0.299f*pics[2] + 0.587*pics[2] + 0.114*pics[0]; gray = gray / 255.f;
2)确定高光区。因为我们要识别高光,所以thresh通过gray*gray,得到的图像中原本亮的地方则为亮,取平均值当阈值,进行二值化得到掩膜mask。
// 确定高光区 cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type()); thresh = gray.mul(gray); // 取平均值作为阈值 Scalar t = mean(thresh); cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1); mask.setTo(255, thresh >= t[0]);
3)对掩膜区边缘进行平滑过渡。假设light为50,那么midrate的掩膜区值为1.5,黑色区为1,过渡区为1~1.5;bright的掩膜区为0.125,黑色区为0,过渡区为0~0.125。
// 参数设置 int max = 4; float bright = light / 100.0f / max; float mid = 1.0f + max * bright; // 边缘平滑过渡 cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1); cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1); for (int i = 0; i < input.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 < input.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; } } }
4)根据midrate和brightrate,进行高光区提亮。对非高光区而言,midrate都为1,brightrate都为0,即没有变化;对高光区而言,midrate都为1.5,brightrate都为0.125,所以色彩数值均有所增加,带来了提亮效果;对边缘地区,midrate和brightrate起到了很好的过渡作用。
注意:temp要进行数值限制,假设temp大于1,则进行uchar处理后数值会因为类型原因产生突变,那么图像也就变成了五颜六色。
// 高光提亮,获取结果图 cv::Mat result = cv::Mat::zeros(input.size(), input.type()); for (int i = 0; i < input.rows; ++i) { float *mi = midrate.ptr<float>(i); float *br = brightrate.ptr<float>(i); uchar *in = input.ptr<uchar>(i); uchar *r = result.ptr<uchar>(i); for (int j = 0; j < input.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; } } }
C++测试代码
#include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; cv::Mat HighLight(cv::Mat input, int light); int main() { cv::Mat src = imread("test3.jpg"); int light1 = 50; int light2 = -50; cv::Mat result1 = HighLight(src, light1); cv::Mat result2 = HighLight(src, light2); imshow("original", src); imshow("result1", result1); imshow("result2", result2); waitKey(0); return 0; } // 图像高光选取 cv::Mat HighLight(cv::Mat input, int light) { // 生成灰度图 cv::Mat gray = cv::Mat::zeros(input.size(), CV_32FC1); cv::Mat f = input.clone(); f.convertTo(f, CV_32FC3); 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); // 取平均值作为阈值 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 = light / 100.0f / max; float mid = 1.0f + max * bright; // 边缘平滑过渡 cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1); cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1); for (int i = 0; i < input.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 < input.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(input.size(), input.type()); for (int i = 0; i < input.rows; ++i) { float *mi = midrate.ptr<float>(i); float *br = brightrate.ptr<float>(i); uchar *in = input.ptr<uchar>(i); uchar *r = result.ptr<uchar>(i); for (int j = 0; j < input.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 原图
从测试效果中可以看出,高光区随light变化而产生亮度变化,当light为正值时,高光区有明显提亮效果;反之,则变更暗。
如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~
到此这篇关于OpenCV图像高光调整的文章就介绍到这了,更多相关OpenCV图像高光调整内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!