OpenCV series of notes seventeen: Adaptive thresholding

Overview

Before introducing Adaptive thresholding, let's look at the concept of Integral Image:

Integral images have been introduced as an efficient way of summing
pixels in image regions of interest. They are widely used in applications
that involve, for example, computations over sliding windows at multiple
scales.

Can be seen, Integral Image avoid a plurality of ROI pixel values ​​and a method for double counting. Next, we achieve the ROI is a simple rectangle, such that each point on the value of the Integral Image: The corner of the image forming point and the pixel values ​​of the rectangular regions and.

The Adaptive thresholding is relative to the fixed thresholding terms. Before we used fixed is fixed threshold, while Adaptive threshold is a fixed threshold and determine a change in the mean, this mean that the average number of pixels around a pixel.

Code

integral.h

In fact, OpenCV has provided a function to calculate the Integral Image of cv::integral, but also to realize his book aside, we here also labeled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73



#define IINTEGRAL

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

#include <vector>

template <typename T, int N>
class IntegralImage {

cv::Mat integralImage;

public:

IntegralImage(cv::Mat image) {


cv::integral(image,integralImage,cv::DataType<T>::type);
}

// compute sum over sub-regions of any size from 4 pixel access
cv::Vec<T,N> operator()(int xo, int yo, int width, int height) {

// window at (xo,yo) of size width by height
return (integralImage.at<cv::Vec<T,N> >(yo+height,xo+width)
-integralImage.at<cv::Vec<T,N> >(yo+height,xo)
-integralImage.at<cv::Vec<T,N> >(yo,xo+width)
+integralImage.at<cv::Vec<T,N> >(yo,xo));
}

// compute sum over sub-regions of any size from 4 pixel access
cv::Vec<T,N> operator()(int x, int y, int radius) {

// square window centered at (x,y) of size 2*radius+1
return (integralImage.at<cv::Vec<T,N> >(y+radius+1,x+radius+1)
-integralImage.at<cv::Vec<T,N> >(y+radius+1,x-radius)
-integralImage.at<cv::Vec<T,N> >(y-radius,x+radius+1)
+integralImage.at<cv::Vec<T,N> >(y-radius,x-radius));
}
};

// convert to a multi-channel image made of binary planes
// nPlanes must be a power of 2
void (const cv::Mat& input, cv::Mat& output, int nPlanes) {

// number of bits to mask out
int n= 8-static_cast<int>(log(static_cast<double>(nPlanes))/log(2.0));
// mask used to eliminate least significant bits
uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0

// create a vector of 16 binary images
std::vector<cv::Mat> planes;
// reduce to nBins bins by eliminating least significant bits
cv::Mat reduced= input&mask;

// compute each binary image plane
for (int i=0; i<nPlanes; i++) {

// 1 for each pixel equals to i<<shift
planes.push_back((reduced==(i<<n))&0x1); // i<<n --> 16, 32, 48, ...
}

// create multi-channel image
cv::merge(planes,output);
}

#endif


main.cpp

这里介绍了,OpenCV自带的fixed thresholding, Adaptive thresholding, boxFilter的用法,同时运用Integral Image重新实现了一遍Adaptive thresholding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
大专栏  OpenCV系列笔记十七:Adaptive thresholding>91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

#include <cv.h>
#include <highgui.h>
#include <opencv2/imgcodecs.hpp>
#include <opencv/cv.hpp>
#include <iostream>
#include "integral.h"

int main(){
cv::Mat image = cv::imread("/home/shensir/Documents/MyPrograming/Cpp/Clions/data/book.png",0);

// display original image
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);

// using a fixed threshold
cv::Mat binaryFixed;
cv::Mat binaryAdaptive;
cv::threshold(image, binaryFixed,70,255,cv::THRESH_BINARY);

//using as adaptive threshold
int blockSize=21; //size of the neighborhood
int threshold=10; // pixel will be compare to (mean-threshold)

int64 time;
time = cv::getTickCount();
cv::adaptiveThreshold(
image, //input image
binaryAdaptive, // output binary image
255, //max value for output
cv::ADAPTIVE_THRESH_GAUSSIAN_C, // adaptive method
cv::THRESH_BINARY, //threshold type
blockSize,
threshold
);


time = cv::getTickCount() - time;
std::cout<<"time (adaptiveThreshold) = "<<time<<endl;

// compute integral image
IntegralImage<int, 1>integral(image);

// test integral image
// cout<<"sum = "<<integral(18, 45,30, 50)<<endl;
// cv::Mat test(image, cv::Rect(18,45,30,50));
// cv::Scalar t = cv::sum(test);
// cout<<"sum test = "<<t[0]<<endl;

cv::namedWindow("Fixed Threshold");
cv::imshow("Fixed Threshold", binaryFixed);

cv::namedWindow("Adaptive Threshold");
cv::imshow("Adaptive Threshold", binaryAdaptive);

cv::Mat binary = image.clone();
time = cv::getTickCount();
int nl = binary.rows; // number of lines
int nc = binary.cols; // total number of elements per line

// compute integral image
cv::Mat iimage;
cv::integral(image, iimage, CV_32S);

//for each row
int halfSize = blockSize/2;
for(int j=halfSize; j<nl - halfSize -1;j++ ){
// get the address of row j
uchar* data = binary.ptr<uchar>(j);
int* idata1 = iimage.ptr<int>(j-halfSize); // 滑动窗口上边
int* idata2 = iimage.ptr<int>(j+halfSize+1); // 滑动窗口下边

//for pixel of a line
for(int i=halfSize; i<nc-halfSize-1;i++){
//compute pix_mean
int pix_mean = (idata2[i+halfSize+1]-idata2[i-halfSize]-idata1[i+halfSize+1]
+idata1[i-halfSize])/(blockSize*blockSize);

//apply adaptive threshold
if(data[i]<(pix_mean-threshold))
data[i] = 0;
else
data[i] =255;
}

}
// add white border
for(int j=0;j<halfSize;j++){
uchar *data = binary.ptr<uchar>(j);
for(int i=0; i<binary.cols;i++)
data[i] = 255;
}
for(int j=binary.rows-halfSize-1;j<binary.rows;j++){
uchar * data = binary.ptr<uchar>(j);
for(int i=0; i<binary.cols;i++){
data[i] = 255;
}
}
for(int j=halfSize;j<nl-halfSize-1;j++){
uchar* data = binary.ptr<uchar>(j);
for(int i=0; i<halfSize;i++)
data[i] = 255;
for(int i=binary.cols-halfSize-1;i<binary.cols;i++)
data[i] = 255;
}

time = cv::getTickCount()-time;
cout<<"time integral= "<<time<<endl;

cv::namedWindow("Adaptive Threshold (integral)");
cv::imshow("Adaptive Threshold (integral)", binary);

// adaptive threshold using image operators
time = cv::getTickCount();
cv::Mat filtered;
cv::Mat binaryFiltered;
// box filter compute avg of pixels over a rectangle region
cv::boxFilter(image, filtered, CV_8U, cv::Size(blockSize,blockSize));
// check if pixel greater than (mean+shreshold)
binaryFiltered = image>=(filtered-threshold);
time = cv::getTickCount()-time;

cout<<"time filtered= "<<time<<endl;

cv::namedWindow("Adaptive filtered");
cv::imshow("Adaptive filtered", binaryFiltered);

cv::waitKey();

return 0;

}

Output:

time (adaptiveThreshold) = 2305893
time integral= 7118664
time filtered= 1179760

Guess you like

Origin www.cnblogs.com/wangziqiang123/p/11724435.html