OpenCV-圖像陰影調整

翟天保Steven 2021-09-18 11:59:24 阅读数:627

opencv- 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;
圖1 灰度處理

2)確定陰影區。因為我們要識別陰影,所以thresh通過(1-gray)*(1-gray),得到的圖像中原本暗的地方則為亮,取平均值當閾值,進行二值化得到掩膜mask。

// 確定陰影區
cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type());
thresh = (1.0f - gray).mul(1.0f - gray);
// 取平均值作為閾值
Scalar t = mean(thresh);
cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
mask.setTo(255, thresh >= t[0]);
圖2 陰影掩膜區

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 Shadow(cv::Mat input, int light);
int main()
{
cv::Mat src = imread("test2.jpg");
int light = 50;
cv::Mat result = Shadow(src,light);
imshow("original", src);
imshow("result", result);
waitKey(0);
return 0;
}
// 圖像陰影選取
cv::Mat Shadow(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 = (1.0f - gray).mul(1.0f - 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 原圖
圖2  light為50的效果圖
圖3 light為-50的效果圖

       從測試效果中可以看出,陰影區隨light變化而產生亮度變化,當light為正值時,陰影區有明顯提亮效果;反之,則變更暗。

       如果函數有什麼可以改進完善的地方,非常歡迎大家指出,一同進步何樂而不為呢~

       如果文章幫助到你了,可以點個贊讓我知道,我會很快樂~加油!

版权声明:本文为[翟天保Steven]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/09/20210918115924132W.html