1. 程式人生 > >【花書DL-book】區域性對比度歸一化 LCN

【花書DL-book】區域性對比度歸一化 LCN

實現方法來自於花書(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.csdn.net/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;
}