实现方法来自于花书(deep-learning book),P387
// written down after reading deep-learning book chinese edition P389
// all about LCN(local contrast normalization) compared to GCN(general contrast normalization)
// Also compared to adaptive-threshold and sobel method in OpenCV
// Author: Jie Li(Sirius)
// blog : http://blog.youkuaiyun.com/Sirius_0
// github: iwhm
// 25/02/2018
#include <stdlib.h>
#include <opencv2>
#include <opencv2\imgproc>
int main(void)
{
cv::Mat yuan;
cv::Mat src;
// sub_windows size
int sub_win_size = 3;
yuan = cv::imread("./lena.jpg");
if(yuan.empty())
{
assert(0);
return -1;
}
if(yuan.channels() == 3)
{
cv::cvtColor(yuan, src, cv::COLOR_BGR2GRAY);
}
else
{
src = yuan.clone();
}
cv::Mat s_dst(src.rows, src.cols, CV_8UC1, cv::Scalar(0, 0, 0));
cv::Mat g_dst(src.rows, src.cols, CV_8UC1, cv::Scalar(0, 0, 0));
/************
dl-book chinese edition P390
use variance and standard deviation
************/
for(int rr = sub_win_size / 2; rr < src.rows - (sub_win_size / 2 + 1); rr++)
{
for(int cc = sub_win_size / 2; cc < src.cols - (sub_win_size / 2 + 1); cc++)
{
// calculate total value
int total_val = 0;
for(int i = - (win_size / 2); i < (win_size / 2 + 1); i++)
{
for(int k = - (win_size / 2); k < (win_size / 2 + 1); k++)
{
total_val += src.at<unsigned char>(rr + k, cc + i);
}
}
// calculate average value
int avg_val = 0;
avg_val = total_val / (win_size * win_size);
if(avg_val == 0)
{
continue;
}
// calculate variance value
long int var_val = 0;
for(int i = - (win_size / 2); i < (win_size / 2 + 1); i++)
{
for(int k = - (win_size / 2); k < (win_size / 2 + 1); k++)
{
var_val = abs(avg_val - src.at<unsigned char>(rr + k, cc + i) * abs(avg_val - src.at<unsigned char>(rr + k, cc +i));
}
}
// calculate standard deviation
int std_dev = 0;
std_dev = std::sqrt(var_val) / (win_size * win_size);
if(std_dev == 0)
{
continue;
}
s_dst.at<UCHAR>(rr, cc) = (abs(src.at<UCHAR>(rr, cc) - avg_val) / std_dev);
}
}
s_dst = s_dst > 0;
cv::imshow("s_dstimg", s_dst);
/************
dl-book chinese edition P390
use gaussian kernel & average value
************/
// acquire a 2-D gaussian kernel
cv::Mat g_kernel1, g_kernel2;
g_kernel1 = cv::getGaussinaKernel(win_size, 0.0, 6);
cv::transpose(cv::getGaussinaKernel(win_size, 0.0, 6), g_kernel2);
cv::Mat g_2Dkernel;
g_2Dkernel = g_kernel1 * g_kernel2;
for(int rr = sub_win_size / 2; rr < src.rows - (sub_win_size / 2 + 1); rr++)
{
for(int cc = sub_win_size / 2; cc < src.cols - (sub_win_size / 2 + 1); cc++)
{
cv::Mat sub_win(win_size, win_size, CV_8UC1, cv::Scalar(0, 0, 0));
cv::Mat tmp_sub_win(win_size, win_size, CV_8UC1, cv::Scalar(0, 0, 0));
for(int i = - (win_size / 2); i < (win_size / 2 + 1); i++)
{
for(int k = - (win_size / 2); k < (win_size / 2 + 1); k++)
{
tmp_sub_win.at<UCHAR>(k + win_size / 2, i + win_size / 2) = tmp.at<UCHAR>(rr + k, cc + i);
}
}
sub_win = tmp_sub_win.mul(g_2Dkernel);
for(int m = 0; m < win_size; m++)
{
for(int n = 0; n < win_size; n++)
{
g_dst.at<UCHAR>(rr, cc) += sub_win.at<UCHAR>(m, n);
}
}
}
}
g_dst = g_dst > 0;
cv::imshow("g_dstimg", g_dst);
// existed method for comparison
// sobel method
cv::Mat sobimg(src.rows, src.cols, CV_8UC1, cv::Scalar(0, 0, 0));
cv::Sobel(src, sobimg, 0, 1, 1, 3, 1, 0, cv::BORDER_DEFAULT);
sobimg = sobimg > 0;
cv::imshow("sobimg", sobimg);
// adaptive-threshold method
cv::Mat adaimg(src.rows, src.cols, CV_8UC1, cv::Scalar(0, 0, 0));
cv::adaptiveThreshold(src, adaimg, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, 25, 18);
adaimg = adaimg > 0;
cv::imshow("adaimg", adaimg);
cv::waitKey(0);
return 0;
}