2019-01-22 00:30:13 qq_42887760 阅读数 449

什么是图像分割(Image Segmentation)

  • 图像分割(Image Segmentation)是图像处理最重要的处理手段之一

  • 图像分割的目标是将图像中像素根据一定的规则分为若干(N)个cluster集合,每个集合包含一类像素。

  • 根据算法分为监督学习方法和无监督学习方法,图像分割的算法多数都是无监督学习方法 - KMeans

  • 从数学角度来看,图像分割是将数字图像划分成互不相交的区域的过程。图像分割的过程也是一个标记过程,即把属于同一区域的像索赋予相同的编号。目的是将图像中像素根据一定的规则分为若干(N)个聚(cluster)集合,每个集合包含一类像素。将对象在背景提取出来。
    在这里插入图片描述

距离变换与分水岭介绍

可以参考:https://blog.csdn.net/zhangjunp3/article/details/79672098

  • 在这里插入图片描述

  • 在这里插入图片描述

  • 在这里插入图片描述

距离变换常见算法有两种:

  • 不断膨胀/ 腐蚀得到
  • 基于倒角距离

分水岭变换常见的算法

  • 基于浸泡理论实现

可以参考:https://blog.csdn.net/zhu_hongji/article/details/81703350

相关API

  • (1)距离变换:计算源图像的每个像素到最近的零像素的距离.
    distanceTransform(
    InputArray src, //8位单通道(二进制)源图像
    OutputArray dst, //输出具有计算距离的图像,8位或32位浮点的单通道图像,大小与src相同.
    OutputArray labels, //输出二维数组标签labels(离散维诺Voronoi图),类型为 CV_32SC1 ,相同距离的算做同一个 label ,算出总共由多少个 labels
    int distanceType, //距离类型 = DIST_L1/DIST_L2
    int maskSize, //距离变换的掩膜大小,DIST_MASK_3(maskSize = 3x3),最新的支持DIST_MASK_5(mask = 5x5),//最新的支持5x5,推荐3x3、
    int labelType=DIST_LABEL_CCOMP //要生成的标签数组的类型
    )

    参数四距离可选类型:
    在这里插入图片描述
    参数六标签可选类型:在这里插入图片描述
  • (2)分水岭:用分水岭算法执行基于标记的图像分割
    watershed(
    InputArray image, //输入8位3通道图像(输入锐化原图8位3通道)
    InputOutputArray markers // 输入或输出32位单通道的标记,和图像一样大小。(输入高峰轮廓标记)
    )

处理流程

  1. 将白色背景变成黑色-目的是为后面的变换做准备
  2. 使用filter2D与拉普拉斯算子实现图像对比度提高,sharp
  3. 转为二值图像通过threshold
  4. 距离变换
  5. 对距离变换结果进行归一化到[0~1]之间
  6. 使用阈值,再次二值化,得到标记
  7. 腐蚀得到每个Peak - erode
  8. 发现轮廓 – findContours
  9. 绘制轮廓- drawContours
  10. 分水岭变换 watershed
  11. 对每个分割区域着色输出结果

演示代码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

程序代码

可以参考:https://blog.csdn.net/huanghuangjin/article/details/81194552

运行截图

参考博客

  1. https://blog.csdn.net/zhangjunp3/article/details/79672098
  2. https://blog.csdn.net/zhu_hongji/article/details/81703350
  3. https://blog.csdn.net/iracer/article/details/49225823
  4. https://blog.csdn.net/huanghuangjin/article/details/81194552
2017-11-19 17:09:28 tsien_sun 阅读数 1088

本次博客是我习得图像处理中的距离变换理论后,而作的算法实现笔记

首先得理解图像像素中的D4,D8邻接概念


距离变换算法,首先是将原图二值话,而后将图像中非0值无穷大化(这里指设置一个尽可能大的一个值)

在进行距离变换时,需要建立一个D8(8-邻接)模板,在变换过程中模板需要通过两种形式进行全图像扫描变换

第一次扫描:是需要从左向右 自上而下的方向进行的

将模板使用为如下形式

   

Fp点是运算的当前点

该模板匹配过程中需要计算AL1->AL4 各点叠加相对Fp点D4距离值,其结果与Fp点值组成一个5个元素的集合,

Fp最终取值为该集合内的最小值(公式的理解)。


第二次扫描:是需要从右向左 自下而上的方向进行,这与第一次的扫描方向完全相反

将模板使用为如下形式


对于公式与模板的理解与第一次扫描相同

‘==============================================================================================

实例是用excel做的,用VBA实现算法

规划《图像处理、分析与机器视觉(第3版)》P14页示例


在Excel中算法实现后的样子(与书本一致)



算法实现代码

Option Explicit
Dim MaxR As Integer
Dim MaxC As Integer
Dim Maximum As Integer
Dim Mat() As Integer

Sub DistanceFormat()
    MaxR = 8: MaxC = 8: Maximum = 30000
    Dim R As Integer, C As Integer
    'Step1:建立目标区域等同大小的数组模板
    ReDim Mat(1 To MaxR, 1 To MaxC) As Integer
    For R = 1 To MaxR 'Mat数组拷贝目标区域0值,非0值记为Maximum
        For C = 1 To MaxC
            Mat(R, C) = IIf(Cells(R, C) = "0", 0, Maximum)
        Next C
    Next R
    '====================================================
    For R = 1 To MaxR
        For C = 1 To MaxC
            AL_Region R, C
        Next C
    Next R
    '----------------------------------------------------
    For R = MaxR To 1 Step -1
        For C = MaxR To 1 Step -1
            BR_Region R, C
        Next C
    Next R
    '=====================================================
    For R = 1 To MaxR 'Mat数组拷贝目标区域0值,非0值记为Maximum
        For C = 1 To MaxC
            Cells(R, C) = Mat(R, C)
        Next C
    Next R
End Sub

Sub AL_Region(R As Integer, C As Integer)
    Dim MinV As Integer
    MinV = Mat(R, C)
    'AL1
    If C > 1 And R < MaxR Then '确保取值不过界
        MinV = IIf(Mat(R + 1, C - 1) + 2 < MinV, Mat(R + 1, C - 1) + 2, MinV)
    End If
    'AL2
    If C > 1 Then
        MinV = IIf(Mat(R, C - 1) + 1 < MinV, Mat(R, C - 1) + 1, MinV)
    End If
    'AL3
    If R > 1 And C > 1 Then
        MinV = IIf(Mat(R - 1, C - 1) + 2 < MinV, Mat(R - 1, C - 1) + 2, MinV)
    End If
    'AL4
    If R > 1 Then
        MinV = IIf(Mat(R - 1, C) + 1 < MinV, Mat(R - 1, C) + 1, MinV)
    End If
    '------------------------------------------------------------------
    Mat(R, C) = MinV
End Sub

Sub BR_Region(R As Integer, C As Integer)
    Dim MinV As Integer
    MinV = Mat(R, C)
    'BR1
    If R > 1 And C < MaxC Then '确保取值不过界
        MinV = IIf(Mat(R - 1, C + 1) + 2 < MinV, Mat(R - 1, C + 1) + 2, MinV)
    End If
    'BR2
    If C < MaxC Then
        MinV = IIf(Mat(R, C + 1) + 1 < MinV, Mat(R, C + 1) + 1, MinV)
    End If
    'BR3
    If C < MaxC And R < MaxR Then
        MinV = IIf(Mat(R + 1, C + 1) + 2 < MinV, Mat(R + 1, C + 1) + 2, MinV)
    End If
    'BR4
    If R < MaxR Then
        MinV = IIf(Mat(R + 1, C) + 1 < MinV, Mat(R + 1, C) + 1, MinV)
    End If
    '------------------------------------------------------------------
    Mat(R, C) = MinV
End Sub

实现一个更为复杂的实例:


运行并添加条件格式后的样子




2018-11-07 22:57:28 Godsolve 阅读数 425

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

根据度量距离的方法不同,距离变换有几种不同的方法,假设像素点为p1(x1, y1),p2(x2, y2),计算距离的方法常见的有:

  • 1.欧几里德距离
  • 2.曼哈顿距离(City Block Distance)
    Distance = |x2-x1|+|y2-y1|
  • 3.象棋格距离(Chessboard Distance)
    Distance = max(|x2-x1|,|y2-y1|)

而这个方法在官方网站上有可以直接使用的方法,于是我就用官网的方法来试了一下,结果如下:
在这里插入图片描述
这里要注意需要转为灰度图才可以进行下面的操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面的滑动条调节的是图片亮度,随着图片亮度增大,打火机的轮廓变得模糊,而原来没有出现的白色圆盒的轮廓开始出现。

图像距离算法还可以应用于目标细化,骨架提取,粘连物体的分离等,并不仅仅是表示出图像中目标与背景的距离。


同时也欢迎各位关注我的微信公众号 南木的下午茶

在这里插入图片描述


代码自取:

// CVE6.2.cpp: 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdio.h>

using namespace cv;

int maskSize0 = CV_DIST_MASK_5;
int voronoiType = -1;
int edgeThresh = 100;
int distType0 = CV_DIST_L1;

// The output and temporary images
Mat gray;

// threshold trackbar callback
static void onTrackbar(int, void*)
{
	static const Scalar colors[] =
	{
		Scalar(0,0,0),
		Scalar(255,0,0),
		Scalar(255,128,0),
		Scalar(255,255,0),
		Scalar(0,255,0),
		Scalar(0,128,255),
		Scalar(0,255,255),
		Scalar(0,0,255),
		Scalar(255,0,255)
	};

	int maskSize = voronoiType >= 0 ? CV_DIST_MASK_5 : maskSize0;
	int distType = voronoiType >= 0 ? CV_DIST_L2 : distType0;

	Mat edge = gray >= edgeThresh, dist, labels, dist8u;

	if (voronoiType < 0)
		distanceTransform(edge, dist, distType, maskSize);
	else
		distanceTransform(edge, dist, labels, distType, maskSize, voronoiType);

	if (voronoiType < 0)
	{
		// begin "painting" the distance transform result
		dist *= 5000;
		pow(dist, 0.5, dist);

		Mat dist32s, dist8u1, dist8u2;

		dist.convertTo(dist32s, CV_32S, 1, 0.5);
		dist32s &= Scalar::all(255);

		dist32s.convertTo(dist8u1, CV_8U, 1, 0);
		dist32s *= -1;

		dist32s += Scalar::all(255);
		dist32s.convertTo(dist8u2, CV_8U);

		Mat planes[] = { dist8u1, dist8u2, dist8u2 };
		merge(planes, 3, dist8u);
	}
	else
	{
		dist8u.create(labels.size(), CV_8UC3);
		for (int i = 0; i < labels.rows; i++)
		{
			const int* ll = (const int*)labels.ptr(i);
			const float* dd = (const float*)dist.ptr(i);
			uchar* d = (uchar*)dist8u.ptr(i);
			for (int j = 0; j < labels.cols; j++)
			{
				int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j] - 1) % 8 + 1;
				float scale = 1.f / (1 + dd[j] * dd[j] * 0.0004f);
				int b = cvRound(colors[idx][0] * scale);
				int g = cvRound(colors[idx][1] * scale);
				int r = cvRound(colors[idx][2] * scale);
				d[j * 3] = (uchar)b;
				d[j * 3 + 1] = (uchar)g;
				d[j * 3 + 2] = (uchar)r;
			}
		}
	}

	imshow("Distance Map", dist8u);
}

static void help()
{
	printf("\nProgram to demonstrate the use of the distance transform function between edge images.\n"
		"Usage:\n"
		"./distrans [image_name -- default image is stuff.jpg]\n"
		"\nHot keys: \n"
		"\tESC - quit the program\n"
		"\tC - use C/Inf metric\n"
		"\tL1 - use L1 metric\n"
		"\tL2 - use L2 metric\n"
		"\t3 - use 3x3 mask\n"
		"\t5 - use 5x5 mask\n"
		"\t0 - use precise distance transform\n"
		"\tv - switch to Voronoi diagram mode\n"
		"\tp - switch to pixel-based Voronoi diagram mode\n"
		"\tSPACE - loop through all the modes\n\n");
}

const char* keys =
{
	"{1| |stuff.jpg|input image file}"
};

int main(int argc, const char** argv)
{
	help();
	CommandLineParser parser(argc, argv, keys);
	//string filename = parser.get<string>("1");
	//gray = imread("stuff.jpg", 0);
	cv::Mat img = imread("E:/C++/CVE6.2/图片1.png");
	imshow("原图", img);
	gray = imread("E:/C++/CVE6.2/图片1.png", 0);
	imshow("灰度图", gray);
	resize(gray, gray, Size(), 0.25, 0.25, 1);
	//if (gray.empty())
	//{
	//	printf("Cannot read image file: %s\n", filename.c_str());
	//	help();
	//	return -1;
	//}

	namedWindow("Distance Map", CV_WINDOW_NORMAL);
	createTrackbar("Brightness Threshold", "Distance Map", &edgeThresh, 255, onTrackbar, 0);

	for (;;)
	{
		// Call to update the view
		onTrackbar(0, 0);

		int c = waitKey(0) & 255;

		if (c == 27)
			break;

		if (c == 'c' || c == 'C' || c == '1' || c == '2' ||
			c == '3' || c == '5' || c == '0')
			voronoiType = -1;

		if (c == 'c' || c == 'C')
			distType0 = CV_DIST_C;
		else if (c == '1')
			distType0 = CV_DIST_L1;
		else if (c == '2')
			distType0 = CV_DIST_L2;
		else if (c == '3')
			maskSize0 = CV_DIST_MASK_3;
		else if (c == '5')
			maskSize0 = CV_DIST_MASK_5;
		else if (c == '0')
			maskSize0 = CV_DIST_MASK_PRECISE;
		else if (c == 'v')
			voronoiType = 0;
		else if (c == 'p')
			voronoiType = 1;
		else if (c == ' ')
		{
			if (voronoiType == 0)
				voronoiType = 1;
			else if (voronoiType == 1)
			{
				voronoiType = -1;
				maskSize0 = CV_DIST_MASK_3;
				distType0 = CV_DIST_C;
			}
			else if (distType0 == CV_DIST_C)
				distType0 = CV_DIST_L1;
			else if (distType0 == CV_DIST_L1)
				distType0 = CV_DIST_L2;
			else if (maskSize0 == CV_DIST_MASK_3)
				maskSize0 = CV_DIST_MASK_5;
			else if (maskSize0 == CV_DIST_MASK_5)
				maskSize0 = CV_DIST_MASK_PRECISE;
			else if (maskSize0 == CV_DIST_MASK_PRECISE)
				voronoiType = 0;
		}
	}

	return 0;
}



2019-10-29 19:16:14 qq_33446100 阅读数 24

说在前面

  • 编译环境:vs2017
  • opencv版本:4.0.1
  • 参考:《图像处理、分析与机器视觉(第4版)》

概念

  • 数字图像

    对于一个图像函数f(x,y)f(x,y),若其定义域以及值域都是离散的,那么称之为数字的;
    通常,通过采样将连续图像进行数字化。
    在这里插入图片描述
    一个连续图像在采样点处被数字化;采样点之间构成的关系为栅格。
  • 距离

    • 定义
      满足以下条件的函数:
      D(p,q)0,p=q,D(p,q)=0D(p,q)=D(q,p)D(p,r)D(p,q)+D(q,r)D(\bold p,\bold q)\geq 0,当且仅当\bold p=\bold q时,D(\bold p,\bold q)=0\\ D(\bold p,\bold q)=D(\bold q,\bold p)\\ D(\bold p,\bold r)\leq D(\bold p,\bold q)+D(\bold q,\bold r)
    • 欧式距离
      DE[(i,j),(h,k)]=(ih)2+(jk)2D_E[(i,j),(h,k)]=\sqrt{(i-h)^2+(j-k)^2}
      在这里插入图片描述
    • 城市街区距离
      只允许横向以及纵向的移动
      在这里插入图片描述
      D4[(i,j),(h,k)]=ih+jkD_4[(i,j),(h,k)]=|i-h|+|j-k|
      在这里插入图片描述
    • 棋盘距离
      允许横向、纵向以及对角线上的移动
      在这里插入图片描述
      D8[(i,j),(h,k)]=max{ih+jk}D_8[(i,j),(h,k)]=max\{|i-h|+|j-k|\}
      在这里插入图片描述
  • 距离变换算法

    • 按照一种距离度量D,D是D4或者D8,对大小为MxN的图像的一个子集S进行距离变换,建立一个MxN的数组F并作初始化:子集S中的元素置为0,其他位置为无穷。
    • 按行遍历图像,从上到下,从左到右。对于上方和左边的邻接像素,设
      F(p)=min[F(p),D(p,q)+F(q)]F(\bold p)=min[F(\bold p), D(p,q)+F(q)]
      在这里插入图片描述
    • 按行遍历图像,从下到上,从右到左。对于下方和右边的邻接像素,设
      F(p)=min[F(p),D(p,q)+F(q)]F(\bold p)=min[F(\bold p), D(p,q)+F(q)]
      在这里插入图片描述
    • 数组F中得到的是子集S的斜切

Code

  • c++实现

    并未处理图像,仅用于展示算法;
    时间复杂度:O(n2)O(n^2)
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    /*
    @{func} 判断i,j是否在范围内
    */
    bool InArea(int i, int j, int rows, int cols)
    {
    	if (i<0 || i>=rows)
    		return false;
    	if (j<0 || j>=cols)
    		return false;
    
    	return true;
    }
    /*
    @{param: i j} 点位置
    @{param: rows cols} 图像大小
    @{param: f} 目标数组
    @{func} 处理上以及左边的近邻像素
    */
    void D4AL(int i,int j, int rows, int cols, vector<vector<int>> &f)
    {
    	//上
    	if (InArea(i - 1, j, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i - 1][j]);
    	//左上
    	if (InArea(i - 1, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i - 1][j - 1]);
    	//左
    	if (InArea(i, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i][j - 1]);
    	//左下
    	if (InArea(i + 1, j - 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i + 1][j - 1]);
    }
    /*
    @{param: i j} 点位置
    @{param: rows cols} 图像大小
    @{param: f} 目标数组
    @{func} 处理下以及右边的近邻像素
    */
    void D4BR(int i, int j, int rows, int cols, vector<vector<int>> &f)
    {
    	//下
    	if (InArea(i + 1, j, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i + 1][j]);
    	//右下
    	if (InArea(i + 1, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i + 1][j + 1]);
    	//右
    	if (InArea(i, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 1 + f[i][j + 1]);
    	//右上
    	if (InArea(i - 1, j + 1, rows, cols))
    		f[i][j] = min(f[i][j], 2 + f[i - 1][j + 1]);
    }
    
    /*
    @{param:src} 源图像
    @{param:f}   目标数组
    */
    void DistanceTransformD4(vector<vector<int>> &src, vector<vector<int>> &f)
    {
    	int cols = src[0].size();
    	int rows = src.size();
    
    	//初始化
    	for (int i = 0; i < rows; ++i)
    		for (int j = 0; j < cols; ++j)
    			if (src[i][j] == 1)
    				f[i][j] = 0;
    			else
    				f[i][j] = INT_MAX - 2;//简单的防止溢出
    	//按行遍历图像,从上到下,从左到右
    	for (int i = 0; i < rows; ++i)
    		for (int j = 0; j < cols; ++j)
    			D4AL(i, j, rows, cols, f);
    
    	//按行遍历图像,从下到上,从右到左
    	for (int i = rows - 1; i >= 0; --i)
    		for (int j = cols - 1; j >= 0; --j)
    			D4BR(i, j, rows, cols, f);
    }
    
    int main()
    {
    	vector<vector<int>> src = { 
    	{0,1,1,0,0,0,1,0 },
    	{0,1,0,0,0,0,0,1 },
    	{0,1,0,0,0,0,0,0 },
    	{0,1,0,0,0,0,0,0 }
    	};
    	int rows = src.size();
    	int cols = src[0].size();
    	vector<vector<int>> f(rows, vector<int>(cols, 0));
    
    	cout << "SRC:" << endl;
    	for (int i = 0; i < rows; ++i)
    	{
    		for (int j = 0; j < cols; ++j)
    			cout << src[i][j] << " ";
    		cout << endl;
    	}
    
    	DistanceTransformD4(src, f);
    
    	cout << "\nResult:" << endl;
    	for (int i = 0; i < rows; ++i)
    	{
    		for (int j = 0; j < cols; ++j)
    			cout << f[i][j] << " ";
    		cout << endl;
    	}
    
    	return 0;
    }
    
    在这里插入图片描述
  • opencv

    void cv::distanceTransform	(	
    InputArray 	src,
    OutputArray 	dst,
    OutputArray 	labels,
    int 	distanceType,
    int 	maskSize,
    int 	labelType = DIST_LABEL_CCOMP 
    )	
    
    栗子?
    在这里插入图片描述

应用

  • 移动机器人领域的路径规划以及障碍躲避
  • 图像中寻找最近特征,骨架抽取
  • 待补充
2019-04-11 02:10:11 qq_35306281 阅读数 221

@图像处理_距离变换_c++代码实现

距离变换:
分为:欧式距离,城区距离,棋盘距离。

算法步骤:

  1. 输入:二值图像。
  2. 从图像左上角第二行开始,从左向右、从上到下移动窗口扫描每个像素,类似滤波过程,检测在中心像素P的周围四个像素与中心像素的距离,若中心像素为0,则跳过。保存最小距离与位置作为结果。
  3. 从右下角倒数第二行开始,从右向左,从下到上扫描图像。其它同2。
  4. 输出图像。

代码如下:

/*created at 2019/4/11*/
#include <iostream>
#include <algorithm>
#include <opencv2/opencv.hpp>

void distance_transform_3x3(cv::Mat& src)
{
	int rows = src.rows;
	int cols = src.cols;
	float sum[5];
	/*第一次扫描*/
	for(int r = 1; r < rows - 1; ++r)
	{
		for(int c = 1; c < cols - 1; ++c)
		{
			if(src.at<uchar>(r,c))
			{
				sum[0] = 1.4142 + src.at<uchar>(r-1,c-1);
				sum[1] = 1      + src.at<uchar>(r-1,  c);
				sum[2] = 1.4142 + src.at<uchar>(r-1,c+1);
				sum[3] = 1	    + src.at<uchar>(r,  c-1);
				sum[4] =          src.at<uchar>(r,    c); 
				std::sort(sum, sum+5);
				src.at<uchar>(r,c) = sum[0];
			}
		}
	}
	/*第二次扫描*/
	for(int r = rows - 1; r > 0; --r)
	{
		for(int c = cols - 1; c > 0; --c)
		{
			if(src.at<uchar>(r,c))
			{
				sum[0] =          src.at<uchar>(r,    c);
				sum[1] = 1	    + src.at<uchar>(r,  c+1);
				sum[2] = 1.4142 + src.at<uchar>(r+1,c-1);
				sum[3] = 1      + src.at<uchar>(r+1,  c);
				sum[4] = 1.4142	+ src.at<uchar>(r+1,c+1);
				std::sort(sum, sum+5);
				src.at<uchar>(r,c) = sum[0];
			}
		}
	}
}

int main(int argc, char* argv[])
{
	cv::Mat src = cv::imread("test.jpg");
	distance_transform_3x3(src );
	cv::imshow("out.jpg", src );
	cv::waitKey(0);
	return 0;
}

原图:
在这里插入图片描述
输出:
在这里插入图片描述

图像处理之距离变换

阅读数 13433

没有更多推荐了,返回首页