# DigitalImage **Repository Path**: sun__ye/digital-image ## Basic Information - **Project Name**: DigitalImage - **Description**: 本项目来源于数字图像处理大作业,实现了基本的程序框架搭建。后续添加曲线数据点提取功能,并且扩充了文件格式,添加了许多常见的附加功能 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2022-07-01 - **Last Updated**: 2025-04-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 数字图像处理程序框架 [TOC] 本项目来源于数字图像处理大作业,实现了基本的程序框架搭建。后续添加曲线数据点提取功能,并且扩充了文件格式,添加了许多常见的附加功能。功能清单: - **基础功能** - [x] 基本图像处理程序框架 - [x] 支持图像文件格式,包括`*.png *.jpg *.bmp *.raw *.data` - [x] 图像傅里叶变换及滤波 - [x] 图像傅里叶描述子的表示 - [ ] 基于傅里叶描述子的图像重构 - [x] 边缘检测 - [x] 添加彩色高通/低通滤波 - **提取曲线数据点** - [x] 方框裁剪 - [x] 按色彩分离 - [x] 顺序选点 - [x] 拟合或插值 - [ ] 解决插值过程中抖动线条问题 - **UI设计改进** - [x] 扩展文件格式,支持pdf格式,自动后台pdf转图像 - [x] 优化文件缓存,解决可能的冲突,缓存在`Roaming/digitalImage`目录下 - [x] 基础功能兼容pdf格式(傅里叶变换、傅里叶描述子的图像重构暂不支持) - [x] 优化界面设计,添加切换、关闭、输入输出移动按钮 - [ ] 将输出框内置与UI界面之内,取消黑色命令行窗口 - **扩展功能** - [x] 图像转PDF功能 - [x] 图像/PDF转GIF功能 - [x] 去除水印功能 - [x] 合成大图功能 - [ ] 水平分割功能 ## 程序说明 ### 目录结构 - main.py:程序主入口,可通过`python main.py`启动PyQt5界面 - main.ui:使用Qt Designer进行的界面设计 - Ui_main.py:通过编译.ui文件生成的界面代码,供主程序调用 - util.py:各种工具函数,包含直方图、傅里叶变换、各种边缘检测算法等 - LineExtractor:与提取曲线数据点相关的类函数 - extractor.py:顺序选点,并进行拟合或插值 - seperator.py:按色彩分离 - tailor.py:方框裁剪 - dist:文件夹中包含打包的各种库和依赖,及可执行文件(由于文件过大未上传,尚处于开发阶段) - 直接运行UI程序,请点击`dist/main/main.exe` - 打包命令:`pyinstaller -D -i test.ico main.py --noconfirm` - doc:相关文档 在使用python main.py前需要安装依赖:`pip install -r requirements.txt` ### 程序界面 程序打开后如下图所示,包含输入输出两个窗格,都是固定700*700大小,窗口可放大缩小,但窗格尺寸固定。 ![image-20220710222933020](https://n.sunie.top:9000/gallery/2022summer/202207102229732.png) 菜单栏包含5个主菜单,每个菜单中都有一些具体的功能,一一对应了下面大作业的所有需求,部分功能都相应的快捷键。 - 文件:打开、关闭和导出图片(及PDF),打开之后会显示在输入窗格 - 基本操作:使用输入窗格内激活的图片作为输入,进行图像基础操作,在输出窗格出显示图片 - FFT:使用输入窗格内激活的图片作为输入,自动转为灰度图,进行FFT相关操作,在输出窗格出显示图片 - “傅里叶变换”功能一共输出FFT,增强,逆变换共3张图 - 边缘检测:使用输入窗格内激活的图片作为输入,自动转为灰度图,进行边缘检测相关操作,在输出窗格出显示图片 - 提取:提取曲线数据点相关步骤,其中尚不支持“提取曲线数据点全流程” - 实用工具:主要是对输入框的PDF使用,如果当前输入不是PDF,则会对所有的输入图片使用,输出一个PDF ![image-20220710223520646](https://n.sunie.top:9000/gallery/2022summer/202207102235400.png) ![image-20220710230459430](https://n.sunie.top:9000/gallery/2022summer/202207102305110.png) 在运行程序时: - 控制台输出的是程序运行的输出,如果执行异常,程序会退出,退出原因见控制台输出 - 打开的图片会被压缩显示,即长、宽中较大的一个被设置成700,另一个维度等比缩放。但是图片的实际尺寸不变,并通过数字显示在右下角,同时会显示图片的名称,如果是pdf还会显示pdf所在的页数 - 输入输出均有多个tab,可以在窗格之间进行切换,想要关闭窗格需点击菜单>文件>关闭,或者点击窗口下方的`×`按钮 - 每个窗格中当前展示的内容是正在激活的tab - 通过窗格下方的`<`及`>`,可以切换正在激活的tab,如果是pdf,只会切换pdf的页面而不会切换tab ## 基础功能 ### 图像处理程序框架 - 基于VC的多文档界面(MDI )方式,设计数字图像处理程序框架 - 软件中编程实现BMP格式图像文件的读取、显示 - 选择实现JPG、 RAW格式文件的读取、显示,以及与BMP格式的转换 - 完成图像的基本操作:加、求反、几何变换 - 完成图像的直方图均衡化处理 #### 文件的读写 文件的格式种类多样,打开和关闭都支持四种类型的图片格式及pdf格式。通过这种方式实现了各种图片格式之间的转换,同时实现了pdf向图片的转换。 ![image-20220710230757818](https://n.sunie.top:9000/gallery/2022summer/202207102307180.png) #### 图像的基本操作 ![image-20220630230720121](https://n.sunie.top:9000/gallery/2022summer/202206302307886.png) 加法会使用所有打开的输入图片(或者当前pdf)进行加法,最终会使用所有图中最大宽和高最为新图的尺寸。 **注意**:请不要使用通道不一致的图片进行加分操作,会造成程序崩溃(可预先都转换为灰度图) ![image-20220630230834085](https://n.sunie.top:9000/gallery/2022summer/202206302308093.png) 进行缩放呈现的效果不会改变,但实际的大小已经变了,见右上角的尺寸。 #### 直方图均衡化 色彩明显变得更加鲜艳: ![image-20220628191431704](https://n.sunie.top:9000/gallery/2022summer/202206302316413.png) ### 图像傅里叶变换及滤波 - 实现图像的FFT变换和显示 - 实现FFT反变换 观察典型图像FFT变换后的频谱图 - 首先构造一幅黑白二值测试图像,例如:在128×128的黑色背景中心产生一个4×4的白色方块。然后依次进行以下测试。 - DFT image-20220627215141715 - 平移、缩放 image-20220627215247881 #### FFT 通过菜单栏>FFT>傅里叶变换进行,执行过程中输出3张图片: - 复数域上频谱图(强行转换uint8使得图片失真) - 动态范围压缩的2DFT图(将值最高的点映射成亮度255) - FFT反变换的输出图 ![image-20220630231153611](https://n.sunie.top:9000/gallery/2022summer/202206302311110.png) #### 高通/低通滤波 支持自定义滤波半径,通过交互式输入方式选择,高通、低通的结果分别如下: ![image-20220630232002932](https://n.sunie.top:9000/gallery/2022summer/202206302320811.png) ### 傅里叶描述子的表示 对于图1中XY平面上的边界,对其进行傅里叶描述子的表示,用不同的项数重构 ![image-20220627215629337](https://n.sunie.top:9000/gallery/2022summer/202206272156275.png) 傅里叶描述子是一种图像特征,用来描述轮廓的特征参数。 傅里叶描述子的基本思想是:首先我们设定物体的**形状轮廓**是一条闭合的曲线,一个点沿边界曲线运动,假设这个点为p(l),它的复数形式的坐标为x(l)+jy(l),它的周期是这个闭合曲线的周长,这也表明属于一个周期函数。该以曲线周长作为周期的函数能够通过傅里叶[级数](https://so.csdn.net/so/search?q=级数&spm=1001.2101.3001.7020)表示。在傅里叶级数里面的多个系数z(k)与闭合边界曲线的形状有着直接关系,将其定义为**傅里叶描述子**。当取到足够阶次的系数项z(k)时,傅里叶描述子能够完全提取形状信息,并恢复物体的形状。 也就是说,傅里叶描述子用一个向量表示轮廓,将轮廓数字化,从而能更好的区分不同的轮廓,达到识别物体的目的。傅里叶描述子的特点是简单并且非常高效,是识别物体形状的重要方法之一。 简单来说,傅里叶描述子就是用一个向量代表一个轮廓,将轮廓数字化,从而能更好地区分不同的轮廓,进而达到识别物体的目的。 如上图所示,少数的傅里叶描述子就可以用于捕获边界的大体特征。这一性质很有用,因为这些系数携带有形状信息。 **整个流程如下:** 1. 边缘检测:使用边缘检测算法将边缘提取出来,并执行闭操作,让边缘更明晰,去掉小黑点 2. 选择所有轮廓中最大轮廓,即目标轮廓,绘制出来 3. 计算轮廓的傅里叶描述子,通过窗格方式输出前32个描述子 4. 可选择描述子的项数进行重构。 ![image-20220630232156686](https://n.sunie.top:9000/gallery/2022summer/202206302321671.png) ### 边缘检测 边缘检测 - 编程实现基于典型微分算子(不少于Roberts、Sobel、Prewitt、拉普拉斯算子)的图像边缘提取,能够读取图像文件内容,进行检测后输出边缘检测结果 - 分析比较不同算子的特性 ![image-20220630233332598](https://n.sunie.top:9000/gallery/2022summer/202206302333920.png) ![image-20220630233039433](https://n.sunie.top:9000/gallery/2022summer/202206302330088.png) ![image-20220630233227582](https://n.sunie.top:9000/gallery/2022summer/202206302332421.png) | 算子 | 优缺点比较 | | --------- | ------------------------------------------------------------ | | Roberts | 对具有陡峭的低噪声的图像处理效果较好,但利用Roberts算子提取边缘的结果是边缘比较粗,因此边缘定位不是很准确 | | Sobel | 对灰度渐变和噪声较多的图像处理效果比较好,Sobel算子对边缘定位比较准确 | | Scharr | 与Sobel算子的不同点是在平滑部分,这里所用的平滑算子是 1/16 *[3, 10, 3],相比于 1/4*[1, 2, 1],中心元素占的权重更重。假设图像这种随机性较强的信号,领域相关性不大 | | Prewitt | 对灰度渐变和噪声较多的图像处理效果较好 | | Laplacian | 对图像中的阶跃性边缘点定位准确,对噪声非常敏感,丢失一部分边缘的方向信息,造成一些不连续的检测边缘。 | | LoG | LG算子经常出现双边缘像素边界,而且该检测方法对噪声比较敏感,所以很少用LG算子检测边缘,而是用来判断边缘像素是位于图像的明区还是暗区。 | | Canny | 此方法不容易受噪声的干扰,能够检测到真正的弱边缘。在edge函数中,最有效的边缘检测方法是Canny方法。该方法的优点在于使用两种不同的阙值分别检测强边缘和弱边缘,并且仅当弱边缘与强边缘相连时,才将弱边缘包含在输出图像 。因此,这种方法不容易被噪声“填充”,更容易检测出真正的弱边缘 | ## 提取曲线数据点 程序运行示例: ![动画](https://n.sunie.top:9000/gallery/2022summer/202207092136731.gif) ### 需求 输入:从文献中收集得到的材料热重测试的.jpg/.png格式图片 输出:对应曲线的数据点 涉及的图片类型主要可以分为以下三类: 1)简单线条 ![image-20220611175527031](https://n.sunie.top:9000/gallery/2022summer/202206111755722.png) 2)存在干扰线条(即蓝色线条) ![image-20220611175541345](https://n.sunie.top:9000/gallery/2022summer/202206111755536.png) 3)复杂线条(存在多条目标曲线) ![image-20220611175603750](https://n.sunie.top:9000/gallery/2022summer/202206111756856.png) ### 问题分析 ![image-20220611181144776](https://n.sunie.top:9000/gallery/2022summer/202206111811628.png) 在上述样例中,所有输入数据有明显的特征,因此相对于普通的曲线提取更加容易。输入特征包含: 1. 曲线大多**被黑色的方框所包围**,方框下方为横坐标,左侧为纵坐标 2. 如果一张图中出现了多条曲线,曲线的**颜色大多不一致** 3. 曲线大多为平滑曲线,从方框最左连续到最右,并且**单调下降** 4. 曲线的形态可以是实线或虚线 5. 曲线标识一般在线条的右上方 5. 图片的背景均为白色 提取流程: 1. 先找到方框或坐标轴所在的位置,将内容裁剪出来识别 2. 通过颜色来区分不同线,多少颜色就输出多少条线,按包含点的数目降序排列。忽略两条线同一颜色的情况 3. 在线上从左到右选点,每次总是选从上到下第一个点,但需要满足单调下降的需求 4. 通过插值或拟合还原原函数,输出100个等距插值点 ### 方框裁剪 见`tailor.py`,核心思路是寻找x/y坐标轴: 1. 根据横向/纵向像素平均值的大小确定水平和竖直线 2. 在这些线条中,坐标轴一般出现在图片最左或者最下 3. 因为坐标轴另外一侧一定有坐标刻度,排除左/下完全为白色的水平和竖直线 4. 根据坐标轴确定裁剪方框并裁剪 ![image-20220709213315230](https://n.sunie.top:9000/gallery/2022summer/202207092133035.png) ### 按色彩分离线条 由于线条有各自的颜色,且线条之间差异较大,可以根据色彩区分不同线条。有以下指标区分线条的差异: - 色相,转换到hsv空间后,色相为0到1间不同的值 - 亮度,及像素的灰度 - 色彩空间距离,即RGB三者的绝对偏差之和 注:为了方便生成新图,在执行分离之前将图片反色,其中灰度值低于50都被认为是黑色,灰度值高于235都被认为是白色 | round | 色彩数 | 色彩带 | | ----- | ------ | ------------------------------------------------------------ | | 0 | 215 | ![image-20220707092403611](https://n.sunie.top:9000/gallery/2022summer/202207070924514.png) | | 1 | 1647 | image-20220707092544177 | | 2 | 6986 | —— | | 3 | 9998 | —— | ![image-20220709213412566](https://n.sunie.top:9000/gallery/2022summer/202207092134204.png) ### 顺序选点 从线上从左到右选点,每次总是选从上到下第一个点,但需要满足单调下降的需求。这样选点能够完全避免选到标签的情况,并且因为限制了单调下降,个不可能出现抖动程度过大。 ![image-20220709213600083](https://n.sunie.top:9000/gallery/2022summer/202207092136826.png) ### 拟合或插值 #### 拟合 在根据样本点拟合过程中,曲线不一定通过样本点,但困难在于函数形式的定义,很难找到符合条件的函数拟合曲线。下图是使用多项式函数,从-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](https://n.sunie.top:9000/gallery/2022summer/202207092129637.png) #### 插值 插值得出的结果要比找到拟合函数容易准确得多,但可能出现抖动或不平滑的现象,如图中圈出的四个地方,插值出来的曲线也会抖动。 ![image-20220707230356099](https://n.sunie.top:9000/gallery/2022summer/202207072303511.png) 抖动线条的解决方式: 1. 在拟合曲线出现抖动的地方,如高阶倒数绝对值较大,删除邻域内的样本点,重新插值。阈值需要人工调整 2. 使用傅里叶描述子重构轮廓,通过调整M项数使轮廓平滑 这一步骤可进一步优化 ## 扩展功能 ### 图像转PDF功能 ### 图像/PDF转GIF功能 ### 去除水印功能 水印的形态各异,市场上常见的水印形态有: - 纯灰色的水印,RGB三个值相同,相较于其他颜色明显更淡(灰度值更低) - 彩色的水印,色相明显与其他颜色不一致 ### 合成大图功能 ### 水平分割功能 希望将一张乐谱,按组分离,分割成一张张图片。 将图片水平像素点相加,可以看到乐谱的每一行之间出现明显的断层:12行共6组分别对应了右图中12个峰共6组,每个峰中存在5个子波峰,即五线谱的五条线 ![image-20220710115129364](C:\Users\sy650\AppData\Roaming\Typora\typora-user-images\image-20220710115129364.png) 取所有灰度值>thresh的作为含有内容的行,将相邻的行聚合在一起,如果相距>distance,就作为两块独立开来,当(thresh=80, distance=30)时输出如下: ```json {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](https://n.sunie.top:9000/gallery/2022summer/202207101215852.png) 这种方法成功找到了所有有效的五线谱序列,在分割的时候,在间隙的的正中央进行分割即可特别的,对于第一组和最后一组,取所有间隔的均值。 ## 错误记录 程序启动时出现:`recursion is detected during loading of cv2 binary extensions. check opencv installation` 因为opencv的版本不对:` pip install opencv-python==4.5.3.56` 图片渲染失败: blocked by CORS policy: The request client is not a secure context and the resource is in more-private address space `local`.