OpenCV learning basic image operations (9): threshold operation

Threshold type

Binarization (THRESH_BINARY)

dst(x,y) = \begin{Bmatrix} max(val) \ \ \ \ \ \textit{if} \ src(x,y) > thresh \\ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ otherwise \end{matrix}

Inverse binarization (THRESH_BINARY_INV)

dst(x,y) = \begin{Bmatrix} 0 \ \ \ \ \ \ \ \ \ \textit{if} \ src(x,y) > thresh \\ max(val) \ \ \ \ \ \ \ \ \ \ \ otherwise \end{matrix}

Truncate (THRESH_TRUNC)

dst(x,y) = \begin{Bmatrix} thresh \ \ \ \ \ \textit{if} \ src(x,y) > thresh \\ src(x,y) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ otherwise \end{matrix}

The threshold is zero (THRESH_TOZERO)

dst(x,y) = \begin{Bmatrix} src(x,y) \ \ \ \ \ \textit{if} \ src(x,y) > thresh \\ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ otherwise \end{matrix}

The threshold is reversed to zero

dst(x,y) = \begin{Bmatrix} 0 \ \ \ \ \ \textit{if} \ src(x,y) > thresh \\ src(x,y) \ \ \ \ \ \ \ \ \ \ \ \ otherwise \end{matrix}

Threshold algorithm

Otsu method (THRESH_OTSU)

main idea

Maximize the variance between the foreground and the background, and then use this as the segmentation limit.

Algorithm flow

1. Calculate the gray histogram

2. Assumed threshold, divided into foreground and background

3. Calculate foreground variance and background variance

4. Repeat iterations 2 to 4 to find the largest segmentation value of the variance of the front and back scenes

Code practice

int otsuThreshold(IplImage* img)
{
	int T = 0;//阈值
	int height = img->height;
	int width = img->width;
	int step = img->widthStep;
	int channels = img->nChannels;
	uchar* data = (uchar*)img->imageData;
	double gSum0;//第一类灰度总值
	double gSum1;//第二类灰度总值
	double N0 = 0;//前景像素数
	double N1 = 0;//背景像素数
	double u0 = 0;//前景像素平均灰度
	double u1 = 0;//背景像素平均灰度
	double w0 = 0;//前景像素点数占整幅图像的比例为ω0
	double w1 = 0;//背景像素点数占整幅图像的比例为ω1
	double u = 0;//总平均灰度
	double tempg = -1;//临时类间方差
	double g = -1;//类间方差
	double Histogram[256]={0};// = new double[256];//灰度直方图
	double N = width*height;//总像素数

	for(int i=0;i<height;i++)
	{//计算直方图
		for(int j=0;j<width;j++)
		{
			double temp =data[i*step + j * 3] * 0.114 + data[i*step + j * 3+1] * 0.587 + data[i*step + j * 3+2] * 0.299;
			temp = temp<0? 0:temp;
			temp = temp>255? 255:temp;
			Histogram[(int)temp]++;
		} 
	}

	//计算阈值
	for (int i = 0;i<256;i++)
	{

		gSum0 = 0;
		gSum1 = 0;
		N0 += Histogram[i];			
		N1 = N-N0;
		if(0==N1)break;//当出现前景无像素点时,跳出循环
		w0 = N0/N;
		w1 = 1-w0;
		for (int j = 0;j<=i;j++)
		{
			gSum0 += j*Histogram[j];
		}
		u0 = gSum0/N0;
		for(int k = i+1;k<256;k++)
		{
			gSum1 += k*Histogram[k];
		}

		u1 = gSum1/N1;
		//u = w0*u0 + w1*u1;
		g = w0*w1*(u0-u1)*(u0-u1);

                //让类间最大方差最大
		if (tempg<g)
		{
			tempg = g;
			T = i;
		}
	}
	return T; 
}

Triangulation (THRESH_TRIANGLE)

main idea

Use the pure geometric method to find the best threshold. The condition is that the maximum peak of the histogram is assumed to be on the brightest side, and then the maximum straight-line distance is obtained through a triangle. The gray level of the histogram corresponding to the maximum straight-line distance is Segmentation threshold:

Write picture description here

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, where the largest The histogram position corresponding to the distance is the threshold T corresponding to the image binarization.

Extended situation:
Sometimes the corresponding position of the largest wave 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 of the expansion situation is shown as follows:
Write picture description here

Algorithm flow

1. Convert the image to grayscale
2. Calculate the grayscale histogram of the image
3. Find the boundaries on both sides of the histogram
4. Find the maximum value of the histogram
5. Check if the largest peak is on the bright side, otherwise flip
6. Calculate the threshold to get the threshold T, 255-T if flipped

Code practice

int otsuThreshold(IplImage* img)
{
	int T = 0;//阈值
	int height = img->height;
	int width = img->width;
	int step = img->widthStep;
	int channels = img->nChannels;
	uchar* data = (uchar*)img->imageData;


        int temp = 0;
        bool isflipped = false;

        QVector<int> histogram(256);


        //直方图
        for(int row = 0; row < height; row++)
        {
            for(int col = 0; col<width; col++)
            {
                gray = qGray(image.pixel(col,row));
                histogram[gray] ++;
            }
        }

            //3. 寻找直方图中两侧边界
    int left_bound = 0;
    int right_bound = 0;
    int max = 0;
    int max_index = 0;

    //左侧为零的位置
    for(i = 0; i<256; i++)
    {
        if(histogram[i]>0)
        {
            left_bound = i;
            break;
        }
    }
    //直方图为零的位置
    if(left_bound > 0)
    {
        left_bound --;
    }


    //直方图右侧为零的位置
    for(i = 255; i>0; i--)
    {
        if(histogram[i]>0)
        {
            right_bound = i;
            break;
        }
    }
    //直方图为零的地方
    if(right_bound >0)
    {
        right_bound++;
    }

    //4. 寻找直方图最大值
    for(i = 0; i<256;i++)
    {
        if(histogram[i] > max)
        {
            max = histogram[i];
            max_index = i;
        }
    }

    //判断最大值是否在最左侧,如果是则不用翻转
    //因为三角法二值化只能适用于最大值在最右侧
    if(max_index - left_bound  < right_bound - max_index)
    {
        isflipped = true;
        i = 0;
        j = 255;
        while( i < j )
        {
            // 左右交换
            temp = histogram[i]; histogram[i] = histogram[j]; histogram[j] = temp;
            i++; j--;
        }
        left_bound = 255-right_bound;
        max_index = 255-max_index;
    }


    // 计算求得阈值
    double thresh = left_bound;
    double a, b, dist = 0, tempdist;
    a = max; b = left_bound-max_index;
    for( i = left_bound+1; i <= max_index; i++ )
    {
       // 计算距离 - 不需要真正计算
       tempdist = a*i + b*histogram[i];
       if( tempdist > dist)
       {
           dist = tempdist;
           thresh = i;
       }
    }
    thresh--;

    // 对已经得到的阈值T,如果前面已经翻转了,则阈值要用255-T
    if( isflipped )
    {
         thresh = 255-thresh;
    }

    return thresh;
}


API introduction

threshold(gray_src, dst, threshold_value, threshold_max, THRESH_BINARY);

#gray_src 输入的灰度图 输入图片必须是单通道的
#threshold_value  分割阈值
#threshold_max    分割后的上限
#THRESH_BINARY   分割方式

Code practice

#include <iostream>
#include <math.h>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;
char OUTPUT[] = "OUTPUT_WINDOS";
int threshold_value = 127;
int threshold_max = 255;
void Threshold_Demo(int, void*);
Mat src, gray_src,dst;
int type_value = 2;
int type_max = 4;
int main(int argc, char* argv[])
{
	//src = imread("src.jpg");
	src = imread("cat.png");
	if (!src.data)
	{
		cout << "cannot open image" << endl;
		return -1;
	}
	namedWindow("input image", WINDOW_AUTOSIZE);
	namedWindow(OUTPUT, WINDOW_AUTOSIZE);
	imshow("input image", src);
	createTrackbar("Type Value:", OUTPUT, &type_value, type_max, Threshold_Demo);
	createTrackbar("Threshold Value:", OUTPUT,&threshold_value,threshold_max,Threshold_Demo);
	
	Threshold_Demo(0, 0);


	waitKey(0);
	return 0;
}

void Threshold_Demo(int , void*)
{
	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	threshold(gray_src, dst, threshold_value, threshold_max, type_value);
	imshow(OUTPUT, dst);
}

 

Guess you like

Origin blog.csdn.net/fan1102958151/article/details/107300395