github仓库见:https://gitee.com/sun__ye/digital-image/tree/master

数字图像处理程序框架

本项目来源于数字图像处理大作业,实现了基本的程序框架搭建。后续添加曲线数据点提取功能,并且扩充了文件格式,添加了许多常见的附加功能:

  • 基础功能
    • 基本图像处理程序框架
    • 支持图像文件格式,包括*.png *.jpg *.bmp *.raw *.data
    • 图像傅里叶变换及滤波
    • 图像傅里叶描述子的表示
    • 基于傅里叶描述子的图像重构
    • 边缘检测
    • 添加彩色高通/低通滤波
  • 提取曲线数据点
    • 方框裁剪
    • 按色彩分离
    • 提取数据点——顺序选点
    • 提取数据点——拟合或插值
    • 解决插值过程中抖动线条问题
  • UI设计改进
    • 扩展文件格式,支持pdf格式,自动后台pdf转图像
    • 优化文件缓存,解决可能的冲突,缓存在Roaming/digitalImage目录下
    • 基础功能兼容pdf格式(傅里叶变换、傅里叶描述子的图像重构暂不支持)
    • 优化界面设计,添加切换、关闭、输入输出移动按钮
    • 将输出框内置与UI界面之内,取消黑色命令行窗口
  • 扩展功能
    • 图像转PDF功能
    • 图像/PDF转GIF功能
    • 去除水印功能
    • 合成大图功能
    • 水平分割功能

程序说明

目录结构

  • main.py:程序主入口,可通过python main.py启动PyQt5界面
  • main.ui:使用Qt Designer进行的界面设计
  • Ui_main.py:通过编译.ui文件生成的界面代码,供主程序调用
  • util.py:各种工具函数,包含直方图、傅里叶变换、各种边缘检测算法等
  • dist:此文件夹中包含打包的各种库和依赖,及可执行文件
    • 直接运行UI程序,请点击dist/main/main.exe

打包命令:pyinstaller -D -i test.ico main.py --noconfirm

程序界面

程序打开后如下图所示,包含输入输出两个窗格,都是固定600*600大小,窗口可放大缩小,当窗格不可伸缩。

image-20220630223352486

菜单栏包含5个主菜单,每个菜单中都有一些具体的功能,一一对应了下面大作业的所有需求,部分功能都相应的快捷键。

  • 文件:打开、关闭和导出图片,注意打开之后会显示在输入窗格

  • 输入:对输入窗格内的图片进行处理,直接在输入窗口中修改(inplace操作)

  • 输出:使用输入窗格内激活的图片作为输入,进行图像基础操作,在输出窗格出显示图片

  • FFT:使用输入窗格内激活的图片作为输入,自动转为灰度图,进行FFT相关操作,在输出窗格出显示图片

    • “傅里叶变换”功能一共输出
  • 边缘检测:使用输入窗格内激活的图片作为输入,自动转为灰度图,进行边缘检测相关操作,在输出窗格出显示图片

image-20220630223744228

image-20220630225123867

在运行程序时:

  • 控制台输出的是程序运行的输出,如果执行异常,程序会退出,退出原因间控制台输出
  • 打开的图片会被压缩显示,即长、宽中较大的一个被设置成600,另一个维度等比缩放。但是图片的实际尺寸不变,并通过数字显示在右上角
  • 输入输出均有多个窗格,可以在窗格之间进行切换,但是尚不支持点击’x’关闭,想要关闭窗格需点击菜单>文件>关闭
  • 每个窗格中当前展示的内容是正在激活的tab

基础功能

图像处理程序框架

  • 基于VC的多文档界面(MDI )方式,设计数字图像处理程序框架
  • 软件中编程实现BMP格式图像文件的读取、显示
  • 选择实现JPG、 RAW格式文件的读取、显示,以及与BMP格式的转换
  • 完成图像的基本操作:加、求反、几何变换
  • 完成图像的直方图均衡化处理

文件的读写

文件的格式种类多样,打开和关闭都支持四种类型的格式。通过这种方式实现了各种图片格式之间的转换。

image-20220630225657487

图像的基本操作

image-20220630230720121

加法会使用所有打开的输入进行加法,最终会使用所有图中最大宽和高最为新图的尺寸。

注意:请不要使用通道不一致的图片进行加分操作,会造成程序崩溃(可预先都转换为灰度图)

image-20220630230834085

进行缩放呈现的效果不会改变,但实际的大小已经变了,见右上角的尺寸。

直方图均衡化

色彩明显变得更加鲜艳:

image-20220628191431704

图像傅里叶变换及滤波

  • 实现图像的FFT变换和显示
  • 实现FFT反变换

观察典型图像FFT变换后的频谱图

  • 首先构造一幅黑白二值测试图像,例如:在128×128的黑色背景中心产生一个4×4的白色方块。然后依次进行以下测试。

    • DFT

      image-20220627215141715
    • 平移、缩放

      image-20220627215247881

FFT

通过菜单栏>FFT>傅里叶变换进行,执行过程中输出3张图片:

  • 复数域上频谱图(强行转换uint8使得图片失真)
  • 动态范围压缩的2DFT图(将值最高的点映射成亮度255)
  • FFT反变换的输出图

image-20220630231153611

高通/低通滤波

支持自定义滤波半径,通过交互式输入方式选择,高通、低通的结果分别如下:

image-20220630232002932

傅里叶描述子的表示

对于图1中XY平面上的边界,对其进行傅里叶描述子的表示,用不同的项数重构

image-20220627215629337

傅里叶描述子是一种图像特征,用来描述轮廓的特征参数。

傅里叶描述子的基本思想是:首先我们设定物体的形状轮廓是一条闭合的曲线,一个点沿边界曲线运动,假设这个点为p(l),它的复数形式的坐标为x(l)+jy(l),它的周期是这个闭合曲线的周长,这也表明属于一个周期函数。该以曲线周长作为周期的函数能够通过傅里叶级数表示。在傅里叶级数里面的多个系数z(k)与闭合边界曲线的形状有着直接关系,将其定义为傅里叶描述子。当取到足够阶次的系数项z(k)时,傅里叶描述子能够完全提取形状信息,并恢复物体的形状。
也就是说,傅里叶描述子用一个向量表示轮廓,将轮廓数字化,从而能更好的区分不同的轮廓,达到识别物体的目的。傅里叶描述子的特点是简单并且非常高效,是识别物体形状的重要方法之一。

简单来说,傅里叶描述子就是用一个向量代表一个轮廓,将轮廓数字化,从而能更好地区分不同的轮廓,进而达到识别物体的目的。

如上图所示,少数的傅里叶描述子就可以用于捕获边界的大体特征。这一性质很有用,因为这些系数携带有形状信息。

整个流程如下:

  1. 边缘检测:使用边缘检测算法将边缘提取出来,并执行闭操作,让边缘更明晰,去掉小黑点
  2. 选择所有轮廓中最大轮廓,即目标轮廓,绘制出来
  3. 计算轮廓的傅里叶描述子,通过窗格方式输出前32个描述子
  4. 可选择描述子的项数进行重构。

image-20220630232156686

边缘检测

边缘检测

  • 编程实现基于典型微分算子(不少于Roberts、Sobel、Prewitt、拉普拉斯算子)的图像边缘提取,能够读取图像文件内容,进行检测后输出边缘检测结果
  • 分析比较不同算子的特性

image-20220630233332598

image-20220630233039433

image-20220630233227582

算子 优缺点比较
Roberts 对具有陡峭的低噪声的图像处理效果较好,但利用Roberts算子提取边缘的结果是边缘比较粗,因此边缘定位不是很准确
Sobel 对灰度渐变和噪声较多的图像处理效果比较好,Sobel算子对边缘定位比较准确
Scharr 与Sobel算子的不同点是在平滑部分,这里所用的平滑算子是 1/16 [3, 10, 3],相比于 1/4[1, 2, 1],中心元素占的权重更重。假设图像这种随机性较强的信号,领域相关性不大
Prewitt 对灰度渐变和噪声较多的图像处理效果较好
Laplacian 对图像中的阶跃性边缘点定位准确,对噪声非常敏感,丢失一部分边缘的方向信息,造成一些不连续的检测边缘。
LoG LG算子经常出现双边缘像素边界,而且该检测方法对噪声比较敏感,所以很少用LG算子检测边缘,而是用来判断边缘像素是位于图像的明区还是暗区。
Canny 此方法不容易受噪声的干扰,能够检测到真正的弱边缘。在edge函数中,最有效的边缘检测方法是Canny方法。该方法的优点在于使用两种不同的阙值分别检测强边缘和弱边缘,并且仅当弱边缘与强边缘相连时,才将弱边缘包含在输出图像 。因此,这种方法不容易被噪声“填充”,更容易检测出真正的弱边缘

提取曲线数据点

程序运行示例:

动画

需求

输入:从文献中收集得到的材料热重测试的.jpg/.png格式图片

输出:对应曲线的数据点

涉及的图片类型主要可以分为以下三类:

1)简单线条

image-20220611175527031

2)存在干扰线条(即蓝色线条)

image-20220611175541345

3)复杂线条(存在多条目标曲线)

image-20220611175603750

问题分析

image-20220611181144776

在上述样例中,所有输入数据有明显的特征,因此相对于普通的曲线提取更加容易。输入特征包含:

  1. 曲线大多被黑色的方框所包围,方框下方为横坐标,左侧为纵坐标
  2. 如果一张图中出现了多条曲线,曲线的颜色大多不一致
  3. 曲线大多为平滑曲线,从方框最左连续到最右,并且单调下降
  4. 曲线的形态可以是实线或虚线
  5. 曲线标识一般在线条的右上方
  6. 图片的背景均为白色

提取流程:

  1. 先找到方框或坐标轴所在的位置,将内容裁剪出来识别
  2. 通过颜色来区分不同线,多少颜色就输出多少条线,按包含点的数目降序排列。忽略两条线同一颜色的情况
  3. 在线上从左到右选点,每次总是选从上到下第一个点,但需要满足单调下降的需求
  4. 通过插值或拟合还原原函数,输出100个等距插值点

方框裁剪

tailor.py,核心思路是寻找x/y坐标轴:

  1. 根据横向/纵向像素平均值的大小确定水平和竖直线
  2. 在这些线条中,坐标轴一般出现在图片最左或者最下
  3. 因为坐标轴另外一侧一定有坐标刻度,排除左/下完全为白色的水平和竖直线
  4. 根据坐标轴确定裁剪方框并裁剪

image-20220709213315230

按色彩分离线条

由于线条有各自的颜色,且线条之间差异较大,可以根据色彩区分不同线条。有以下指标区分线条的差异:

  • 色相,转换到hsv空间后,色相为0到1间不同的值
  • 亮度,及像素的灰度
  • 色彩空间距离,即RGB三者的绝对偏差之和

注:为了方便生成新图,在执行分离之前将图片反色,其中灰度值低于50都被认为是黑色,灰度值高于235都被认为是白色

round 色彩数 色彩带
0 215 image-20220707092403611
1 1647 image-20220707092544177
2 6986
3 9998

image-20220709213412566

顺序选点

从线上从左到右选点,每次总是选从上到下第一个点,但需要满足单调下降的需求。这样选点能够完全避免选到标签的情况,并且因为限制了单调下降,个不可能出现抖动程度过大。

image-20220709213600083

拟合或插值

拟合

在根据样本点拟合过程中,曲线不一定通过样本点,但困难在于函数形式的定义,很难找到符合条件的函数拟合曲线。下图是使用多项式函数,从-3到+3项数进行拟合的结果,七个系数分别为:

[-5.81978492e+01  2.23481625e+02 -1.84351082e+02  1.34665445e+02
 -2.35943787e+00  1.71681831e-02 -1.61449275e-05]

可见这种方式并不能很好地表示曲线。

image-20220709212917925

插值

插值得出的结果要比找到拟合函数容易准确得多,但可能出现抖动或不平滑的现象,如图中圈出的四个地方,插值出来的曲线也会抖动。

image-20220707230356099

抖动线条的解决方式:

  1. 在拟合曲线出现抖动的地方,如高阶倒数绝对值较大,删除邻域内的样本点,重新插值。阈值需要人工调整
  2. 使用傅里叶描述子重构轮廓,通过调整M项数使轮廓平滑

这一步骤可进一步优化

扩展功能

图像转PDF功能

图像/PDF转GIF功能

去除水印功能

水印的形态各异,市场上常见的水印形态有:

  • 纯灰色的水印,RGB三个值相同,相较于其他颜色明显更淡(灰度值更低)
  • 彩色的水印,色相明显与其他颜色不一致

合成大图功能

水平分割功能

希望将一张乐谱,按组分离,分割成一张张图片。

将图片水平像素点相加,可以看到乐谱的每一行之间出现明显的断层:12行共6组分别对应了右图中12个峰共6组,每个峰中存在5个子波峰,即五线谱的五条线

image-20220710115129364

取所有灰度值>thresh的作为含有内容的行,将相邻的行聚合在一起,如果相距>distance,就作为两块独立开来,当(thresh=80, distance=30)时输出如下:

{0: [171, 177, 182, 183, 188, 194], 1: [230, 231, 236, 242, 247, 248, 253], 2: [305, 327, 328, 333, 334, 339, 345, 350, 351], 3: [387, 388, 393, 398, 399, 404, 410, 411], 4: [484, 485, 490, 495, 496, 501, 502, 507], 5: [544, 549, 550, 555, 561, 566, 567], 6: [624, 625, 641, 646, 647, 652, 653, 658, 663, 664], 7: [700, 701, 706, 712, 717, 718, 723], 8: [797, 798, 803, 804, 809, 814, 815, 820, 821], 9: [861, 866, 867, 872, 878, 883, 884], 10: [958, 963, 964, 969, 970, 975, 980, 981], 11: [1017, 1018, 1023, 1029, 1034, 1035, 1040]})

可视化效果:

image-20220710121526274

这种方法成功找到了所有有效的五线谱序列,在分割的时候,在间隙的的正中央进行分割即可特别的,对于第一组和最后一组,取所有间隔的均值。

错误记录

程序启动时出现:recursion is detected during loading of cv2 binary extensions. check opencv installation

因为opencv的版本不对: pip install opencv-python==4.5.3.56