树莓派循迹+图片识别(模板匹配)小车 C++

文末有有道翻译的版本,翻译的不准确的地方再来对照英文就清楚了
另外需要注意的是,在拟合到目标图像的四个角的位置后,对原图像进行投影变换再二值化进行模板匹配(而不是对二值化图像直接进行投影变换)效果要好不少
————

更新, 竟然有这么多人感兴趣,看来拿树莓派小车当课题的学校不少啊~
那我就在图片之间添加一些我认为比较重要的中文个人心得吧。
在这里插入图片描述
我用的是树莓派3b,两个串口里只有AMA0能用,另一个好像时钟源不太行,需要对系统进行更改,百度上一大把教程,我们学校提供的树莓派系统已经改好了,还带有opencv和codeblocks……

这里提到了python造成主板损坏,其实不是的(用脚指头想也不可能),问题是我们老师设计的H桥母版有问题,在和arduino进行通讯时产生了一些未知错误。总体来说,使用python对树莓派编程肯定是最方便简单的。
在这里插入图片描述
在刚开始我把图片转化成了灰度图,用灰度值来区分颜色,蠢到不行。除非你能保证整个地板的光是均匀反射的,否则灰度值完全不可靠。最可靠的还是HSV,我建议先写一个实时探测hsv的面板,用opencv自带的控件,来看地面上无论是黑线也好红线也好还是别的什么颜色的线,它们的hsv值到底是多少。磨刀不误砍柴工嘛。
在这里插入图片描述

在这里插入图片描述
网上大部分的循迹方法都是取一行像素来计算,可以是可以,但是小车速度比较快,路线比较曲折的情况下就很容易出错了,因为很容易冲过头。所以最好的办法是选择一个矩形区域内的所有像素点,计算左右两边的目标颜色的像素点个数差,再用这个差值进行PID计算是最好的。当然这个矩形大小要控制好,个人经验一般循环超过5K次的话,计算速度就赶不上小车速度了。所以可以先用resize函数把图片缩小一点。

在这里插入图片描述
这里介绍了均值滤波算法,没什么好说的,去除一些小噪点用的,如果你用的是一行像素点的方法,噪点的影响比较大,可以用这个算法滤波。但是如果用我建议的矩形区域方法,那小噪点的影响基本上就是微乎其微了,用不着滤波,找好HSV值就够了。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
边缘检测canny函数非常非常好用,因为单个的噪点是构不成边缘的,所以如果要滤波,与其选择那些杂七杂八的滤波算法,还不如选择边缘检测把整个图像二值化了。当然,像后期老师要求根据地面线颜色的变化来选择不同的路线,这个时候肯定就不能用边缘检测了。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在图像匹配中,最重要的依然是选择合适的HSV值进行二值化。要保证目标图像充分和周围的环境区分开来,所以我前面强烈建议先写探测HSV的控件面板,因为HSV值太重要了,直接影响到识别的精度。

在这里插入图片描述
二值化后就是拟合目标图形了,三角形有三个点,四边形有四个点,五边形就有五个点……找到这些点的位置过后就可以用投影变换把要匹配的图像“摆正”了。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
注意,找到点位后,对原图像进行投影变换会更合适,因为HSV二值化后目标图形肯定会有一点失真的,投影变换会进一步放大这个失真。把原图像变换后,再用resize把图片放大一点,再来匹配,成功率就会非常非常高了。

后面的内容是投影变换,模板匹配,形状拟合等等函数的代码,需要的自取。

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

在这里插入图片描述

在这里插入图片描述中文版本:

摘要

   这个实验的目的是用树莓派搭建一个小车,期望它能跟随黑线并识别特定的图像等等。利用OpenCV对摄像机图像进行处理,图像识别的主要方法是模板匹配。为了提高识别的准确性,需要在匹配之前对图像进行处理。

介绍

   研究人员已经在之前的实验中(使用Arduino)建造了这辆车。因此,最初的想法是用树莓派代替Arduino。Pi和MCU的不同之处在于Pi有1GB左右的内存,可以安装系统(甚至Windows)。因此,Pi几乎支持每一种编程语言。研究者从Moodle下载了系统,它已经配置了OpenCV。

研究者使用的Pi模型是3B,它有两个输出串口(ttyAMA0和ttyS0)。但ttyAMA0是开发人员分配给蓝牙模块的,ttyS0没有时钟源(波特率基于CPU时钟,不可靠)。因此,用户可以将系统文件改为使用ttyAMA0(官网的具体课程)。另一种方法是使用USB端口与CH340(一个小模块转换USB到TTL。有些型号甚至还有校正芯片)。研究人员测试了两种方法,发现USB端口比ttyAMA0更可靠(100次转换没有错误,而ttyAMA0有5到10个错误)。

研究人员已经利用PID控制和光传感器实现了线跟踪。在这个实验中,主要的挑战是图像处理。因此,研究者一开始就选择了python作为编程语言,它在图像处理方面比c++有更多的优势,而且OpenCV有一个完整的python
API。另外,研究者选择Arduino作为slave计算机直接控制车辆(一个愚蠢的想法)。不幸的是Arduino导致了底座板的故障,浪费了一天时间来修复。在这次事故后,研究者放弃了python(当时没有人知道是python或Arduino导致了故障)。

为了识别图像中的黑线,研究者首先要找到黑线的颜色值。OpenCV支持多种颜色标准,研究者先后测试了gray(单通道),RGB和HSV(三通道)。研究者首先选择灰度图像(简单且计算量低),黑到白对应0

  • 255。在这种情况下,只有一个数字被用来描述颜色。然而,由于光的反射,灰度值有时并不准确。在此情况下,本实验中考虑饱和的HSV更适合描述黑线或任何颜色。一旦识别出黑线,研究者就可以计算出线与图像中心的距离。另外,计算误差的算法很多,具体方法将在方法部分进行讨论。

行过之后,车辆应能识别图像。研究人员使用伺服来调整相机的位置(检测到粉色方块后,伺服旋转90度)。这个演示的难点在于摄像头没有直接面对图像,因此研究者首先需要重置图像的位置。而且很难找到准确的图像HSV值。

方法

直线跟踪算法

所有行跟踪算法的主要目的是找到其颜色值为黑色的像素。研究人员测试的第一种方法是使用numpy(一种用于科学计算的python库)查找一排图像中的所有黑色像素。首先,创建一个列表来存储一行像素:

选中的是200行。

然后使用numpy库找到所有的黑色像素:

Black_count = numpy。总和(颜色<
10)

Black_index = numpy。(颜色<
10)

在这段代码中,黑色像素的数量被记录为Black_count,位置也被记录为Black_index(一个二维数组)。黑色的灰度应该是零,然而,因为光线反射的效果,一些黑色像素的灰度大于零(在强光区域,它甚至可以超过50 !)

然后找到黑线的中心:

Center =
(Black_index[0][Black_count-1] + Black_index[0][0]) / 2

误差= 320 -中心

Black_index[0][0]是第一个找到的黑色像素的位置(从左到右),Black_index[0][Black_count-1]理论上是最终的黑色索引。因此,它们的中间应该是黑线的中心。在这种情况下,它与行中心的差值(图像的像素为640×320,所以中心应该是640/2 = 320)描述了车辆与黑线的距离。

如果没有检测到黑色像素,程序将报告错误并停止运行。因此,需要特殊处理:

试一试:

   Center = (Black_index[0][Black_count-1] +

Black_index[0][0]) / 2

误差=
320 -中心

除了:

   没有黑色像素//如果在尝试中发生错误,这一行工作。

但由于现实因素的影响,该算法的性能并不理想。最大的问题是图像中有一些噪点(一些像素本应该是白色却显示为黑色,本应该是黑色却显示为白色)。为了解决这一问题,研究者对图像进行了噪声消减和侵蚀。OpenCV提供了许多平滑算法,研究人员选择了平均滤波:blur()。

平均过滤和侵蚀的原理并不复杂。对于平均滤波,每个像素根据周围像素的平均值重新分配,并指定其范围。模糊函数有输入图像、输出图像和范围三个参数。

模糊(input_image output_image,大小)

   大小实际上是参与计算的面积。例如Size(3,3),即取3×3区域内像素的平均值,并将其分配到中心像素。在这种情况下,一些小的噪声点被删除(如灰尘或污点)。



   侵蚀与平均过滤相似。将图像分割成多个区域,每个区域的像素被赋值为其中的最小值。值最小的像素称为锚点,如下图所示:

























                                                  图(1)

在这种情况下,图像的亮度在侵蚀后会下降。黑暗的区域会变得更明显,而明亮的区域会减少甚至消失。因此,消除了黑线中的噪声点。此外,如果车辆沿着白线行驶,研究人员可以使用膨胀使图像更亮(与侵蚀相同,但采用最亮值)。结果部分将显示所有图像处理的效果。

   根据误差,采用PID进行误差计算和控制。在这个过程中,研究者考虑使用Arduino来计算PID和控制车辆,并通过USB数据线与Pi通信。在本例中,Arduino承担了部分计算任务。此外,Arduino还内置了AD/DA转换器,方便了其他模块的连接。但是如果Arduino将错误的信号传输到baseboard,就会造成损坏(研究者没有baseboard的代码,所以没有人知道它是怎么坏的)。在实验的最后,研究者再次尝试Python,发现使用Python的串行API是可行的(无论使用什么程序语言,串口都是通过ASCII来传输信号)。换了底板后,研究员回到c++。



   没有numpy库,研究人员使用循环找到黑色像素:



   for (int i

= 0;我< 640;我+
+)

   {

          如果((int) gray.at < uchar

(200年,我)<

                 {

                        Center = i;//检测到黑色像素            

                        打破;

                 }

   }



   该算法简单地从左到右对200行像素进行检测。一旦检测到黑色像素,黑色像素记录为黑线并退出循环。与上一种算法相比,该算法有更多的缺点。最大的问题是无法检测到尖锐的曲线。





                 图(2)



   在这种情况下,研究者做了一些改进:



   int i, j。

   (我=

0;< 640;我+ +)

   {

          如果((int) gray.at < uchar

(200年,我)<

                 {

                        打破;

                 }

   }



   (j = 640; >我;j——)

   {

          如果((int) gray.at < uchar

(200年,j) < 10)

                 {

                        中心= (j + i) / 2; 

                        打破;

                 }

   }



   该算法原理如下:





                                                     图(3)



   研究人员用这个算法完成了生产线的演示,但是,它的表现不够好。因此,提出了最终的算法。平均滤波后进行边缘检测(canny),找到黑线。

   canny的原理是计算像素梯度的值和方向,从而找到强边缘及其方向,与Sobel算子相同。canny的改进是将梯度方向上不大于像素的边缘去除,从而细化了边缘。计算结果如下:

像素的卷积系数:

                                                 图(4)

集成和梯度:

                                                    图(5)







                                             图(6)

方向:

                                      图(7)

结果:

                                                    图(8)

然后将边缘上的像素与梯度方向上的像素进行比较,低于梯度方向的像素将被消除:

                               图(9)



   例如,如果图(9)中的x为3,梯度方向为90度(从左到右)。因为x=3和x<4,它被赋值为0。在这种情况下,只保留值最大的像素,并对边缘进行细化。



   最后的算法如下图所示:

int left_count = 0;

int right_count = 0;

调整大小(灰度,160,60)//调整图像大小以减少计算量

(int i = 20,我< 30;我+
+)

{

for (int j = 0; < 80; j

  • +)

            {
    

If
((int)gray.at(i,j)==255) //canny改变黑线为//白色

                 {

                        left_count + +;

                 }

          }

for (int j = 80; j <
160; + +)

          {

                 如果((int) gray.at < uchar

(i, j) = = 255)

                 {

                        right_count + +;

                 }

          }

}

Center = left_count - right_count

   在该算法中,研究者选择一个区域并找到其中所有的黑色像素点。然后分别记录左侧和右侧的黑色像素个数。left_count和right_count的差值表示偏移(如果黑线的中心正好在图像的中心,那么left_count应该等于right_count)。



   此外,有时车辆仍然不能跟随急转弯,因为转弯速度太低,黑线超出了相机的视野。在这种情况下,研究者增加了一个新的参数来记录黑线的方向:

int left_count = 0;

int right_count = 0;

int line = 0;

调整大小(灰度,160,60)//调整图像大小以减少计算量

(int i = 20,我< 30;我+
+)

{

for (int j = 0; < 80; j

  • +)

            {
    

If ((int)gray.at(i,j)==255)
//canny改变黑线为//白色

                 {

                        left_count + +;

                 }

          }

for (int j = 80; j <
160; + +)

          {

                 如果((int) gray.at < uchar

(i, j) = = 255)

                 {

                        right_count + +;

                 }

          }

}

Center = left_count - right_count

如果(中心>
0)

   {

          线= 1;//黑线倾向于被留下                 

   }

其他的

   {

          黑线趋向于正确                    

   }

if (left_count0&&right_count0)//无黑线,继续旋转

   {

          if (line == 1)

                 {

                        Vehicle_turn_left ();

}

          其他的

                 {

                        Vehicle_turn_right ();

                 }

   }

其他的

   {

          PID ();

   }



   黑线的趋势用一个整数变量“线”来记录。一旦黑线离开了相机的视野,车辆就会转向,直到再次找到黑色像素为止(转向的方向取决于“线”的值)。另一种方法是用退却代替“转身”,这种方法非常可靠,几乎不会出错。唯一的缺点是不够流利:



   如果(left_count = = 0

&&right_count = = 0)

   {

          Vehicle_retreat ();

   }

另一个演示是“快捷键”。一些不同颜色的曲线被放置作为捷径。期望车辆走捷径,不同的捷径对速度的要求不同。为此,研究人员在黑线之前添加了颜色检测:

int color = 0;

for (int j = 0; < 80; j + +)

   {

   (int)hsv.at<vector3b>(20,j)[0];

(int)hsv.at(20,j)[1];

(int)hsv.at(20,j)[2];

如果(pixel was red) //HSV更适合寻找特定的颜色

                 {

                        颜色=

1;

                        打破;

                 }

   if(像素为蓝色)

          {

                        颜色= 2;

                        打破;

          }

/ /重复……

}

然后用不同的“Color”值编写相应的代码。

最后的演示是图像识别,并完成了相应的任务,如距离测量(使用HC_SR04)和踢球等。伺服拿着相机,一旦发现粉色方块,伺服就会举起相机。本报告将简单介绍HC_SR04,主要介绍图像识别。

HC_SR04是一个超声波测距模块。它有四个引脚:VCC, GND, TRIG和ECHO。三角函数是波发射的触发器。如果给它一个高电平信号(至少10美元),这个模块就会产生超声波,并将回声设置为低电平,直到它收到声波的反馈。因此,记录回声变化的时间可以简单地计算出距离。代码:

include < stdio . h >

include < wiringPi >

include < sys / time.h >

#定义回声2

#定义三角3

wiringPiSetup ();

pinMode(呼应,输入);

pinMode(三角、输出);

浮动说;

struct timeval t1;

struct timeval t2;

长start_time;

长end_time;

而(1)

{

digitalwrite(三角,0);

delayMicroseconds (2);

digitalWrite(三角,1);

delayMicroseconds (10);

digitalWrite(三角,0);

而(! (digitalRead (Echo) = =
1));

gettimeofday (t1, NULL);

而(! (digitalRead (Echo) = =
0));

gettimeofday (t2, NULL);

start_time = t1.tv_sec *
1000000 + t1.tv_usec;

end_time = t2.tv_sec *
1000000 + t2.tv_usec;

说=(浮动)*
0.017;

printf("距离=
% dcm \ n”,说);

}

图象识别

图像识别的方法是模板匹配。在这个过程中,有两个主要的挑战。第一个是视角转换。为了做到这一点,研究人员必须找到四个点来决定转换的区域。第二个挑战是尽可能准确地找到图像的HSV值。

图(10)图(11)

   如图(10)和(11)所示,图像由粉色框架和粉色符号组成。在这种情况下,研究者可以使用find等值线()函数来查找图像的位置。

findcontour()实际上是Satoshi
Suzuki提出的一种边缘检测方法。与坎尼不同的是,它只适用于二值图像。校长也差不多:

                                                 图(12)



   如图(12)所示,“frame”是最外面的frame,

background是由0组成的组件。“洞”是1区域内的0区域。“外边”是第一个边,“洞边”是第二个边。在本例中,查找外边界和孔边界:

        外边界的起点



 孔边界的起始点



   找到边界后,用编码对像素进行标记,称为NBD。当找到新的边时,NBD
  • 1。如果满足f(p,q)=1且f(p,q+1)=0(这意味着边的结束)的点,设该点为-NBD。用这种方法,可以找到边缘。

     在这种情况下,研究者使用inRange()函数进行图像二值化(仅保留粉色):
    
    
    
    
    
    
    
                   图(13)
    
    
    
     然后研究者创建一个对偶向量来存储边缘:
    

垫框架;

帧= imread (“img.jpg”);

std::矢量< <上>

轮廓;

std::向量< Vec4i >层次结构;

findContours(框架、轮廓、层次结构、0,CHAIN_APPROX_NONE);

   创建一个黑色图像绘制边缘:

垫dstImg (frame.size ()、CV_8UC3标量::(0));

drawContours (dstImg contours_poly,标量(0255255),2、8);

                                             图(14)



   然后研究者要找出四个角的点:













           图(15)



   为此,研究者使用了approxPolyDP()函数,该函数使用Douglas-Pucker算法。该算法利用一系列点对曲线进行近似描述。它的原理是:



   1、在曲线的开始和结束点a和点B之间连接一条直线AB,这就是曲线的弦

   2、求出曲线上与线段距离最大的点C。计算从C到直线AB的距离

   3、将距离与预设的阈值进行比较。如果小于阈值,则将直线段视为曲线的近似值,完成该段的处理。

   4、如果距离大于阈值,则用C将曲线划分为AC和BC两段,分别对这两段进行1~3处理。

   5、处理完所有的曲线后,将每个分割点依次连接起来形成的折线作为曲线的近似。







          图(16)



   因此,需要一个对偶向量(点类型)来存储函数的返回值。

代码:

std::矢量< <上>

contours_poly (contours.size ());

approxPolyDP(垫(轮廓[我]),contours_poly[我],30岁,真的);

   对于四边形,approxPolyDP()的返回应该是四个向量。因此,创建一个大小为4的向量来存储角的位置:

std::向量<点>
finalContours (4);

for (int i = 0;我< 4,我+
+)

{

finalContours[我]=
contours_poly[0][我];

}

   如果存在一些噪声点(一条以上的边),则需要检查每个向量的大小,找出大小为4的那个。



   最后利用透视变换对图像的位置进行校正。其方程为:

          



                                             图(17)

   u和v为原图像,对应x和y(经过变换后):





          图(18)

   

   该矩阵可分为四部分:

线性:锅:角度:

    图(18)图(19)图(20)                                          

此时,方程可以写成:

                 图(21)



   透视转换的代码在报告的最后。



   经过这些图像处理后,可以与模板图像进行匹配:

float compareImages(Mat cameraImage, Mat librarySymbol)

{

(2(float)countNonZero(librarySymbol^cameraImage)));

返回matchPercent;

}

浮动议员;

MP = compareImages(图像,模板);

·
如果MP大于阈值(0.75),匹配成功。值得注意的是,在这个阶段图像需要适当放大以提高精度。

RESAUT

   效果和结果将在这部分显示为图像。

平均滤波:

     大小(6,6)大小(12日12)大小(20、20)                                                         










































          图(22)图(23)图(24)                                                                 



   如图所示,尺寸越大,图像越模糊。有一会儿,吵闹的地方被压抑了。

侵蚀:

                 图(26)图(25)                                                              

膨胀:

                 图(27)图(28)                                                              

灰色:

            图(29)图(30)                                                            

HSV:

            图(31)图(32)                                                            

精明的:

            图(33)图(34)                                                            

坎尼最小化了光线的影响!

视角转换:

                                                    图(35)

讨论

  在这项实验中,研究人员在最初几天没有使用HSV。显然,在明亮的环境下,灰色不能准确地描述颜色。此外,很难找到合适的HSV值用于图像识别。模板匹配本身的缺点也限制了匹配成功率。

为了改进,级联分级机是更好的选择。研究人员可以从不同角度收集图像的照片,不需要使用透视变换,更加方便。

结论

   在本实验中,研究人员很不幸地完成了黑线跟踪、捷径和少量的图像识别演示。但是,研究人员学习了不同的颜色标准,几种图像处理算法,并逐步提高了编码的性能。

参考

铃木s和安倍K。,利用边界跟踪法对数字化二值图像进行拓扑结构分析。CVGIP 30
1,第32-46页(1985)

perspective transformation 的代码:
在这里插入图片描述

a
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

template matching 的代码:

在这里插入图片描述

  • 22
    点赞
  • 133
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
以下是一份使用树莓派摄像头寻黑线的小车代码: ```python import cv2 import RPi.GPIO as GPIO import time # 定义引脚 left_motor_forward_pin = 11 left_motor_backward_pin = 12 right_motor_forward_pin = 13 right_motor_backward_pin = 15 # 初始化GPIO GPIO.setmode(GPIO.BOARD) GPIO.setup(left_motor_forward_pin, GPIO.OUT) GPIO.setup(left_motor_backward_pin, GPIO.OUT) GPIO.setup(right_motor_forward_pin, GPIO.OUT) GPIO.setup(right_motor_backward_pin, GPIO.OUT) # 初始化摄像头 cap = cv2.VideoCapture(0) # 定义小车运动函数 def car_move(left_speed, right_speed): # 控制左右轮的转速 if left_speed >= 0: GPIO.output(left_motor_forward_pin, GPIO.HIGH) GPIO.output(left_motor_backward_pin, GPIO.LOW) left_pwm.ChangeDutyCycle(left_speed) else: GPIO.output(left_motor_forward_pin, GPIO.LOW) GPIO.output(left_motor_backward_pin, GPIO.HIGH) left_pwm.ChangeDutyCycle(-left_speed) if right_speed >= 0: GPIO.output(right_motor_forward_pin, GPIO.HIGH) GPIO.output(right_motor_backward_pin, GPIO.LOW) right_pwm.ChangeDutyCycle(right_speed) else: GPIO.output(right_motor_forward_pin, GPIO.LOW) GPIO.output(right_motor_backward_pin, GPIO.HIGH) right_pwm.ChangeDutyCycle(-right_speed) # 初始化PWM left_pwm = GPIO.PWM(left_motor_forward_pin, 100) left_pwm.start(0) right_pwm = GPIO.PWM(right_motor_forward_pin, 100) right_pwm.start(0) # 小车沿着黑线行驶 try: while True: # 读取摄像头图像 ret, frame = cap.read() if not ret: break # 将图像转换为灰度图像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 对灰度图像进行二值化处理 ret, binary = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY) # 查找轮廓 contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 如果没有找到轮廓,保持直行 if len(contours) == 0: car_move(50, 50) else: # 查找最大的轮廓 max_contour = max(contours, key=cv2.contourArea) # 计算轮廓的中心点 M = cv2.moments(max_contour) cx = int(M['m10'] / M['m00']) # 根据中心点位置来控制小车的转向 if cx < 200: car_move(-30, 30) elif cx > 440: car_move(30, -30) else: car_move(50, 50) # 显示图像 cv2.imshow('frame', frame) cv2.waitKey(1) except KeyboardInterrupt: pass # 清理GPIO GPIO.cleanup() ``` 这份代码使用了树莓派摄像头来获取图像,并对图像进行处理以检测黑线。具体来说: - 将图像转换为灰度图像; - 对灰度图像进行二值化处理,将黑线变为白色,其他区域变为黑色; - 查找图像中的轮廓,找到最大的轮廓; - 计算轮廓的中心点位置,根据中心点位置来控制小车的转向和转速。 该代码中,使用的是简单的阈值二值化方法和查找最大轮廓方法,实际应用中可能需要更复杂的图像处理算法来实现更精确的控制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值