영상처리

Auto Threshold (Li) C++

park__ 2024. 12. 10. 16:12

Java -> C++ 로 변경

 

Code

int Li(float* histo, int n_length) {
	// Implements Li's Minimum Cross Entropy thresholding method
	// This implementation is based on the iterative version (Ref. 2) of the algorithm.
	// 1) Li C.H. and Lee C.K. (1993) "Minimum Cross Entropy Thresholding" 
	//    Pattern Recognition, 26(4): 617-625
	// 2) Li C.H. and Tam P.K.S. (1998) "An Iterative Algorithm for Minimum 
	//    Cross Entropy Thresholding"Pattern Recognition Letters, 18(8): 771-776
	// 3) Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding 
	//    Techniques and Quantitative Performance Evaluation" Journal of 
	//    Electronic Imaging, 13(1): 146-165 
	//    http://citeseer.ist.psu.edu/sezgin04survey.html
	// Ported to ImageJ plugin by G.Landini from E Celebi's fourier_0.8 routines
	int threshold;
	int ih;
	int num_pixels;
	int sum_back; /* sum of the background pixels at a given threshold */
	int sum_obj;  /* sum of the object pixels at a given threshold */
	int num_back; /* number of background pixels at a given threshold */
	int num_obj;  /* number of object pixels at a given threshold */
	double old_thresh;
	double new_thresh;
	double mean_back; /* mean of the background pixels at a given threshold */
	double mean_obj;  /* mean of the object pixels at a given threshold */
	double mean;  /* mean gray-level in the image */
	double tolerance; /* threshold tolerance */
	double temp;

	tolerance = 0.5;
	num_pixels = 0;
	for (ih = 0; ih < n_length; ih++)
		num_pixels += histo[ih];

	/* Calculate the mean gray-level */
	mean = 0.0;
	for (ih = 0; ih < n_length; ih++) //0 + 1?
		mean += ih * histo[ih];
	mean /= num_pixels;
	/* Initial estimate */
	new_thresh = mean;

	do 
	{
		old_thresh = new_thresh;
		threshold = (int)(old_thresh + 0.5);	/* range */
		/* Calculate the means of background and object pixels */
		/* Background */
		sum_back = 0;
		num_back = 0;

		for (ih = 0; ih <= threshold; ih++) 
		{
			sum_back += ih * histo[ih];
			num_back += histo[ih];
		}

		mean_back = (num_back == 0 ? 0.0 : (sum_back / (double)num_back));
		/* Object */
		sum_obj = 0;
		num_obj = 0;

		for (ih = threshold + 1; ih < n_length; ih++) 
		{
			sum_obj += ih * histo[ih];
			num_obj += histo[ih];
		}

		mean_obj = (num_obj == 0 ? 0.0 : (sum_obj / (double)num_obj));

		/* Calculate the new threshold: Equation (7) in Ref. 2 */
		//new_thresh = simple_round ( ( mean_back - mean_obj ) / ( Math.log ( mean_back ) - Math.log ( mean_obj ) ) );
		//simple_round ( double x ) {
		// return ( int ) ( IS_NEG ( x ) ? x - .5 : x + .5 );
		//}
		//
		//#define IS_NEG( x ) ( ( x ) < -DBL_EPSILON ) 
		//DBL_EPSILON = 2.220446049250313E-16
		temp = (mean_back - mean_obj) / (log(mean_back) - log(mean_obj));

		if (temp < -2.220446049250313E-16)
			new_thresh = (int)(temp - 0.5);
		else
			new_thresh = (int)(temp + 0.5);
		/*  Stop the iterations when the difference between the
		new and old threshold values is less than the tolerance */
	} while (abs(new_thresh - old_thresh) > tolerance);
	
	return threshold;
}

 

사용 예시

		std::string str_image_path;
		cv::Mat raw_img = cv::imread(str_image_path, -1);
		cv::Mat raw_img_8bit;
		
		// 8Bit 1Channel 만 가능...
		raw_img.convertTo(raw_img_8bit, CV_8UC1, 256.0 / 65536.0);

		int histSize = 256;    
		float range[] = { 0, 255 };
		const float* histRange = { range };
		cv::Mat hist;
		cv::calcHist(&raw_img_8bit, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange);

		int n_length = raw_img.rows * raw_img.cols;
		uint8_t threshold_value = Li((float*)hist.data, 256);
	
		cv::Mat threshold_img;
		cv::threshold(raw_img_8bit, threshold_img, threshold_value, 255, cv::THRESH_BINARY);

 

imageJ 관련 문서 : https://imagej.net/plugins/auto-threshold

github link : https://github.com/fiji/Auto_Threshold/blob/master/src/main/java/fiji/threshold/Auto_Threshold.java