图像阈值
固定阈值,自适应阈值,Otsu 二值化等
全局阈值和局部阈值
一、图像二值化
定义:图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
灰度值0:黑,灰度值255:白
一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。这是研究灰度变换的最特殊的方法,称为图像的二值化(Binarization)。
二、固定阈值
与名字一样,这种方法非常简单。但像素值高于阈值时,我们给这个像素赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。这个函数就是:
threshold(img, threshold, maxval,type)
参数解释:
img就是原图像,原图像应该是灰度图
threshold是设定的阈值,就是用来对像素值进行分类的阈值
maxval是当灰度值大于(或小于)阈值时将该灰度值赋成的值
type规定的是当前二值化的方式
OpenCV提供了多种不同的阈值方法,这是有第四个参数来决定的。这些方法包括:
THRESH_BINARY
二值化阈值,大于阈值的部分被置为255,小于部分被置为0
THRESH_BINARY_INV
反向二值化阈值,大于阈值部分被置为0,小于部分被置为255
THRESH_TRUNC
截断阈值化,大于阈值部分被置为threshold,小于部分保持原样
THRESH_TOZERO
小于阈值部分被置为0,大于部分保持不变
THRESH_TOZERO_INV
大于阈值部分被置为0,小于部分保持不变
三、自适应阈值
在图像阈值化操作中,更关注的是从二值化图像中,分离目标区域和背景区域,但是仅仅通过设定固定阈值很难达到理想的分割效果。
在前面的部分我们使用是全局阈值,整幅图像采用同一个数作为阈值。当时这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
自适应阈值,则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。这样做的好处:
每个像素位置处的二值化阈值不是固定不变的,而是由其周围邻域像素的分布来决定的。
亮度较高的图像区域的二值化阈值通常会较高,而亮度低的图像区域的二值化阈值则会相适应的变小。
不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值。
适合处理光照不均的图像。
adaptiveThreshold(src,maxValue,adaptiveMethod,thresholdType, bolckSize, C)
Adaptive Method 指定计算阈值的方法。
ADPTIVE_THRESH_MEAN_C:阈值取自相邻区域的平均值
ADPTIVE_THRESH_GAUSSIAN_C:阈值取值相邻区域的加权和,权重为一个高斯窗口。
thresholdType 指定阈值类型。可选择THRESH_BINARY或者THRESH_BINARY_INV两种。(即二进制阈值或反二进制阈值)
Block Size 邻域大小(用来计算阈值的区域大小),一般选择为3、5、7…等。
C 参数C表示与算法有关的参数,阈值就等于的平均值或者加权平均值减去这个常数,可以是负数。
自适应阈值化计算过程
自适应阈值化计算大概过程是为每一个象素点单独计算的阈值,即每个像素点的阈值都是不同的,就是将该像素点周围B*B区域内的像素加权平均,然后减去一个常数C,从而得到该点的阈值。
- ADAPTIVE_THRESH_MEAN_C,为局部邻域块的平均值,该算法是先求出块中的均值,再减去常数C。
- ADAPTIVE_THRESH_GAUSSIAN_C,为局部邻域块的高斯加权和。该算法是在区域中(x, y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算,再减去常数C。
1
2
例子(参考博客)
如果使用平均值方法,平均值mean为190,差值delta(即常数C)为30。那么灰度小于160的像素为0,大于等于160的像素为255。
二值化:
反向二值化:
四、Otsu二值化
在使用全局阈值时,我们就是随便给了一个数来做阈值,那我们怎么知道我们选取的这个数的好坏呢?答案就是不停的尝试。如果是一副双峰图像(简单来说双峰图像是指图像直方图中存在两个峰)呢?我们岂不是应该在两个峰之间的峰谷选一个值作为阈值?
#include <opencv.hpp>
#include<iostream>
#include <string>
#include<conio.h>
#include<time.h>
using namespace std;
using namespace cv;
Mat src, gray_src, dst;
int threshold_value=227;
int threshold_max = 255;
int type_value = 2;
int type_max = 5;
const char* output_title = "binary image";
void PrintMs(const char* text = "")
{
static long long last = 0;
long long cur = getTickCount();
if (last == 0)
{
last = cur;
return;
}
long long ms = 0;
ms = ((double)(cur - last) / getTickFrequency()) * 1000;
if (*text != 0)
{
printf("%s=%dms\n", text, ms);
printf("\n");
}
last = getTickCount();
}
void THreshhold_Demo(int ,void *)
{
cvtColor(src, gray_src, COLOR_BGR2GRAY);
//threshold(gray_src, dst, threshold_value, threshold_max,type_value);
//自动计算阈值
threshold(gray_src, dst, 0, 255, THRESH_OTSU|type_value);
//通过TRIANGLE计算得到
//threshold(gray_src, dst, 0, 255, THRESH_TRIANGLE | type_value);
imshow(output_title, dst);
}
int main()
{
src = imread("E:\\Users\\opencvCoder\\image\\niu.jpg",1);
//Mat result = src.clone();
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image", src);
namedWindow(output_title, WINDOW_AUTOSIZE);
createTrackbar("Threshold Value:",output_title, &threshold_value, threshold_max, THreshhold_Demo);
createTrackbar("Type Value:", output_title, &type_value, type_max, THreshhold_Demo);
THreshhold_Demo(0, 0);
waitKey(0);
return 0;
}
OTSU自适应阈值方法
typeValue=2