Detailed Explanation of Threshold Function in OpenCv

Brief analysis of parameters:

Performs a fixed threshold operation on array elements

void cvThreshold( const CvArr* src, CvArr* dst, double threshold,
                  double max_value, int threshold_type );

src

Raw array (single channel, 8-bit of 32-bit float).

dst

Output array, must be the same type as src, or 8-bit.

threshold

threshold

max_value

Use the maximum value of CV_THRESH_BINARY and CV_THRESH_BINARY_INV.

threshold_type

threshold type

The function cvThreshold applies a fixed threshold operation to a single-channel array. A typical application of this function is to perform a threshold operation on a grayscale image to obtain a binary image. (cvCmpS can also achieve this purpose) or to remove noise, such as filtering image points with small or large pixel values. The method of thresholding an image supported by this function is determined by threshold_type:

threshold_type=CV_THRESH_BINARY:
dst(x,y) = max_value, if src(x,y)>threshold
           0, otherwise

threshold_type=CV_THRESH_BINARY_INV:
dst(x,y) = 0, if src(x,y)>threshold
           max_value, otherwise

threshold_type=CV_THRESH_TRUNC:
dst(x,y) = threshold, if src(x,y)>threshold
           src(x,y), otherwise

threshold_type=CV_THRESH_TOZERO:
dst(x,y) = src(x,y), if (x,y)>threshold
           0, otherwise

threshold_type=CV_THRESH_TOZERO_INV:
dst(x,y) = 0, if src(x,y)>threshold
           src(x,y), otherwise

Detailed explanation:

Definition: The binarization of an image is to set the grayscale value of the pixel on the image to 0 or 255, that is, to present the entire image with an obvious visual effect of only black and white.

Gray value 0: black, gray value 255: white

   An image includes the target object, background and noise. To directly extract the target object from a multi-valued digital image, the common method is to set a threshold T, and use T to divide the image data into two parts: Pixel groups and pixel groups smaller than T. This is the most special method of studying grayscale transformation, called image binarization (Binarization).

threshold

cv2.threshold(img, threshold, maxval,type)

in:

  1. threshold is the set threshold
  2. maxval is the value assigned to the grayscale value when the grayscale value is greater than (or less than) the threshold
  3. type specifies the current binarization method                

The dashed line is the value that will be thresholded, the middle line is the threshold    

cv2.THRESH_BINARY The part greater than the threshold is set to 255, and the part less than the value is set to 0      

                          

                cv2.THRESH_BINARY_INV The part greater than the threshold is set to 0, and the part less than the value is set to 255

                         

                cv2.THRESH_TRUNC The part greater than the threshold is set to the threshold, and the part smaller than the threshold remains the same  

                         

  

                cv2.THRESH_TOZERO The part less than the threshold is set to 0, and the part greater than the threshold remains unchanged

 

              cv2.THRESH_TOZERO_INV The part greater than the threshold is set to 0, and the part smaller than the threshold remains unchanged 

Let's focus on THRESH_OTSU and THRESH_TRIANGLE.

When THRESH_OTSU and THRESH_TRIANGLE apply :

Refer to the following link:
https://zhuanlan.zhihu.com/p/81680297

The OTSU algorithm has two peaks in the histogram, and the histogram with a clear trough in the middle corresponds to the image binarization effect is better, but the image segmentation effect corresponding to the histogram with only one single peak is not as good as the double peak.
Therefore, OTSU is more suitable for images with distinct foreground and background colors.

 

THRESH_TRIANGLE

Not all photos are made of obvious foreground and background contrasts, like this one.

 So, is there a better way to deal with this unimodal image? The answer is yes. THRESH_TRIANGLE came into being.

THRESH_OTSU:
The Otsu method (OTSU) is an algorithm for determining the image binarization segmentation threshold, which was proposed by Japanese scholar Otsu in 1979. From the principle of the Otsu method, this method is also called the maximum inter-class variance method, because after the image binarization is performed according to the threshold obtained by the Otsu method, the inter-class variance between the foreground and background images is the largest.

It is considered to be the best algorithm for threshold selection in image segmentation. It is simple to calculate and is not affected by image brightness and contrast, so it has been widely used in digital image processing. It divides the image into two parts, the background and the foreground, according to the grayscale characteristics of the image. Because the variance is a measure of the uniformity of the gray distribution, the greater the inter-class variance between the background and the foreground, the greater the difference between the two parts of the image. will result in a smaller difference between the two parts. Therefore, the segmentation that maximizes the between-class variance means the minimum probability of misclassification.

Application: It is the best method to calculate the global threshold value of the image. The application is self-evident, and it is suitable for most occasions where the global threshold value of the image is required.

Advantages: The calculation is simple and fast, and it is not affected by the brightness and contrast of the image.

Find the between-class variance:

The assumption of the OTSU algorithm is that there is a threshold TH to divide all the pixels of the image into two types C1 (less than TH) and C2 (greater than TH), then the respective mean values ​​of these two types of pixels are m1 and m2, and the global mean value of the image is mG. At the same time, the probabilities of pixels being classified into C1 and C2 classes are p1 and p2, respectively. Hence:
insert image description here
insert image description hereOTSU code

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
 
int Otsu(cv::Mat& src, cv::Mat& dst, int thresh){
	const int Grayscale = 256;
	int graynum[Grayscale] = { 0 };
	int r = src.rows;
	int c = src.cols;
	for (int i = 0; i < r; ++i){
		const uchar* ptr = src.ptr<uchar>(i);
		for (int j = 0; j < c; ++j){        //直方图统计
			graynum[ptr[j]]++;
		}
	}
 
    double P[Grayscale] = { 0 };   
	double PK[Grayscale] = { 0 };
	double MK[Grayscale] = { 0 };
	double srcpixnum = r*c, sumtmpPK = 0, sumtmpMK = 0;
	for (int i = 0; i < Grayscale; ++i){
		P[i] = graynum[i] / srcpixnum;   //每个灰度级出现的概率
		PK[i] = sumtmpPK + P[i];         //概率累计和 
		sumtmpPK = PK[i];
		MK[i] = sumtmpMK + i*P[i];       //灰度级的累加均值                                                                                                                                                                                                                                                                                                                                                                                                        
		sumtmpMK = MK[i];
	}
	
	//计算类间方差
	double Var=0;
	for (int k = 0; k < Grayscale; ++k){
		if ((MK[Grayscale-1] * PK[k] - MK[k])*(MK[Grayscale-1] * PK[k] - MK[k]) / (PK[k] * (1 - PK[k])) > Var){
			Var = (MK[Grayscale-1] * PK[k] - MK[k])*(MK[Grayscale-1] * PK[k] - MK[k]) / (PK[k] * (1 - PK[k]));
			thresh = k;
		}
	}
 
	//阈值处理
	src.copyTo(dst);
	for (int i = 0; i < r; ++i){
	    uchar* ptr = dst.ptr<uchar>(i);
		for (int j = 0; j < c; ++j){
			if (ptr[j]> thresh)
				ptr[j] = 255;
			else
				ptr[j] = 0;
		}
	}
	return thresh;
}
 
 
int main(){
	cv::Mat src = cv::imread("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Img\\Fig1039(a)(polymersomes).tif");
	if (src.empty()){
		return -1;
	}
	if (src.channels() > 1)
		cv::cvtColor(src, src, CV_RGB2GRAY);
 
	cv::Mat dst,dst2;
	int thresh=0;
	double t2 = (double)cv::getTickCount();
	thresh=Otsu(src , dst, thresh); //Otsu
	std::cout << "Mythresh=" << thresh << std::endl;
	t2 = (double)cv::getTickCount() - t2;
	double time2 = (t2 *1000.) / ((double)cv::getTickFrequency());
	std::cout << "my_process=" << time2 << " ms. " << std::endl << std::endl;
    double  Otsu = 0;
 
	Otsu=cv::threshold(src, dst2, Otsu, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
	std::cout << "OpenCVthresh=" << Otsu << std::endl;
 
	cv::namedWindow("src", CV_WINDOW_NORMAL);
	cv::imshow("src", src);
	cv::namedWindow("dst", CV_WINDOW_NORMAL);
	cv::imshow("dst", dst);
	cv::namedWindow("dst2", CV_WINDOW_NORMAL);
	cv::imshow("dst2", dst2);
	//cv::imwrite("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Image Filtering\\MeanFilter\\TXT.jpg",dst);
	cv::waitKey(0);
}

THRESH_TRIANGLE:
The triangular method to find the threshold was first seen in Zack's paper "Automatic measurement of sister chromatid exchange frequency", which is mainly used for the study of chromosomes. This method uses histogram data and is based on a pure geometric method to find the optimal threshold. Its establishment conditions It is assumed that the maximum peak of the histogram is on the brightest side, and then the maximum straight-line distance is obtained through the triangle, and the gray level of the histogram corresponding to the maximum straight-line distance is the segmentation threshold, as shown below:

Detailed explanation of the above figure:
Construct a straight line from the highest peak bmx to the darkest corresponding histogram bmin(p=0)% on the histogram, and calculate the vertical distance from each corresponding histogram b to the straight line from bmin , until bmax is known, the histogram position corresponding to the maximum distance is the threshold T corresponding to image binarization.

Extended situation:
Sometimes the corresponding position of the largest peak is not on the brightest side of the histogram, but on the dark side. In this way, the histogram needs to be flipped, and the value obtained after flipping is subtracted from 255 to obtain the threshold T. The histogram representation of the expansion case is as follows:

THRESH_TRIANGLE algorithm steps

  1. Image to grayscale
  2. Compute image grayscale histogram
  3. Find the borders on both sides of the histogram
  4. Find the histogram maximum
  5. Check if the largest peak is on the bright side, otherwise flip
  6. Calculate the threshold to get the threshold T, if flipped then 255-T
  7. Code:
package com.gloomyfish.filter.study;
import java.awt.image.BufferedImage;

public class TriangleBinaryFilter extends AbstractBufferedImageOp{

    public TriangleBinaryFilter() {
        System.out.println("triangle binary filter");
    }
    @Override
    public BufferedImage filter(BufferedImage src, BufferedImage dest) {
        int width = src.getWidth();
        int height = src.getHeight();

        if ( dest == null )
            dest = createCompatibleDestImage( src, null );
        // 图像灰度化
        int[] inPixels = new int[width*height];
        int[] outPixels = new int[width*height];
        getRGB( src, 0, 0, width, height, inPixels );
        int index = 0;
        for(int row=0; row<height; row++) {
            int ta = 0, tr = 0, tg = 0, tb = 0;
            for(int col=0; col<width; col++) {
                index = row * width + col;
                ta = (inPixels[index] >> 24) & 0xff;
                tr = (inPixels[index] >> 16) & 0xff;
                tg = (inPixels[index] >> 8) & 0xff;
                tb = inPixels[index] & 0xff;
                int gray= (int)(0.299 *tr + 0.587*tg + 0.114*tb);
                inPixels[index]  = (ta << 24) | (gray << 16) | (gray << 8) | gray;
            }
        }
        // 获取直方图
        int[] histogram = new int[256];
        for(int row=0; row<height; row++) {
            int tr = 0;
            for(int col=0; col<width; col++) {
                index = row * width + col;
                tr = (inPixels[index] >> 16) & 0xff;
                histogram[tr]++;
            }
        }
        int left_bound = 0, right_bound = 0, max_ind = 0, max = 0;
        int temp;
        boolean isflipped = false;
        int i=0, j=0;
        int N = 256;

        // 找到最左边零的位置
        for( i = 0; i < N; i++ )
        {
            if( histogram[i] > 0 )
            {
                left_bound = i;
                break;
            }
        }
     // 位置再移动一个步长,即为最左侧零位置 
        if( left_bound > 0 )
            left_bound--;
        // 找到最右边零点位置
        for( i = N-1; i > 0; i-- )
        {
            if( histogram[i] > 0 )
            {
                right_bound = i;
                break;
            }
        }
        // 位置再移动一个步长,即为最右侧零位置 
        if( right_bound < N-1 )
            right_bound++;

        // 在直方图上寻找最亮的点Hmax
        for( i = 0; i < N; i++ )
        {
            if( histogram[i] > max)
            {
                max = histogram[i];
                max_ind = i;
            }
        }
        // 如果最大值落在靠左侧这样就无法满足三角法求阈值,所以要检测是否最大值是否靠近左侧
        // 如果靠近左侧则通过翻转到右侧位置。
        if( max_ind-left_bound < right_bound-max_ind)
        {
            isflipped = true;
            i = 0;
            j = N-1;
            while( i < j )
            {
                // 左右交换
                temp = histogram[i]; histogram[i] = histogram[j]; histogram[j] = temp;
                i++; j--;
            }
            left_bound = N-1-right_bound;
            max_ind = N-1-max_ind;
        }
        // 计算求得阈值
        double thresh = left_bound;
        double a, b, dist = 0, tempdist;
        a = max; b = left_bound-max_ind;
        for( i = left_bound+1; i <= max_ind; i++ )
        {
            // 计算距离 - 不需要真正计算
            tempdist = a*i + b*histogram[i];
            if( tempdist > dist)
            {
                dist = tempdist;
                thresh = i;
            }
        }
        thresh--;

        // 对已经得到的阈值T,如果前面已经翻转了,则阈值要用255-T
        if( isflipped )
            thresh = N-1-thresh;
        // 二值化
        System.out.println("final threshold value : " + thresh);
        for(int row=0; row<height; row++) {
            for(int col=0; col<width; col++) {
                index = row * width + col;
                int gray = (inPixels[index] >> 8) & 0xff;
                if(gray > thresh)
                {
                    gray = 255;
                    outPixels[index]  = (0xff << 24) | (gray << 16) | (gray << 8) | gray;
                }
                else
                {
                    gray = 0;
                    outPixels[index]  = (0xff << 24) | (gray << 16) | (gray << 8) | gray;
                }
            }
        }
        // 返回二值图像
        setRGB(dest, 0, 0, width, height, outPixels );
        return dest;
    }

}

adaptive threshold

In the image thresholding operation, more attention is paid to separating the target area and the background area from the binarized image, but it is difficult to achieve the ideal segmentation effect only by setting a fixed threshold. The adaptive threshold is to determine the binarization threshold at the pixel position according to the pixel value distribution of the neighborhood blocks of the pixel. The benefits of doing this:

The binarization threshold at each pixel position is not fixed, but is determined by the distribution of neighboring pixels around it.

The binarization threshold of the image area with higher brightness is usually higher, and the binarization threshold of the image area with lower brightness will be correspondingly smaller.

Local image regions with different brightness, contrast, and texture will have corresponding local binarization thresholds.
 

1.    void adaptiveThreshold(InputArray src, OutputArray dst,  
2.                           double maxValue, int adaptiveMethod,  
3.                           int thresholdType, int bolckSize, double C) 
4. 参数说明
参数1:InputArray类型的src,输入图像,填单通道,单8位浮点类型Mat即可。
参数2:函数运算后的结果存放在这。即为输出图像(与输入图像同样的尺寸和类型)。
参数3:预设满足条件的最大值。
参数4:指定自适应阈值算法。可选择ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C两种。(具体见下面的解释)。
参数5:指定阈值类型。可选择THRESH_BINARY或者THRESH_BINARY_INV两种。(即二进制阈值或反二进制阈值)。
参数6:表示邻域块大小,用来计算区域阈值,一般选择为3、5、7......等。
参数7:参数C表示与算法有关的参数,它是一个从均值或加权均值提取的常数,可以是负数。(具体见下面的解释)。

Explanation of the content of parameter 4 and parameter 7:
the approximate process of adaptive thresholding calculation is to calculate the threshold value separately for each pixel point, that is, the threshold value of each pixel point is different, that is, B*B around the pixel point The weighted average of the pixels in the area is then subtracted by a constant C to obtain the threshold of the point. B is specified by parameter 6, and the constant C is specified by parameter 7.
ADAPTIVE_THRESH_MEAN_C, which is the average value of the local neighborhood block, the algorithm first calculates the average value in the block, and then subtracts the constant C.

ADAPTIVE_THRESH_GAUSSIAN_C, Gaussian weighted sum of local neighborhood blocks. The algorithm is to weight the pixels around (x, y) in the area according to the Gaussian function according to their distance from the center point, and then subtract the constant C.

For example: if the average method is used, the average mean is 190, and the difference delta (ie, the constant C) is 30. Then the pixel whose gray level is less than 160 is 0, and the pixel whose gray level is greater than or equal to 160 is 255. As shown below:

insert image description here

If it is reverse binarization, as shown below:

insert image description here

It is also possible to choose a negative value for delta (constant C).
 

/*
    自适应阈值:adaptiveThreshold()函数
*/
#include <opencv2/core/core.hpp>              
#include <opencv2/highgui/highgui.hpp>              
#include <opencv2/imgproc/imgproc.hpp>             
#include <iostream>            
using namespace std;
using namespace cv;

int main()
{
    //------------【1】读取源图像并检查图像是否读取成功------------    
    Mat srcImage = imread("D:\\OutPutResult\\ImageTest\\build.jpg");
    if (!srcImage.data)
    {
        cout << "读取图片错误,请重新输入正确路径!\n";
        system("pause");
        return -1;
    }
    imshow("【源图像】", srcImage);
    //------------【2】灰度转换------------    
    Mat srcGray;
    cvtColor(srcImage, srcGray, CV_RGB2GRAY);
    imshow("【灰度图】", srcGray);
    //------------【3】初始化相关变量---------------  
    Mat dstImage;        //初始化自适应阈值参数
    const int maxVal = 255;
    int blockSize = 3;    //取值3、5、7....等
    int constValue = 10;
    int adaptiveMethod = 0;
    int thresholdType = 1;
    /*
        自适应阈值算法
        0:ADAPTIVE_THRESH_MEAN_C
        1:ADAPTIVE_THRESH_GAUSSIAN_C
        --------------------------------------
        阈值类型
        0:THRESH_BINARY
        1:THRESH_BINARY_INV
    */
    //---------------【4】图像自适应阈值操作-------------------------
    adaptiveThreshold(srcGray, dstImage, maxVal, adaptiveMethod, thresholdType, blockSize, constValue);
 
    imshow("【自适应阈值】", dstImage);
    waitKey(0);
    return 0;
}

Filter processing
In addition, whether to perform filter processing or not has a relatively large impact on image segmentation.

  1. adaptiveThreshold division
    Mat img=imread("D:/ImageTest/sudoku.png",CV_LOAD_IMAGE_COLOR);
        Mat dst1;
        Mat dst2;
        Mat dst3;
        cv::cvtColor(img,img,COLOR_RGB2GRAY);//进行,灰度处理
        medianBlur(img,img,5);//中值滤波
        threshold(img,dst1, 127, 255, THRESH_BINARY);//阈值分割
        adaptiveThreshold(img,dst2,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,11,2);//自动阈值分割,邻域均值
        adaptiveThreshold(img,dst3,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,11,2);//自动阈值分割,高斯邻域
        //ADAPTIVE_THRESH_MEAN_C : threshold value is the mean of neighbourhood area
        //ADAPTIVE_THRESH_GAUSSIAN_C : threshold value is the weighted sum of neighbourhood values where weights are a gaussian window. 
        imshow("dst1", dst1);
        imshow("dst2", dst2);
        imshow("dst3", dst3);
        imshow("img", img);
        waitKey(0);
    

    2. Adding the maximum inter-class variance segmentation of filtering processing

Mat img=imread("D:/ImageTest/pic2.png",CV_LOAD_IMAGE_COLOR);
    Mat dst1;
    Mat dst2;
    Mat dst3;
    cv::cvtColor(img,img,COLOR_RGB2GRAY);//进行,灰度处理
    //    medianBlur(img,img,5);
    threshold(img,dst1, 127, 255, THRESH_BINARY);
    threshold(img,dst2,0, 255, THRESH_OTSU);//最大类间方差法分割 Otsu algorithm to choose the optimal threshold value
    Mat img2=img.clone();
    GaussianBlur(img2,img2,Size(5,5),0);//高斯滤波去除小噪点
    threshold(img2,dst3, 0, 255, THRESH_OTSU);
    imshow("BINARY dst1", dst1);
    imshow("OTSU dst2", dst2);
    imshow("GaussianBlur OTSU dst3", dst3);
    imshow("original img", img);
    waitKey(0);

Guess you like

Origin blog.csdn.net/qq_37835727/article/details/123373339