PotatoPie 4.0 实验教程(21) —— FPGA实现摄像头图像二值化(RGB2Gray2Bin)-Anlogic-安路论坛-FPGA CPLD-ChipDebug

PotatoPie 4.0 实验教程(21) —— FPGA实现摄像头图像二值化(RGB2Gray2Bin)

手机扫码

20240416075513933-1713225291635

链接直达

https://item.taobao.com/item.htm?ft=t&id=776516984361

为什么要进行图像的二值化?

当我们处理图像时,常常需要将其转换为二值图像。这是因为在很多应用中,我们只对图像中的某些特定部分感兴趣,而不需要考虑所有像素的颜色信息。下面我会详细解释为什么要进行图像的二值化:

  1. 简化图像结构:彩色图像包含大量的像素,每个像素都有自己的颜色信息。这种复杂性使得图像处理变得复杂。将图像转换为二值图像可以大大简化图像结构,使得后续的处理更加高效。

  2. 突出目标特征:在很多应用中,我们只对图像中的某些目标感兴趣,而不关心其他部分。通过二值化,我们可以将目标与背景分离,突出目标的特征,便于进一步的分析和处理。

  3. 去除噪声:图像中常常包含各种噪声,如摄像头传感器的噪声、环境光线的影响等。这些噪声会干扰图像的分析和处理。通过二值化,我们可以将噪声过滤掉,只保留目标信息,提高图像的质量。

  4. 减少计算量:在一些图像处理算法中,需要对每个像素进行复杂的计算。如果图像的分辨率很高,计算量会非常大。将图像转换为二值图像可以大大减少计算量,提高算法的执行效率。

  5. 图像压缩:二值图像只包含黑白两种颜色,信息量较少。因此,可以通过二值化来压缩图像,减少存储空间和传输带宽。

总之,图像二值化是图像处理中的一项基础技术,它可以简化图像结构、突出目标特征、去除噪声、减少计算量以及实现图像压缩等多种目的。

图像二值化的常用算法

图像二值化是图像处理中的一项基础任务,有许多不同的算法可以实现。以下是几种常用的图像二值化算法:

  1. 全局阈值法(Global Thresholding):全局阈值法是最简单和最常用的二值化方法之一。它通过设定一个全局阈值,将图像中的像素分为两类:大于阈值的像素设为白色,小于等于阈值的像素设为黑色。其中,Otsu 方法是全局阈值法中的一种优化方法,它能自动确定最佳的阈值,使得类间方差最大化。

  2. 局部阈值法(Local Thresholding):局部阈值法考虑到图像中不同区域的灰度分布可能不同,因此采用不同的阈值来进行二值化。常见的局部阈值法包括自适应阈值法和基于统计的方法,如局部均值、局部中值等。

  3. 基于直方图的方法(Histogram-based Methods):这些方法利用图像的灰度直方图来确定阈值。常见的方法包括基于双峰分析、直方图形态学等。

  4. 基于梯度的方法(Gradient-based Methods):这些方法基于图像的梯度信息来确定阈值。常见的方法包括基于边缘检测算子的方法,如Sobel、Prewitt等。

  5. 基于聚类的方法(Clustering-based Methods):这些方法将图像中的像素看作是一个样本集合,利用聚类算法将像素分成两个类别,然后根据类别信息进行二值化。常见的方法包括K均值聚类、自组织映射聚类等。

这些算法各有优缺点,适用于不同的图像处理任务和场景。在实际应用中,根据图像的特点和需求选择合适的二值化算法是非常重要的。我们本节实验主要采用Otsu 方法

Otsu 方法是由日本学者大津展之(Nobuyuki Otsu)于1979年提出的图像二值化算法。这个方法旨在通过自适应地确定最佳阈值,将图像分为背景和前景两个类别,以最大化类间方差来实现图像的自动化处理。

在 Otsu 方法被提出之前,常用的图像二值化方法主要是基于手动设定阈值的全局阈值法。然而,手动选择阈值可能会因为主观性而不准确,尤其是对于不同的图像和应用场景,需要不断调整阈值才能得到满意的结果。Otsu 方法的提出填补了这一缺陷,使得图像二值化可以更加自动化和准确。

Otsu 方法的核心思想是通过最大化类间方差来确定最佳的阈值。在图像中,背景和前景之间的差异体现在它们的灰度分布上。通过寻找一个阈值,使得背景和前景之间的类间方差最大化,我们可以实现最佳的图像二值化。这种方法不仅能够自动地确定最佳阈值,而且在很多情况下能够产生高质量的二值化结果。

Otsu 方法的提出极大地促进了图像处理领域的发展,成为了图像二值化中的经典算法之一。它被广泛应用于数字图像处理、计算机视觉、图像分割等领域,为图像分析和识别提供了重要的基础。

Otsu 算法的详细步骤:

  1. 计算直方图:首先,计算图像的灰度直方图,统计每个灰度级别的像素数量。

  2. 归一化直方图:将直方图中每个灰度级别的像素数量除以图像的总像素数,得到每个灰度级别的归一化频率。

  3. 计算累积分布函数:计算归一化直方图的累积分布函数,即累积概率密度函数。这可以通过累加每个灰度级别的归一化频率来实现。

  4. 初始化类间方差和最佳阈值:初始化类间方差为 0,并将最佳阈值设为 0。

  5. 遍历阈值:对每个可能的阈值 t 进行遍历(从 0 到最大灰度级别)。

  6. 计算类间方差:对于每个阈值 t,计算两个类别的均值和方差,然后根据这些统计量计算类间方差。类间方差可通过下式计算得到:

    20240413092544208-image

  7. 选择最佳阈值:选择使类间方差最大的阈值作为最佳阈值,即找到能够最好地将图像分成两个类别,使得类别之间的差异最大化。

  8. 应用阈值:使用最佳阈值将图像进行二值化处理,将大于阈值的像素设为白色(255),小于等于阈值的像素设为黑色(0)。

  9. 通过这些步骤,Otsu 算法能够自动确定最佳的阈值,将图像转换为二值图像,并且在很多情况下能够产生高质量的二值化结果。

用python实现Otsu 算法对图像进行二值化处理

这个代码会读取名为 Lena.jpg 的彩色图片,然后将其转换为灰度图像,并使用 Otsu 算法进行图像二值化。然后显示原始彩色图像、灰度图像和二值化后的图像。

以下是代码的详细说明:

  1. 导入 OpenCV 库:

    import cv2
  2. 获取当前脚本文件的路径:

    current_file_path = __file__
    current_file_dir = os.path.dirname(current_file_path)

    这里使用了 __file__ 内置变量来获取当前脚本文件的路径,然后通过 os.path.dirname() 函数提取目录部分,并将结果存储在 current_file_dir 变量中。

  3. 读取图片:

    img = cv2.imread(current_file_dir + '/Lena.jpg')

    使用 cv2.imread() 函数读取名为 ‘Lena.jpg’ 的彩色图像文件,并将其存储在名为 img 的变量中。

  4. 转换为灰度图像:

    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    使用 cv2.cvtColor() 函数将彩色图像 img 转换为灰度图像,并将结果存储在名为 gray_img 的变量中。

  5. 使用 Otsu 算法进行图像二值化:

    _, binary_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    使用 cv2.threshold() 函数将灰度图像 gray_img 进行二值化处理,并使用 Otsu 算法自动确定阈值。将阈值化后的图像存储在名为 binary_img 的变量中。

  6. 显示图像:

    cv2.imshow('Original Image', img)
    cv2.imshow('Gray Image', gray_img)
    cv2.imshow('Binary Image (Otsu)', binary_img)

    使用 cv2.imshow() 函数显示原始图像、灰度图像和二值化后的图像。第一个参数是窗口的标题,第二个参数是要显示的图像。

  7. 等待按下任意键继续并关闭窗口:

    cv2.waitKey(0)
    cv2.destroyAllWindows()

    使用 cv2.waitKey(0) 函数等待用户按下任意键,然后使用 cv2.destroyAllWindows() 函数关闭所有打开的窗口。

这段代码演示了如何使用 OpenCV 库对图像进行灰度化和二值化处理,并显示处理后的图像。

用如下的powershell指令运行python脚本,后面的教程中将不再举例说明如何运行python文件。

20240415110908416-image

可以看到演示效果:

20240414103538709-image

上面的代码是直接采用的opencv的otsu方法实现的,没有展示细节,我们下面提供的这段代码展示了otsu的方法细节。

这段代码使用了 OpenCV 库对一张彩色图像进行了灰度化和自适应阈值二值化处理,并显示了原始图像、灰度图像和二值化后的图像。以下是代码的详细说明:

  1. 导入必要的库:

    import cv2
    import os
    import numpy as np
    • cv2:用于图像处理和显示。
    • os:用于操作文件路径。
    • numpy:用于数值计算。
  2. 获取当前文件的路径:

    current_file_path = os.path.abspath(__file__)
    current_file_dir = os.path.dirname(current_file_path)

    使用 os.path.abspath(__file__) 获取当前脚本文件的绝对路径,然后通过 os.path.dirname() 函数提取目录部分,并将结果存储在 current_file_dir 变量中。

  3. 读取图像:

    img = cv2.imread(os.path.join(current_file_dir, 'Lena.jpg'))

    使用 cv2.imread() 函数读取名为 ‘Lena.jpg’ 的彩色图像文件,并将其存储在名为 img 的变量中。

  4. 转换为灰度图像:

    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    使用 cv2.cvtColor() 函数将彩色图像 img 转换为灰度图像,并将结果存储在名为 gray_img 的变量中。

  5. 计算灰度直方图并归一化:

    histogram = cv2.calcHist([gray_img], [0], None, [256], [0, 256])
    histogram /= histogram.sum()

    使用 cv2.calcHist() 函数计算灰度图像 gray_img 的直方图,并将结果存储在名为 histogram 的变量中。然后,将直方图归一化以便于后续计算。

  6. 初始化变量:

    max_variance = 0
    best_threshold = 0

    初始化最大方差 max_variance 和最佳阈值 best_threshold

  7. 遍历所有可能的阈值:

    for threshold in range(256):
    ...

    使用循环遍历所有可能的阈值,计算每个阈值对应的类别1和类别2的概率、均值和方差,并更新最大方差和最佳阈值。

  8. 使用最佳阈值进行二值化:

    _, binary_img = cv2.threshold(gray_img, best_threshold, 255, cv2.THRESH_BINARY)

    使用 cv2.threshold() 函数根据最佳阈值将灰度图像 gray_img 进行二值化处理,并将结果存储在名为 binary_img 的变量中。

  9. 显示图像:

    cv2.imshow('Original Image', img)
    cv2.imshow('Gray Image', gray_img)
    cv2.imshow('Binary Image (Otsu)', binary_img)

    使用 cv2.imshow() 函数显示原始图像、灰度图像和二值化后的图像。

  10. 等待按下任意键继续并关闭窗口:

    cv2.waitKey(0)
    cv2.destroyAllWindows()

    使用 cv2.waitKey(0) 函数等待用户按下任意键,然后使用 cv2.destroyAllWindows() 函数关闭所有打开的窗口。

这段代码演示了如何使用 OpenCV 对图像进行灰度化和自适应阈值二值化处理,并显示处理后的图像。

matlab版代码

这段 MATLAB 代码实现了以下功能:

  1. 读取名为 “Lena.jpg” 的彩色图像。
  2. 将彩色图像转换为灰度图像。
  3. 使用 Otsu 算法对灰度图像进行二值化处理,得到二值化图像。
  4. 在单个窗口中显示原始彩色图像、灰度图像和二值化图像,以便比较和分析图像处理的效果。

通过这段代码,可以轻松地了解图像处理中常用的 Otsu 二值化算法,并可视化其效果。

以下是代码的详细说明:

  1. 获取当前文件的路径:

    current_file_path = mfilename('fullpath');
    [current_file_dir, ~, ~] = fileparts(current_file_path);
    fprintf('current_file_dir: %s\n', current_file_dir);

    使用 mfilename('fullpath') 函数获取当前脚本文件的完整路径,然后通过 fileparts() 函数提取目录部分,并将结果存储在 current_file_dir 变量中。

  2. 读取图片:

    img = imread(fullfile(current_file_dir, 'Lena.jpg'));

    使用 imread() 函数读取名为 ‘Lena.jpg’ 的彩色图像文件,并将其存储在名为 img 的变量中。fullfile() 函数用于生成文件的完整路径。

  3. 转换为灰度图像:

    gray_img = rgb2gray(img);

    使用 rgb2gray() 函数将彩色图像 img 转换为灰度图像,并将结果存储在名为 gray_img 的变量中。

  4. 使用 Otsu 算法进行图像二值化:

    level = graythresh(gray_img);
    binary_img = imbinarize(gray_img, level);

    使用 graythresh() 函数计算灰度图像 gray_img 的阈值,并将结果存储在名为 level 的变量中。然后,使用 imbinarize() 函数根据计算得到的阈值将灰度图像进行二值化处理,并将结果存储在名为 binary_img 的变量中。

  5. 显示图像:

    subplot(1, 3, 1);
    imshow(img);
    title('Original Image');

    subplot(1, 3, 2);
    imshow(gray_img);
    title('Gray Image');

    subplot(1, 3, 3);
    imshow(binary_img);
    title('Binary Image (Otsu)');

    使用 subplot() 函数将图像显示区域分为 1 行 3 列的子图区域,并分别在每个子图区域中使用 imshow() 函数显示原始图像、灰度图像和二值化后的图像。使用 title() 函数设置每个子图的标题。

20240415113353830-image

FPGA工程分析

工程层次图

20240413093523694-image

与demo18相比,只是多了一个img2rgb的模块,也就是下面这一段代码,在从SDRAM读出来之后,经它处理后再输出hdmi_tx模块。

img_rgb2gray2bin u_img_rgb2gray2bin
(
    .i_clk(clk_pixel),  
    .i_rst_n(sys_rst_n), 
    .i_hs(VGA_HS),     
    .i_vs(VGA_VS),     
    .i_de  (VGA_DE),         
    .i_vld(1'b1),
    .i_th(78), 
    .i_r(VGA_RGB[23:16]),
    .i_g(VGA_RGB[15:8] ),
    .i_b(VGA_RGB[7:0]  ),    
    .o_bin_hs(gray_hs),  
    .o_bin_vs(gray_vs),  
    .o_bin_de  (gray_de),
    .o_bin_data(gray_data)
);

模块代码分析

由于 Otsu 算法使用 Verilog 实现十分复杂,这些步骤在EG4上实现资源不太够, 这里我们将直接指定阈值进行二值化。 

模块的管脚定义注释比较清晰,如下

module img_rgb2gray2bin (
    input   wire                i_clk,      // 时钟信号
    input   wire                i_rst_n,    // 复位信号

    input   wire                i_vld,      // 有效信号
    input   wire    [7:0]       i_th,       // 阈值信号

    input   wire                i_hs,       // 水平同步信号
    input   wire                i_vs,       // 垂直同步信号
    input   wire    [7:0]       i_r,        // 红色通道信号
    input   wire    [7:0]       i_g,        // 绿色通道信号
    input   wire    [7:0]       i_b,        // 蓝色通道信号
    input   wire                i_de,       // 数据使能信号

    output  reg                 o_bin_hs,   // 二值化水平同步信号
    output  reg                 o_bin_vs,   // 二值化垂直同步信号
    output  reg                 o_bin_de,   // 二值化数据使能信号
    output  reg     [7:0]       o_bin_data  // 二值化图像数据信号
);

代码的流程比较简单先进行灰度转换

然后进行阈值比较

管脚约束

PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

时序约束

PotatoPie 4.0 实验教程(18) —— FPGA实现OV5640摄像头采集以SDRAM作为显存进行HDMI输出显示相同,不作赘述。

实验结果

这是我的键盘和显示器,这效果出来像水墨画啊,下面是阈值98时候的效果。

20240413095941476-image

说值调到45看看。

20240413100421589-image

调到 78时效果我比较喜欢

20240413102429158-image

请登录后发表评论

    没有回复内容