opencv版本: opencv3.4.1
目录
1. 二值化(阈值化)
计算复杂度: 二值化图像<灰度图像<彩色图像;
一幅图像包括目标,背景及噪声,想要直接提取目标物体,需要采用灰度变换阈值化操作;常见的方法有OTSU,固定阈值,自适应阈值,双阈值及半阈值化操作;
1.1 OTSU阈值化
OTSU算法步骤:
(a) 统计灰度级中每个像素在整幅图像中的个数;
(b) 计算每个像素在整幅图像的概率分布;
(c) 对灰度级进行遍历搜索,计算当前灰度值下前景背景类间概率;
(d) 通过目标函数计算出类内与类间方差下对应的阈值;
代码实现:
int OTSU(cv::Mat srcImage )
{
int cols = srcImage.cols;
int rows = srcImage.rows;
int threshold = 0;
//初始化统计参数
int nSumPix[256] = {0}; //每个像素在整幅图像中的个数;
float nProDis[256] = {0};
memset(nSumPix, 0, sizeof(nSumPix));
memset(nProDis, 0, sizeof(nProDis));
//统计灰度级图像中每个像素在增幅图像中的个数;
for(int i = 0; i < cols; i++){
for(int j = 0; j < rows; j++){
nSumPix[(int)srcImage.at<uchar>(i,j)]++;
}
}
//计算每个灰度级占图像中的概率分布
for(int i = 0; i < 256; i++){
nProDis[i] = (float)nSumPix[i] / (cols * rows);
}
//遍历灰度级[0~255],计算出最大类间方差下的阈值;
float w0, w1, u0_temp, u1_temp, u0, u1, delta_temp;
double delta_max = 0.0;
for(int i = 0; i < 256; i++){
//初始化相关参数
w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;
for(int j = 0; j < 256; j++){
if(j <= i){ //背景部分
//当前i为分割阈值,第一类总的概率
w0 += nProDis[j];
u0_temp += j * nProDis[j];
}else{ //前景部分
w1 += nProDis[j];
u1_temp += j * nProDis[j];
}
}
//分别计算各类的平均灰度
u0 = u0_temp / w0;
u1 = u1_temp / w1;
delta_temp = (float)(w0 * w1 * pow((u0 - u1),2));
//依次找到最大类间方差下的阈值
if(delta_temp > delta_max){
delta_max = delta_temp;
threshold = i ;
}
}
return threshold;
}
1.2 固定阈值化
double threshold(InputArray _src, OutputArray _dst, double thresh, double maxval, int type)
参数1:源图像
参数2:输出图像
参数3:阈值
参数4:预设最大值;一般为255;
参数5:阈值化处理的类型,取值如下:
enum ThresholdTypes {
//二进制阈值化;大于thresh的灰度值设定为255,低于thresh设定为0;
THRESH_BINARY = 0,//反二进制阈值化;大于thresh的灰度值设定为0,低于thresh设定为255;
THRESH_BINARY_INV = 1,//截断阈值化;大于thresh的灰度值设定为thresh,低于thresh设定为不变;
THRESH_TRUNC = 2,//阈值化0;大于thresh的灰度值设定为不变,低于thresh设定为0;
THRESH_TOZERO = 3,//反阈值化0;大于thresh的灰度值设定为0,低于thresh设定为不变;
THRESH_TOZERO_INV = 4,
THRESH_MASK = 7,
THRESH_OTSU= 8,
THRESH_TRIANGLE = 16
};
int main()
{
cv::Mat srcImage = cv::imread("./lena.jpg");
if(srcImage.empty()){
std::cout << "imread failed" << endl;
return -1;
}
//转为灰度图
cv::Mat grayImage;
cv::cvtColor(srcImage, grayImage, CV_RGB2GRAY);
cv::Mat dstImage;
double thresh = 100.0;
//二值化, thresh为阈值
cv::threshold(grayImage, dstImage, thresh, 255, THRESH_BINARY);
cv::imwrite("./lena_threshold.jpg", dstImage);
}
1.3 自适应阈值化
void cv::adaptiveThreshold( InputArray _src, OutputArray _dst, double maxValue,
int method, int type, int blockSize, double delta );参数1: 输入图像;
参数2:输出图像;
参数3:预设最大值;
参数4:自适应算法选择;取值如下:
enum AdaptiveThresholdTypes {
ADAPTIVE_THRESH_MEAN_C = 0,
ADAPTIVE_THRESH_GAUSSIAN_C = 1
};参数5:阈值类型,同threshold中type;
参数6:邻域块大小,用来计算区域阀值,一般选择3,5,7...
参数7:常数,是一个从均值或加权均值提取的常数;
int main()
{
cv::Mat srcImage = cv::imread("./lena.jpg");
if(srcImage.empty()){
std::cout << "imread failed" << endl;
return -1;
}
//转为灰度图
cv::Mat grayImage;
cv::cvtColor(srcImage, grayImage, CV_RGB2GRAY);
cv::Mat dstImage;
cv::adaptiveThreshold(grayImage, dstImage, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 109, 0);
cv::imwrite("./lena_adaHold.jpg", dstImage);
return 0;
}
1.4 双阈值化
对于图像有明显的双分界特征,我们考虑用双阈值法进行二值化操作; 预先设定好特定的阈值量thresh1,thresh2,且thresh1<thresh2; 阈值操作只需要将大于thresh1且大于thresh2的灰度值设定为max_val;其余情况设定为0;
int main()
{
cv::Mat srcImage = cv::imread("./lena.jpg");
if(srcImage.empty()){
std::cout << "imread failed" << endl;
return -1;
}
//转为灰度图
cv::Mat grayImage;
cv::cvtColor(srcImage, grayImage, CV_RGB2GRAY);
const int maxVal = 255;
int low_threshold = 109;
int high_threshold = 210;
cv::Mat dstTempImage1, dstTempImage2, dstImage;
//小阈值对源灰度图像进行阈值化操作
cv::threshold(grayImage, dstTempImage1, low_threshold, maxVal, THRESH_BINARY);
//大阈值对源灰度图像进行阈值化操作
cv::threshold(grayImage, dstTempImage2, high_threshold, maxVal, THRESH_BINARY_INV);
//矩阵与运算得到二值化结果
cv::bitwise_and(dstTempImage1, dstTempImage2, dstImage);
cv::imwrite("./lena_bitwise.jpg", dstImage);
return 0;
}
1.5. 半阈值化
预先设定好thresh, 半阈值化操作值需要将大于thresh的灰度值设定为不变,将其余情况设定为0;
int main()
{
cv::Mat srcImage = cv::imread("./lena.jpg");
if(srcImage.empty()){
std::cout << "imread failed" << endl;
return -1;
}
//转为灰度图
cv::Mat grayImage;
cv::cvtColor(srcImage, grayImage, CV_RGB2GRAY);
const int maxVal = 255;
int low_threshold = 109;
cv::Mat dstTempImage, dstImage;
//对源灰度图像进行阈值化操作
cv::threshold(grayImage, dstTempImage, low_threshold, maxVal, THRESH_BINARY);
//矩阵与运算得到二值化结果
cv::bitwise_and(grayImage, dstTempImage, dstImage);
cv::imwrite("./lena_bitwise1.jpg", dstImage);
return 0;
}
2. 感兴趣区域提取
实际调用了copyTo函数拷贝指定区域;
int main()
{
cv::Mat srcImage = cv::imread("./lena.jpg");
if(srcImage.empty()){
std::cout << "imread failed" << endl;
return -1;
}
cv::Mat roiImage(srcImage.rows, srcImage.cols, CV_8UC3);
srcImage(cv::Rect(80, 50, 300, 350)).copyTo(roiImage);
cv::imwrite("./lena_roi.jpg", roiImage);
}
效果: