2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

2021全国大学生电子设计竞赛F题智能送药小车

前提:本篇文章重在分享自己的心得与感悟,我们把最重要的部分,摄像头循迹,摄像头数字识别问题都解决了,有两种方案一种是openARTmini摄像头进行数字识别加寻迹,即融合代码。另一种是使用openmv4进行数字识别(使用的是模板匹配),然后利用灰度传感器进行寻迹。因为当时python用得不算很熟,最终我们选择了第二种方案使open MV4实现数字识别,灰度传感器寻迹,在控制智能车运动调试的过程中更加简单。当然赛后我们也尝试了使用open ARTmini的方案,同样操作容易。其次我们下来也做了方案三K210数字识别,数字识别率可达97.8%,使用openmv寻迹。

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

人们常常希望向成功者获取经验,可是生活中哪有那么多成功者,我想只有失败者才最有发言权,最有经验可以分享吧。因为某些原因我们无缘国奖,但我不甘是失败者,我们为此付出了多少,坚持了多少,只有我们自己知道, 只要全力以赴就无所谓失败,也许多年后再回过头来看,想起来这些热血我依然能够热泪盈眶,那就无悔了。
追求卓越,成功才会在不经意见追上你!

目录:
一、题目分析
二、分工以及小车的搭建
三、摄像头部分
四、控制部分
五、联调
六、随谈
七、工程代码

备注:
只需要工程代码的同学(如下是此次电赛过程中所有的代码),建议先看完正文
openMV4模板匹配数字识别方案,本次电赛所有工程代码包括论文报告
https://download.csdn.net/download/cubejava/79012510
openmv巡线代码:
https://download.csdn.net/download/cubejava/41873305
stm32HAL库keil工程(配置freeRTOS,巡线,自动返回,定点停车):
https://download.csdn.net/download/cubejava/41871669
openARTmini巡线和数字识别的融合代码:
https://download.csdn.net/download/cubejava/79012645
小车底板AD原理图和pcb工程:
https://download.csdn.net/download/cubejava/42512528

(可私聊提供技术支持,代码注释完善,若有不懂的可手把手教学)

; 正文

一、题目

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
我们是做测控方向的,当时在4号早上7:30左右题目就出来了,随即就有人发在了群里。2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
看了一下测控方向除了无人机能选的也就D题和F题了,D题是基于互联网的摄像测量系统(D题),我们对这方面的了解不多,没有什么把握,就没敢选,后来听说选D题的在GitHub上能够找到源码,做到后面车调不动时倒有点小后悔,哈哈。

F题经典小车题目,但与往年不同的是今年识别和控制都是重点,必须要先能做到识别,你才能谈接下来的循迹和运动控制。
谈到识别就需要用到摄像头,但之前我们对于摄像头并没有太重视,循迹一般用的都是红外对管,灰度传感器,或者线性CCD就足够了,但是这次的赛到元素存在十字,黑白色块,还要求能自动返回,使用普通的循迹模块就比较吃力了。在电赛备赛期间就做过线性CCD寻迹的智能车,使用的stm32f4,HAL库,cube MX配置的。采取了二值化,动态阈值算法,将CCD采集的值滤波,导入PID,再加入速度环,实现串级PID。也算是练了练手。

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
工程链接:https://download.csdn.net/download/cubejava/79012959

; 二、分工以及小车的搭建

我们组三人都没有玩过摄像头,要立马开始现学,我们一开始准备使用openmv进行循迹和数字识别,后来发现我们的openmv的版本不能训练神经网络,必须要openmvplus(可是没有,买又来不急送到),就很难受。
于是我们想采用openARTMiNi进行数字识别加循迹,但由于python用得不熟,当时没有足够的把握能够实现。所以我们采取了折中的办法,即文章开头说的,openMV使用模板匹配识别数字,寻迹使用灰度传感器。
其中一位学长着手完成openMV模板匹配识别数字,另和我则负责stm32f4的工程创建小车的底层代码和运动控制,使用的HAL库。
由于之前备赛准备的东西比较充分,所以当天上午就把车给搭建好了,使用平衡小车,加了一个万向轮,转弯差速控制。

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
小车硬件:
STM32F411CEU6,TB6612,车模(自带霍尔编码器减速电机),LM2596,MPU6050,航模电池,openMV,K210
使用AD画小车底板。原理图和PCB如下
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

小车软件:
使用的HAL库建立工程,在cubeMX中配置freeRTOS操作系统,

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
KEIL工程程序代码:
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

串口重定向printf


int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart6, (uint8_t *)&ch, 1, 1000);
  return ch;
}

编码器模式取值


int Read_Encoder(uint8_t TIMX)
{
   int Encoder_TIM;
   switch(TIMX)
     {
       case 1:  Encoder_TIM= (short)TIM1 -> CNT;  TIM1 -> CNT=0;break;
         case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;

         default:  Encoder_TIM=0;
     }
        return Encoder_TIM;
}

判断mpu6050初始化是否成功

void Mpu6050_Init()
{
    while(w_mpu_init() != mpu_ok)
    {
        printf("0x%x (ID_ERROR)\r\n", w_mpu_init());
        HAL_Delay(10);
    }

    dmp_init();
}

初始化程序

int main(void)
{

    HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);
    HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);

    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);

    HAL_TIM_PWM_Start(&htim5,TIM_CHANNEL_3);
    HAL_TIM_PWM_Start(&htim5,TIM_CHANNEL_4);

    HAL_UART_Receive_IT(&huart1,(uint8_t *)&usart6_rxbuff,1);

    Temporary();
    Mpu6050_Init();

    HAL_TIM_Base_Start_IT(&htim4);
    HAL_UART_Transmit(&huart6, (uint8_t *)&cc, 1,0xFFF);

    MX_FREERTOS_Init();
    HAL_UART_Transmit(&huart6, (uint8_t *)&cc, 1,0xFFF);

    osKernelStart();
   while (1)
  {

  }

}

freeRTOS.C文件中的代码

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

巡线和功能实现代码均放在freeRTOS.C中

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

三、摄像头部分

有关摄像头部分,我们在第一天尝试过单使用openMV寻迹,但神经网络训练不了就弃置了。
在openmv中处理好数据,可以寻红线,识别十字,识别黑白色块,
传回来的数据:
左右线传回为模拟量,传入单片机,进行pid控制

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
openMV模板匹配识别数字:
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
可实现一排识别多个数字。
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

; 四、控制部分

控制部分,巡线使用的PD控制

首先速度闭环,PI控制,让小车保持在一个稳定的速度

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
在速度环上叠加一层方向环,PD控制,巡线
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
int track_control(float collect_openmv)
{
    static float last_bias,Bias,kp=0.75,Kd=0.25;
    float Turn;
    Bias=-(collect_openmv-60);
    Turn=-Bias*kp-Kd*(Bias-last_bias);
    last_bias = Bias;

    return Turn;
}

小车的自动返回,和判定十字转弯,黑白色块停车使用了标志位,定义数组,根据出栈入栈原理,记录去回的方向。

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

五、联调

openmv和K210的数据传入单片机
打开串口中断,注意需要写好摄像头和单片机之间的通信协议,如下:

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    uint16_t tem;
    if(huart->Instance == USART1)
    {

        tem = usart6_rxbuff;
        printf("%x\r\n",tem);
        if(RxState==0&&tem==0x79)
        {
            RxState=1;
            RxBuffer1[RxCounter1++]=tem;
        }
        else if(RxState==1)
        {
            RxBuffer1[RxCounter1++]=tem;
            if(RxCounter1>=10||tem == 0x85)
            {
                RxState=2;
            }
         }
         else if(RxState==2)
         {
                if(RxBuffer1[RxCounter1-1] == 0x85)
                {
                        if(RxBuffer1[1] == 0xaa)
                        {
                            Flag = Flag + 1;

                            JG=RxBuffer1[RxCounter1-2];
                            Turn = RxBuffer1[RxCounter1-3];
                            if(Flag>3)
                            {
                                if(JG>0&&FS==0) {FS = JG;}
                                printf("\r\n Turn:%d FS:%d  \r\n",Turn,FS);
                            }

                        }
                        if(RxBuffer1[1] == 0xbb)
                        {
                            printf("\r\n___sou xian___\r\n");
                            printf("\r\n Cx:%d, STOP:%d \r\n",Cx,STOP);
                            Cx=RxBuffer1[RxCounter1-2];

                            STOP=RxBuffer1[RxCounter1-3];
                        }

                            RxCounter1 = 0;
                            RxState = 0;

                }
                else
                {
                            RxState = 0;
                            RxCounter1=0;
                            for(i=0;i<10;i++)
                            {
                                    RxBuffer1[i]=0x00;
                            }
                            printf("接收错误\r\n");
                }
            }
         if(state==2)
         {
             if(STOP == 0)
             {
                 state = 0;
             }
         }
         if(state==4)
         {
             if(STOP == 0)
             {
                 state = 0;
             }
         }
    }
    HAL_UART_Receive_IT(&huart1, (uint8_t *)&usart6_rxbuff, 1);
}

六、随谈

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)
我们是在结束的前一天晚上完成好的数字识别,离调车整个程序的逻辑就只有一天的时间,说真的,程序写少了,这个程序逻辑真的很难想出来,尤其是在当时已经连续三天每天只睡2个小时左右,头脑非常混沌,呆滞。
还是要多写程序,练习自己写程序的逻辑思维。

结尾:第一次参加国赛,也是第一次参加电赛,本来应该是大一结束就开始了的,电赛延期拖到了大二开学后的一段时间,自己需要学的内容还有很多,好好学习,提升自己的技术,明年省赛继续冲!

; 七、 工程代码:

openMV4模板匹配数字识别方案,本次电赛所有工程代码包括论文报告
https://download.csdn.net/download/cubejava/79012510
openmv巡线代码:
https://download.csdn.net/download/cubejava/41873305
stm32HAL库keil工程(配置freeRTOS,巡线,自动返回,定点停车):
https://download.csdn.net/download/cubejava/41871669
openARTmini巡线和数字识别的融合代码:
https://download.csdn.net/download/cubejava/79012645
小车底板AD原理图和pcb工程:
https://download.csdn.net/download/cubejava/42512528

备注:这些都是我们没日没夜熬出来的程序代码,花费了很多精力,所以没有免费开源(望理解),设置了付费,不过在博客里也写了相关的思路,可以参考借鉴一些经验,来年省赛加油!!!
现在电赛越来越趋向于视觉和人工智能了,一定要好好学习摄像头,视觉,神经网络。

Original: https://blog.csdn.net/cubejava/article/details/121274043
Author: 也无风雨也无晴W
Title: 2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)



相关阅读

Title: 大津阈值分割(OSTU)

文章首发于我的个人博客

大津法是一种灰度图像自适应阈值分割算法,是日本学者Ostu于1979年提出,又称类间方差阈值分割法。大津法根据图像的灰度分布将图像分为前景和背景两部分,前景是我们分割出来的部分。前景和背景的分割值就是我们要通过类间方差法求出的阈值。

一、算法原理

设图像灰度级为L,灰度级为i的像素点数为ni,那么直方图分布为:
p i = n i / N , ∑ i = 0 L − 1 p i = 1 p_i = n_i /N, \sum_{i=0}^{L-1}p_i=1 p i ​=n i ​/N ,i =0 ∑L −1 ​p i ​=1
按灰度级用阈值t划分为两类:C_0=(0,1,…,t)和C_1=(t+1,t+2…,L-1)。因此C_0类和C_1类的概率级均值分别由一下各式给出:
w 0 = P r ( C 0 ) = ∑ i = 0 k p i w_0=Pr(C_0)=\sum_{i=0}^{k}p_i w 0 ​=P r (C 0 ​)=i =0 ∑k ​p i ​

w 1 = P r ( C 1 ) = ∑ i = k + 1 L − 1 p i = 1 − w 0 w_1=Pr(C_1)=\sum_{i=k+1}^{L-1}p_i = 1-w_0 w 1 ​=P r (C 1 ​)=i =k +1 ∑L −1 ​p i ​=1 −w 0 ​

u 0 = ∑ i = 0 k i P r ( i ∣ C 0 ) = ∑ i = 0 k i p i / w 0 u_0=\sum_{i=0}^{k}iPr(i|C_0)=\sum_{i=0}^{k}ip_i/w_0 u 0 ​=i =0 ∑k ​i P r (i ∣C 0 ​)=i =0 ∑k ​i p i ​/w 0 ​

u 1 = ∑ i = k + 1 L − 1 i P r ( i ∣ C 1 ) = ∑ i = k + 1 L − 1 i p i / w 1 u_1=\sum_{i=k+1}^{L-1}iPr(i|C_1)=\sum_{i=k+1}^{L-1}ip_i/w_1 u 1 ​=i =k +1 ∑L −1 ​i P r (i ∣C 1 ​)=i =k +1 ∑L −1 ​i p i ​/w 1 ​

u T = ∑ i = 0 L − 1 i p i u_T=\sum_{i=0}^{L-1}ip_i u T ​=i =0 ∑L −1 ​i p i ​

可以看出,对任何t值,下式都能成立:
w o u 0 + w 1 u 1 = u T , w 0 + w 1 = 1 w_ou_0+w_1u_1=u_T,w_0+w_1=1 w o ​u 0 ​+w 1 ​u 1 ​=u T ​,w 0 ​+w 1 ​=1
C_0和C_1类的方差可由下式求得:
σ 0 2 = ∑ i = 0 t ( i − u 0 ) 2 p i / w 0 \sigma_0^2=\sum_{i=0}^{t}(i-u_0)^2p_i/w_0 σ0 2 ​=i =0 ∑t ​(i −u 0 ​)2 p i ​/w 0 ​

σ 1 2 = ∑ i = t + 1 L − 1 ( i − u 1 ) 2 p i / w 1 \sigma_1^2=\sum_{i=t+1}^{L-1}(i-u_1)^2p_i/w_1 σ1 2 ​=i =t +1 ∑L −1 ​(i −u 1 ​)2 p i ​/w 1 ​

由此便可定义,类内方差(within):
σ w 2 = w 0 σ 0 2 + w 1 σ 1 2 \sigma_w^2=w_0\sigma_0^2+w_1\sigma_1^2 σw 2 ​=w 0 ​σ0 2 ​+w 1 ​σ1 2 ​
类间方差(Between):
σ B 2 = w 0 ( u 0 − u T ) 2 + w 1 ( u 1 − u T ) 2 = w 0 w 1 ( u 1 − u 0 ) 2 \sigma_B^2=w_0(u_0-u_T)^2+w_1(u_1-u_T)^2=w_0w_1(u_1-u_0)^2 σB 2 ​=w 0 ​(u 0 ​−u T ​)2 +w 1 ​(u 1 ​−u T ​)2 =w 0 ​w 1 ​(u 1 ​−u 0 ​)2
总体方差:
σ T 2 = σ B 2 + σ w 2 \sigma_T^2=\sigma_B^2+\sigma_w^2 σT 2 ​=σB 2 ​+σw 2 ​
何为类间方差?我们将灰度级L按t非为两类,反应在直方图上就是两类灰度。我们要做的就是找到能使两边灰度差的最大的阈值,根据这个阈值对灰度图像进行分割。因此我们要做的就是找到能使类间方差最大的t值。

二、类间方差推导

σ B 2 = w 0 ( μ 0 − μ T ) 2 + w 1 ( μ 1 − μ T ) 2 = w 0 [ μ 0 − w 0 μ 0 − w 1 μ 1 ] 2 + w 1 [ μ 1 − w 0 μ 0 − w 1 μ 1 ] 2 = w 0 [ ( 1 − w 0 ) μ 0 − w 1 μ 1 ] 2 + w 1 [ ( 1 − w 1 ) μ 1 − w 0 μ 0 ] 2 = w 0 [ w 1 μ 0 − w 1 μ 1 ] 2 + w 1 [ w 0 μ 1 − w 0 μ 0 ] 2 = w 0 w 1 ( μ 0 − μ 1 ) 2 + w 0 w 1 ( μ 1 − μ 0 ) 2 = ( μ 1 − μ 0 ) 2 w 0 w 1 ( w 0 + w 1 ) = w 0 w 1 ( μ 1 − μ 0 ) 2 {\sigma_B}^2=w_0(\mu_0-\mu_T)^2+w_1(\mu_1-\mu_T)^2 \ = w_0[\mu_0-w_0\mu_0-w_1\mu_1]^2+w_1[\mu_1-w_0\mu_0-w_1\mu_1]^2 \ = w_0[(1-w_0)\mu_0-w_1\mu_1]^2+w_1[(1-w_1)\mu_1-w_0\mu_0]^2 \ =w_0[w_1\mu_0-w_1\mu_1]^2+w_1[w_0\mu_1-w_0\mu_0]^2 \ =w_0w_1(\mu_0-\mu_1)^2+w_0w_1(\mu_1-\mu_0)^2 \ =(\mu_1-\mu_0)^2w_0w_1(w_0+w_1)\ =w_0w_1(\mu_1-\mu_0)^2 σB ​2 =w 0 ​(μ0 ​−μT ​)2 +w 1 ​(μ1 ​−μT ​)2 =w 0 ​[μ0 ​−w 0 ​μ0 ​−w 1 ​μ1 ​]2 +w 1 ​[μ1 ​−w 0 ​μ0 ​−w 1 ​μ1 ​]2 =w 0 ​[(1 −w 0 ​)μ0 ​−w 1 ​μ1 ​]2 +w 1 ​[(1 −w 1 ​)μ1 ​−w 0 ​μ0 ​]2 =w 0 ​[w 1 ​μ0 ​−w 1 ​μ1 ​]2 +w 1 ​[w 0 ​μ1 ​−w 0 ​μ0 ​]2 =w 0 ​w 1 ​(μ0 ​−μ1 ​)2 +w 0 ​w 1 ​(μ1 ​−μ0 ​)2 =(μ1 ​−μ0 ​)2 w 0 ​w 1 ​(w 0 ​+w 1 ​)=w 0 ​w 1 ​(μ1 ​−μ0 ​)2

三、c++实现(核心代码)

    auto tempImage = originImage.clone();
    std::vector<int> countPixel(256, 0);

    std::vector<float> pixelProb;
    std::vector<int> pixel(256);
    for(int i = 0; i < 256; i++){
        pixel[i] = i;
    }

    for(int i = 0; i < tempImage.rows; i++){
        for(int j = 0; j < tempImage.cols; j++){
            countPixel[tempImage.at<uchar>(i, j)]++;
        }
    }

    float sum_pixel = tempImage.rows * tempImage.cols;
    for(auto iter = countPixel.begin(); iter != countPixel.end(); iter++){
        pixelProb.push_back(*iter / sum_pixel);
    }

    std::vector<float> class_within_variance_vec;

    std::vector<float> class_between_variance_vec;

    for(int k = 0; k < 256; k++){

        float class_0_prob = 0;

        float class_1_prob = 0;
        for(int i = 0; i  k; i++){
            class_0_prob += pixelProb[i];
        }
        class_1_prob = 1 - class_0_prob;

        float class_0_average = 0;

        float class_1_average = 0;
        for(int i = 0; i  k; i++){
            class_0_average += i * (pixelProb[i] / (class_0_prob+0.000001));
        }
        for(int i = k+1; i  255; i++){
            class_1_average += i * (pixelProb[i] / (class_1_prob+0.000001));
        }

        float class_0_variance = 0;

        float class_1_variance = 0;
        for(int i = 0; i  k; i++){
            class_0_variance += std::pow((i-class_0_average),2)*(pixelProb[i]/(class_0_prob+0.000001));
        }

        for(int i = k+1; i  255; i++){
            class_1_variance += std::pow((i-class_1_average),2)*(pixelProb[i]/(class_1_prob+0.000001));
        }

        float class_within_variance = class_0_prob*std::pow(class_0_variance,2) + class_1_prob*std::pow(class_1_variance,2);
        class_within_variance_vec.push_back(class_within_variance);

        float class_between_variance = class_0_prob * class_1_prob * std::pow((class_1_average-class_0_average),2);
        class_between_variance_vec.push_back(class_between_variance);
    }

    auto max_value_iter = std::max_element(class_between_variance_vec.begin(), class_between_variance_vec.end());

    int k_value = std::distance(class_between_variance_vec.begin(), max_value_iter);
    std::cout<<"k_value:"<< k_value<<"  max_var:"<<*max_value_iter<<std::endl;

    for(int i = 0; i < tempImage.rows; i++){
        for(int j = 0; j < tempImage.cols; j++){
            if(tempImage.at<uchar>(i, j)  k_value)
                tempImage.at<uchar>(i, j) = 0;
            else
                tempImage.at<uchar>(i, j) = 255;
        }
    }

四、效果对比

下图从左到右依次为,原灰度图、OpenCV提供OSTU算法分割结果、自己实现的OSTU分割效果。

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

下图为调用matlab函数实现的大津法阈值分割效果

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

必不可少的Lenna。

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

Original: https://blog.csdn.net/qq_42780025/article/details/113000940
Author: nachr
Title: 大津阈值分割(OSTU)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/199261/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

免费咨询
免费咨询
扫码关注
扫码关注
联系站长

站长Johngo!

大数据和算法重度研究者!

持续产出大数据、算法、LeetCode干货,以及业界好资源!

2022012703491714

微信来撩,免费咨询:xiaozhu_tec

分享本页
返回顶部