【OpenCV图像处理】十三、图像的距离变换

版权声明:本文为博主转载文章,如有不妥,请私信删除。原文地址: https://blog.csdn.net/qq_34784753/article/details/68951918

图像的距离变换实现了像素与图像区域的距离变换,使得最后生成的图像在该自己元素位置处的像素为0,临近的背景的像素具有较小的值,且随着距离的增大它的的数值也就越大。对于距离图像来说,图像中的每个像素的灰度值为该像素与距离其最近的背景像素间的距离,也就是说,给每个像素赋值为离它最近的背景像素点与其距离,一幅二值图像的距离变换可以提供每个像素到最近的非零像素的距离。

距离变换的一般步骤如下:

1.将输入图片转换为二值图像,前景设置为1,背景设置为0;

2.第一遍水平扫描从左上角开始,依次从左往右逐行扫描,扫描完一行自动跳转到下一行的最左端继续扫描,按行遍历图像。掩膜模板mask为maskL 使用下面的公式进行计算。

其中D表示距离,包括欧氏距离,棋盘距离或是麦哈顿距离,f(p)为像素点p的像素值。

3.第二遍水平扫描从右下角开始,依次从右往左逐行扫描,扫描完一行自动转到上一行的最右端继续扫描,按行遍历图像,掩膜模板mask为maskR,方法和上一步相同

4.根据模板maskL和maskR的扫描结果得到最终的距离变换图像。

具体的程序

  1.  

    //距离变换的扫描实现
    
    
    #include <iostream>
    
    #include <opencv2\core\core.hpp>
    
    #include <opencv2\highgui\highgui.hpp>
    
    #include <opencv2\imgproc\imgproc.hpp>
    
    
    using namespace cv;
    
    using namespace std;
    
    
    //计算欧氏距离的函数
    
    float calcEuclideanDiatance(int x1, int y1, int x2, int y2)
    
    {
    
    return sqrt(float((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)));
    
    }
    
    
    //计算棋盘距离的函数
    
    int calcChessboardDistance(int x1, int y1, int x2, int y2)
    
    {
    
    return cv::max(abs(x1 - x2), (y1 - y2));
    
    }
    
    
    //计算麦哈顿距离(街区距离)
    
    int calcBlockDistance(int x1, int y1, int x2, int y2)
    
    {
    
    return abs(x1 - x2) + abs(y1 - y2);
    
    }
    
    
    //距离变换函数的实现
    
    void distanceTrans(Mat &srcImage, Mat &dstImage)
    
    {
    
    CV_Assert(srcImage.data != nullptr);
    
    //CV_Assert()若括号中的表达式值为false,则返回一个错误信息。
    
    //定义灰度图像的二值图像
    
    Mat grayImage, binaryImage;
    
    //将原图像转换为灰度图像
    
    cvtColor(srcImage, grayImage, CV_BGR2GRAY);
    
    //将灰度图像转换为二值图像
    
    threshold(grayImage, binaryImage, 100, 255, THRESH_BINARY);
    
    imshow("二值化图像", binaryImage);
    
    
    int rows = binaryImage.rows;
    
    int cols = binaryImage.cols;
    
    uchar *pDataOne;
    
    uchar *pDataTwo;
    
    float disPara = 0;
    
    float fDisMIn = 0;
    
    
    //第一遍遍历图像,使用左模板更新像素值
    
    for (int i = 1; i < rows - 1; i++)
    
    {
    
    //图像的行指针的获取
    
    pDataOne = binaryImage.ptr<uchar>(i);
    
    for (int j = 1; j < cols; j++)
    
    {
    
    //分别计算左模板掩码的相关距离
    
    //PL PL
    
    //PL P
    
    //PL
    
    pDataTwo = binaryImage.ptr<uchar>(i - 1);
    
    disPara = calcEuclideanDiatance(i, j, i - 1, j - 1);
    
    fDisMIn = cv::min((float)pDataOne[j], disPara + pDataTwo[j - 1]);
    
    disPara = calcEuclideanDiatance(i, j, i - 1, j);
    
    fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j]);
    
    pDataTwo = binaryImage.ptr<uchar>(i);
    
    disPara = calcEuclideanDiatance(i, j, i, j - 1);
    
    fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j-1]);
    
    pDataTwo = binaryImage.ptr<uchar>(i+1);
    
    disPara = calcEuclideanDiatance(i, j, i+1,j-1);
    
    fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j - 1]);
    
    pDataOne[j] = (uchar)cvRound(fDisMIn);
    
    }
    
    }
    
    
    //第二遍遍历图像,使用右模板更新像素值
    
    for (int i = rows - 2; i > 0; i--)
    
    {
    
    pDataOne = binaryImage.ptr<uchar>(i);
    
    for (int j = cols - 1; j >= 0; j--)
    
    {
    
    //分别计算右模板掩码的相关距离
    
    //pR pR
    
    //pR p
    
    //pR
    
    pDataTwo = binaryImage.ptr<uchar>(i + 1);
    
    disPara = calcEuclideanDiatance(i, j, i + 1, j);
    
    fDisMIn = cv::min((float)pDataOne[j], disPara + pDataTwo[j]);
    
    disPara = calcEuclideanDiatance(i, j, i + 1, j + 1);
    
    
    fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j+1]);
    
    pDataTwo = binaryImage.ptr<uchar>(i);
    
    disPara = calcEuclideanDiatance(i, j, i, j +1);
    
    fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j + 1]);
    
    pDataTwo = binaryImage.ptr<uchar>(i - 1);
    
    disPara = calcEuclideanDiatance(i, j, i - 1, j + 1);
    
    fDisMIn = cv::min(fDisMIn, disPara + pDataTwo[j + 1]);
    
    pDataOne[j] = (uchar)cvRound(fDisMIn);
    
    }
    
    }
    
    dstImage = binaryImage.clone();
    
    }
    
    
    //主函数
    
    int main()
    
    {
    
    Mat srcImage = imread("2345.jpg");
    
    if (!srcImage.data)
    
    {
    
    cout << "读入图片错误!" << endl;
    
    system("pause");
    
    return -1;
    
    }
    
    Mat dstImage;
    
    distanceTrans(srcImage, dstImage);
    
    imshow("距离变换图像", dstImage);
    
    waitKey();
    
    return 0;
    
    }

     

这里使用的是以计算欧氏距离为例,执行程序后的结果如下:

 

在OpenCV中也提供了相关的函数

函数distanceTransform函数用来计算原图像中每个像素的距离,函数的声明如下:

CV_EXPORTS_AS(distanceTransformWithLabels) void distanceTransform( InputArray src,

OutputArray dst, OutputArray labels, int distanceType, int maskSize,int labelType=DIST_LABEL_CCOMP );

//! computes the distance transform map

CV_EXPORTS_W void distanceTransform( InputArray src, OutputArray dst, int distanceType, int maskSize );

函数说明

用于计算图像中每一个非零点距离离自己最近的零点的距离,distanceTransform的第二个Mat矩阵参数dst保存了每一个点与最近的零点的距离信息,图像上越亮的点,代表了离零点的距离越远。

第一个参数src是单通道的8bit的二值图像(只有0或1)

第二个参数dst表示的是计算距离的输出图像,可以使单通道32bit浮点数据

第三个参数distanceType表示的是选取距离的类型,可以设置为CV_DIST_L1,CV_DIST_L2,CV_DIST_C等,具体如下:

DIST_USER = ⑴, //!< User defineddistance

DIST_L1 = 1, //!< distance = |x1-x2| + |y1-y2|

DIST_L2 = 2, //!< the simple euclidean distance

DIST_C = 3, //!< distance = max(|x1-x2|,|y1-y2|)

DIST_L12 = 4, //!< L1-L2 metric: distance =2(sqrt(1+x*x/2) - 1))

DIST_FAIR = 5, //!< distance = c^2(|x|/c-log(1+|x|/c)),c = 1.3998

DIST_WELSCH = 6, //!< distance = c^2/2(1-exp(-(x/c)^2)), c= 2.9846

DIST_HUBER = 7 //!< distance = |x|<c ? x^2/2 :c(|x|-c/2), c=1.345

第四个参数maskSize表示的是距离变换的掩膜模板,可以设置为3,5或CV_DIST_MASK_PRECISE,对 CV_DIST_L1 或CV_DIST_C 的情况,参数值被强制设定为 3, 因为3×3 mask 给出5×5 mask 一样的结果,而且速度还更快。 

参数labels表示可选输出2维数组

参数labelType表示的是输出二维数组的类型

函数的具体用法如下程序所示:

//使用OpenCV中的相应的函数实现距离变换


#include <iostream>

#include <opencv2\core\core.hpp>

#include <opencv2\highgui\highgui.hpp>

#include <opencv2\imgproc\imgproc.hpp>


using namespace cv;

using namespace std;


int main()

{

Mat srcImage = imread("2345.jpg");

if (!srcImage.data)

{

cout << "读入图片错误!" << endl;

system("pause");

return -1;

}

//转换为灰度图像

Mat grayImage;

cvtColor(srcImage, grayImage, CV_BGR2GRAY);

//转换为二值图像

Mat binaryImage;

threshold(grayImage, binaryImage, 100, 255, THRESH_BINARY);

//距离变换

Mat dstImage;

distanceTransform(binaryImage, dstImage, CV_DIST_L2, CV_DIST_MASK_PRECISE);

//使用欧式距离进行计算

//归一化矩阵

normalize(dstImage, dstImage, 0, 1, NORM_MINMAX);

imshow("二值化图像", binaryImage);

imshow("距离变换后的图像", dstImage);

waitKey();

return 0;

}

程序执行后,执行的结果如下:

 

 

--------------------- 本文来自 S大幕 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qq_34784753/article/details/68951918?utm_source=copy

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import cv2 as cv import numpy as np def cv_show(name,img): cv.imshow(name,img) cv.waitKey(0) cv.destroyAllWindows() def detectAndDescribe(image): gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) sift = cv.xfeatures2d.SIFT_create() (kps,features)=sift.detectAndCompute(image,None)#这里的kps是一个特征点对象,,属性有.pt关键点坐标 #.angle关键点方向 .response响应强度 .size该点的直径大小 kps = np.float32([kp.pt for kp in kps])#此刻得到kps特征点对象中每一个特征点的坐标。 return (kps,features) def matchKeypoints(kpsA,kpsB,features1,features2,ratio): bf = cv.BFMatcher() rawMatches = bf.knnMatch(features1,features2,2)#rawMatcher是一个Dmatch型对象,属性有.distance描述符间距离 #.trainIdx样本图像特征点标识符,.queryIdx测试图像的特征点标识符,.imgIdx训练图像的索引 matches = [] for m,n in rawMatches: if m.distance 4: pts1 = np.float32([kpsA[i] for (_,i) in matches])#将测试图像的坐标储存到Pts1里 pts2 = np.float32([kpsB[i] for (i,_) in matches])#将样本图像的坐标储存到pts2里 # 计算视角变换矩阵H #参数一,测试图像的特征点坐标,参数二,样本图像的特征点坐标,参数三,RANSAC算法: #RANSCA原理, 因为拟合一条直线只需要两个点,因此我们每次随机选取两个点,做出直线,划定一个距离,判断落在直线周围距离范围点的个数, # 不断的迭代,直到找出拟合的直线,使得点落在上面最多的拟合曲线 #参数四:参数范围1~10,原图像的点经过变换后点与目标图像上对应点的误差,超过了就是outlier (H, status) = cv.findHomography(pts1, pts2, cv.RANSAC, 5) return (matches, H, status) return None imageA = cv.imread("E:/opencv/picture/right1.jpg") imageB = cv.imread("E:/opencv/picture/left1.png") (kpsA,features1)=detectAndDescribe(imageA) (kpsB,features2)=detectAndDescribe(imageB) M = matchKeypoints(kpsA, kpsB, features1, features2, 0.75) (matches, H, status) = M # 将片A进行视角变换,result是变换片 result = cv.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageB.shape[0])) cv_show('result1',result) result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB cv_show('result2', result) 经常遇到的一个错误: new style getargs format but argument is not a tuple 针对这句代码:result = cv.warpPerspective(imageA,M,[imageA.shape[1]+imageB.shape[1],max(imageA.shape[0],imageB.shape[0])]) 原因是size那个参数应该是tuple(),而不是list[]。即应该是()而不是[]。 下面讲一下这个案例的大体过程: 1.首先我们是通过SIFT算法找到两张(right,left)的特征点及特征向量,并把特征点的坐标储存起来。 2.通过蛮力匹配算法的得到kWmatches对象,将kWmatches对象的queryIdx和trainIdx给存起来,其中features1对应的图像为样本图像 3.求出样本图像的特征点坐标和测试图像的特征点坐标,找出这两坐标矩阵的H变换公式(利用RANSAC算法),将H变换公式对right图像做透视变换,得到拼接后的右边图像 4.将left原赋给result对应的ROI区域,大功告成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值