图像像素距离变换
图像中两个像素之间有多种定义方式,图像处理中常用的距离有欧式距离、街区距离和棋盘距离
欧式距离是指两个像素点之间的直线距离,与直角坐标系中两点之间都直线距离求取方式相同
分别计算两个像素在X方向和Y方向上的距离,之后利用勾股定理得到两个像素之间的距离,可以含有小数部分
街区距离是指两个像素点X方向和Y方向的距离之和,欧式距离表示的是两个像素点的最短距离
然而,有时我们并不能以两个点之间连线的方向前进,例如在一个城市内两点之间可能存在障碍物的阻碍,因此从一个点到另一个点需要
沿着街道行走,因此这种距离的度量方式被称为街区距离,根据定义一定为整数
棋盘距离是指两个像素点X方向距离和Y方向距离的最大值,与街区距离相似
但是,棋盘距离表示的是两个像素点移动到同一行或者同一列时需要移动的最大距离
OpenCV4中提供了用于计算图像中不同像素之间距离的distanceTransform()函数(两个原型)
distanceTransform()函数原型1:
void distanceTransform(InputArray src,
OutputArray dst,
OutputArray labels,
int distanceType,
int maskSize,
int labelType = DIST_LABEL_CCOMP
)
- src:输入图像,数据类型为CV_8U的单通道图像
- dst:输出图像,与输入图像具有相同的尺寸,数据类型为CV_8U或CV_32F的单通道图像
- labels:二维的标签数组(离散Voronoi图),与输入图像具有相同的尺寸,数据类型为CV_32S的单通道数据
- distanceType:选择计算两个像素之间距离方法的标志,其常用的距离度量在表中
- maskkSize:距离变换掩码矩阵尺寸,参数可选DIST_MASK_3(3x3) 和 DIST_MASK_5(5x5)
- labelType:要构建的标签数组的类型,可以选择参数在表中
distanceTransform()函数中常用距离度量方法选择标志
| 标志参数 | 简记 | 作业 | ||||
|---|---|---|---|---|---|---|
| DIST_USER | -1 | 自定义距离 | ||||
| DIST_L1 | 1 | 街区距离,d = \ | x1-x2\ | + \ | y1-y2\ | |
| DIST_L2 | 2 | 欧式距离,d = sqrt( [x1-x2]^2 + [y1-y2]^2 ) | ||||
| DIST_C | 3 | 棋盘距离,d = max(\ | x1-x2\ | ,\ | y1-y2\ | ) |
distanceTransform()函数中标签数组类型标志
| 标志参数 | 简记 | 作用 |
|---|---|---|
| DIST_LABEL_CCOMP | 0 | 输入图像中每个连接的0像素(以及最接近连接区域的所有非零像素) 都将被分配为相同的标签 |
| DIST_LABEL_PIXEL | 1 | 输入图像中每个0像素(以及接近它的所有非零像素) 都有自己的标签 |
该函数用于实现图像的距离变换,即统计图像中所有像素距离0像素的最小距离
第一个参数为待变换的输入图像,输入图像要求必须是CV_8U的单通道图像
第二个参数为输出图像,与输入图像具有相同的尺寸,图像中每个像素值表示该图像在原始图像中距离0像素的最小距离
由于图像的尺寸可能大于256,因此图像中某个像素距离0像素的最近距离可能大于255,为了正确地统计出每个像素距离0像素的
最小距离,输出图像的数据类型可以选择CV_8U或者CV_32F
第三个参数是原始图像的离散Voronoi图,输出图像是数据类型为CV_32S的单通道图像,图像尺寸与输入图像相同
第四个参数是距离变换过程中使用的距离种类,常用距离为欧式距离、街区距离和棋盘距离
第五个参数是求取路径时候的掩码矩阵尺寸,该尺寸与选择的距离种类有关,当使用街区距离时,掩码尺寸选择3x3还是5x5没有影响
因此通常默认选择3x3加快函数运算速度,当使用欧式距离时,3x3为粗略计算。5x5为精准计算,差异较大,推荐使用5x5
当使用棋盘距离时,掩码矩阵尺寸对计算结果没有影响
最后一个参数为构建标签数组的类型,当labelType == DIST_LABEL_CCOMP时,该函数会自动在输入图像中找到0像素的连通分量
并用相同的标签标记它们,当labelType == DIST_LABEL_PIXEL时,该函数扫描输入图像并用不同的标签标记所有0像素
该函数原型在对图像进行距离变换时会生成离散Voronoi图,但有时不需要使用离散图,占用了内存资源
因此distanceTransform()函数第二种原型取消了生成离散图,只输出距离变换后的图像
distanceTransform()函数原型2:
void distanceTransform(InputArray src,
OutputArray dst,
int distanceType,
int maskSize,
int dstType = CV_32F)
- src:输入图像,数据类型为CV_8U的单通道图像
- dst:输出图像,与输入图像具有相同的尺寸,数据类型为CV_8U或CV_32F的单通道图像
- distanceType:选择计算两个像素之间距离方法的标志,其常用的距离度量在表中
- maskkSize:距离变换掩码矩阵尺寸,参数可选DIST_MASK_3(3x3) 和 DIST_MASK_5(5x5)
- dstType:输出图像的数据类型,可以是CV_8U或者CV_32F
该函数前4个参数相同,最后一个参数为输出图像的数据类型,虽然可以在CV_8U和CV_32F两个类型中选择,但是图像输出是实际的
数据类型与距离变换时选择的距离种类有着密切关系,CV_8U只能使用在计算街区距离时,当计算欧式距离和棋盘距离时
即使参数设置CV_8U,实际输出图像的数据类型也是CV_32F
由于distanceTransform()函数是计算图像中非零像素距离0像素的最小距离,而图像中0像素表示黑色,因此,为了保证能够清楚地
观察到距离变换的结果,不建议使用尺寸过小或者黑色区域较多的图像,否则处理后图像中几乎全为黑色,不利于观察
示例程序:首先将图像二值化,之后将二值化图像黑白像素反转,再利用distanceTransform()函数实现距离变换
查看图像时与原二值图像一致,但是内部数据不一致
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
//构建简易矩阵,用于求取像素之间的距离
Mat a = (Mat_<uchar>(5,5) << 1,1,1,1,1,
1,1,1,1,1,
1,1,0,1,1,
1,1,1,1,1,
1,1,1,1,1);
Mat dist_L1,dist_L2,dist_C,dist_L12;
//计算街区距离
distanceTransform(a,dist_L1,1,3,CV_8U);
cout << "街区距离:" << endl << dist_L1 << endl;
//计算欧式距离
distanceTransform(a,dist_L2,2,5,CV_8U);
cout << "欧式距离:" << endl << dist_L2 << endl;
//计算棋盘距离
distanceTransform(a,dist_C,3,5,CV_8U);
cout << "棋盘距离:" << endl << dist_C << endl;
//对图像进行距离变换
Mat rice = imread("E:\\CLion\\opencv_xin\\SG\\SG_0.jpg",IMREAD_GRAYSCALE);
if (rice.empty())
{
cout << "error" << endl;
return -1;
}
Mat riceBW,riceBW_INV;
//将图像转成二值图像,同时把黑白区域颜色互换
threshold(rice,riceBW,50,255,THRESH_BINARY);
threshold(rice,riceBW_INV,50,255,THRESH_BINARY_INV);
//距离变换
Mat dist,dist_INV;
distanceTransform(riceBW,dist,1,3,CV_32F); //为了显示清晰,将数据类型变成CV_32F
distanceTransform(riceBW_INV,dist_INV,1,3,CV_8U);
//显示变换结果
imshow("riceBW",riceBW);
imshow("dist",dist);
imshow("riceBW_INV",riceBW_INV);
imshow("dist_INV",dist_INV);
waitKey(0);
return 0;
}