一、图像阈值操作
什么是阈值
阈值又叫临界值,是指一个效应能够产生的最低值或最高值。实际上是基于图片亮度的一个黑白分界值,默认值是50%中性灰,即128,亮度高于128(<50%的灰)的会变白,低于128(>50%的灰)的会变黑。从一副图像中利用阈值分割出我们需要的物体部分(当然这里的物体可以是一部分或者整体)。这样的图像分割方法是基于图像中物体与背景之间的灰度差异,而且此分割属于像素级的分割。为了从一副图像中提取出我们需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断。(注意:阈值的选取依赖于具体的问题。即:物体在不同的图像中有可能会有不同的灰度值)。
阈值化分类
二进制阈值化
处理方案
效果图
运用该阈值类型的时候,先要选定一个特定的阈值量,比如:125,这样,新的阈值产生规则可以解释为大于125的像素点的灰度值设定为最大值(如8位灰度值最大为255),灰度值小于125的像素点的灰度值设定为0。
反二进制阈值化
处理方案
效果图
该阈值化与二进制阈值化相似,先选定一个特定的灰度值作为阈值,不过最后的设定值相反。(在8位灰度图中,例如大于阈值的设定为0,而小于该阈值的设定为255)。
截断阈值化
处理方案
效果图
同样首先需要选定一个阈值,图像中大于该阈值的像素点被设定为该阈值,小于该阈值的保持不变。(例如:阈值选取为125,那小于125的阈值不改变,大于125的灰度值(230)的像素点就设定为该阈值)。
阈值化为0
处理方案
效果图
先选定一个阈值,然后对图像做如下处理:像素点的灰度值大于该阈值的不进行任何改变;像素点的灰度值小于该阈值的,其灰度值全部变为0。
反阈值化为0
处理方案
效果图
阈值操作函数
double threshold( InputArray src, OutputArray dst,double thresh, double maxval, int type );
/*******************************************************************
* src: 输入图像
* dst: 输出图像
* thresh: 阈值的具体值
* maxval: 阈值的最大值
* type: 阈值操作类型
*********************************************************************/
//阈值操作类型
enum ThresholdTypes {
THRESH_BINARY = 0, //二进制
THRESH_BINARY_INV = 1, //反二进制
THRESH_TRUNC = 2, //截断
THRESH_TOZERO = 3, //阈值化为0
THRESH_TOZERO_INV = 4, //反阈值化为0
THRESH_MASK = 7, //自适应阈值
THRESH_OTSU = 8, //利用最大类间方差法(OTSU)求出对图像进行二值化处理的最佳阈值
THRESH_TRIANGLE = 16 //三角算法选择最优阈值
};
综合代码
#include <iostream>
#include <map>
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
class Threshold
{
public:
Threshold() :img(imread("test.jpg"))
{
result["原图"] = img;
}
void Show()
{
for (auto& v : result)
{
imshow(v.first, v.second);
}
waitKey(0);
}
void Testthreshold()
{
threshold(img, result["二进制"], 125, 188, THRESH_BINARY);
threshold(img, result["反二进制"], 125, 188, THRESH_BINARY_INV);
threshold(img, result["截断"], 125, 188, THRESH_TRUNC);
threshold(img, result["阈值化为0"], 125, 188, THRESH_TOZERO);
threshold(img, result["反阈值化为0"], 125, 188, THRESH_TOZERO_INV);
//threshold(img, result["自适应"], 125, 188, THRESH_MASK);
//threshold(img, result["最大类间方差法"], 125, 255, THRESH_OTSU);
//threshold(img, result["三角算法"], 125, 255, THRESH_TRIANGLE);
}
private:
Mat img;
map<string, Mat> result;
};
int main()
{
Threshold* pthresh = new Threshold;
pthresh->Testthreshold();
pthresh->Show();
return 0;
}
二、图像形态学操作之腐蚀和膨胀操作
膨胀与腐蚀是数学形态学在图像处理中最基础的操作。其卷积操作非常简单,对于图像的每个像素,取其一定的邻域,计算最大值/最小值作为新图像对应像素位置的像素值。其中,取最大值就是膨胀,取最小值就是腐蚀。膨胀与腐蚀能实现多种多样的功能,主要如下:
消除噪声
分割出独立的图像元素,在图像中连接相邻的元素。
寻找图像中的明显的极大值区域或极小值区域
求出图像的梯度
图像梯度计算的是图像变化的速度
形态学概述
形态学一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。数学形态学是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换、灰值腐蚀和膨胀、灰值开闭运算、灰值形态学梯度等。简单来讲,形态学操作就是基于形状的一系列图像处理操作。opencv为进行图像的形态学变换提供了快捷、方便的函数。最基本的形态学操作有二种,他们是:膨胀与腐蚀
注意:在做图像的形态学处理的时候,需要对图像做二值化,且需要将处理的像素值改为255,因为图像的形态学处理操作都是基于白色像素上处理的
膨胀
膨胀操作是取每个位置领域内最大值,所以膨胀后输出图像的总体亮度的平均值比起原图会有所升高,图像中比较亮的区域的面积会变大,而较暗物体的尺寸会减小甚至消失。(增加高亮部分)
void dilate( InputArray src, OutputArray dst, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );
/*******************************************************************
* src: 输入图像
* dst: 输出图像
* kernel: 膨胀操作的核
* 参数为0:中心3*3的核
* anchor: 锚点
* (-1,-1):表示位于中心
* iterations: 迭代的次数
* borderType: 图像外部像素的某种边界模式
* borderValue: 边界值,一般不管
*********************************************************************/
腐蚀
膨胀和腐蚀是一对好基友,是相反的一对操作,所以腐蚀就是求局部最小值的操作。(减少高亮部分)
void erode( InputArray src, OutputArray dst, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );
/*******************************************************************
* src: 输入图像
* dst: 输出图像
* kernel: 腐蚀操作的核
* 参数为0:中心3*3的核
* anchor: 锚点
* (-1,-1):表示位于中心
* iterations: 迭代的次数
* borderType: 断图像外部像素的某种边界模式
* borderValue: 边界值,一般不管
*********************************************************************/
核操作
getStructuringElement函数可用于构造一个特定大小和形状的结构元素,用于图像形态学处理
Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
/*******************************************************************
* shape: 形状类型
* ksize: 大小
* anchor: 锚点
* (-1,-1):表示位于中心
*********************************************************************/
//shape取值
enum MorphShapes {
MORPH_RECT = 0, //矩形
MORPH_CROSS = 1, //十字交叉
MORPH_ELLIPSE = 2 //椭圆
};
综合代码
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
class Img
{
public:
Img() :img(imread("text.jpg"))
{
result["原图"] = img;
}
void Show()
{
for (auto& v : result)
{
imshow(v.first, v.second);
}
waitKey(0);
}
void Testdilate()
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
dilate(img, result["膨胀"], kernel);
}
void Testerode()
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
erode(img, result["腐蚀"], kernel);
}
private:
Mat img;
map<string, Mat> result;
};
int main()
{
Img* pimg = new Img;
pimg->Testdilate();
pimg->Testerode();
pimg->Show();
return 0;
}
三、图像形态学操作之开和闭操作
形态学处理
处理类型 操作 作用 开运算 先腐蚀再膨胀 可在纤细点出分离物体。有助于消除噪音 闭运算 先膨胀后腐蚀 用于排除前景对象中的小孔或对象上的小黑点 形态学梯度 膨胀图与腐蚀图之差 用于保留目标物体的边缘轮廓 顶帽 原图与开运算图之差 分离比邻近点亮的斑块,用于突出原图像中比周围亮的区域 黑帽 闭运算图与原图差 分离比邻近点暗的斑块,突出原图像中比周围暗的区域 形态学处理往往是在阈值处理之后才进行的,因为当图像被转化为二值图以后,这个时候只有黑白两者颜色,然后再进行形态学处理是效果最好的。
操作函数
void morphologyEx( InputArray src, OutputArray dst,int op, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );
/*******************************************************************
* src: 输入图像
* dst: 输出图像
* op: 操作类型
* kernel: 操作的核
* 参数为0:中心3*3的核
* anchor: 锚点
* (-1,-1):表示位于中心
* iterations: 迭代的次数
* borderType: 图像外部像素的某种边界模式
* borderValue: 边界值,一般不管
*********************************************************************/
//操作类型
enum MorphTypes{
MORPH_ERODE = 0, //腐蚀
MORPH_DILATE = 1, //膨胀
MORPH_OPEN = 2, //开操作
MORPH_CLOSE = 3, //闭操作
MORPH_GRADIENT = 4, //梯度操作
MORPH_TOPHAT = 5, //顶帽操作
MORPH_BLACKHAT = 6, //黑帽操作
MORPH_HITMISS = 7 //随机操作,只支持CV_8UC1模式
}
综合代码
#include <iostream>
#include <map>
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
class Morphology
{
public:
Morphology() :img(imread("20.jpg"))
{
result["原图"] = img;
}
void Show()
{
for (auto& v : result)
{
imshow(v.first, v.second);
}
waitKey(0);
}
void TestMorphology()
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(img, result["腐蚀"], MORPH_ERODE, kernel);
morphologyEx(img, result["膨胀"], MORPH_DILATE, kernel);
morphologyEx(img, result["开"], MORPH_OPEN, kernel);
morphologyEx(img, result["闭"], MORPH_CLOSE, kernel);
morphologyEx(img, result["梯度"], MORPH_GRADIENT, kernel);
morphologyEx(img, result["顶帽"], MORPH_TOPHAT, kernel);
morphologyEx(img, result["黑帽"], MORPH_BLACKHAT, kernel);
}
private:
Mat img;
map<string, Mat> result;
};
int main()
{
Morphology* pmorph = new Morphology;
pmorph->TestMorphology();
pmorph->Show();
return 0;
}