资讯动态

基于Python的数字图像处理课程设计:灰度化、卷积与傅里叶变换实战

发布时间:2026/10/13 21:43:05 来源:尧图企业网站定制
简介这是一份围绕Python数字图像处理的课程设计资源包面向计算机视觉初学者和需要完成图像处理实训的在校学生。内容完整覆盖彩色图像转灰度、卷积与相关操作、高斯核平滑滤波、二维傅里叶变换与中心化处理、频谱图像分析、整数次幂填充等核心知识点并基于OpenCV与NumPy提供可直接运行的代码实现方便读者边看边练、对照原理理解每一步运算。资源共17个文件其中ipynb交互式笔记适合逐步演示py脚本便于整体运行与修改另配有多种tif/tiff灰度及彩色测试图、README说明和Git相关配置文件包体仅3.57MB下载后即可快速开展实验。目前已有825人学习使用无论用于课程设计还是入门自学都能从中获得完整的技术链路和实验素材。借助代码注释与示例图像读者可深入理解空间域与频率域处理的差异并据此扩展出更多图像分析应用。1. 基于 Python 的数字图像处理课程设计这套代码包到底能跑出什么做图像处理课程设计的人最怕的不是不懂原理而是公式推完了、报告写完了一跑代码全是黑白噪点。这套编号 100011309 的课程设计资源核心是一份ImageProcess.py加两个 Jupyter 作业笔记本work_1.ipynb、work_2.ipynb配齐了 cameraman、lena_gray_512、lunar_surface、mandril_color、rose512 这些经典测试图覆盖灰度转换、卷积与相关、高斯核平滑、二维傅里叶变换、频谱中心化、幂次填充补零这几块数字图像处理的必修内容。它不是一本教你怎么装 OpenCV 的入门手册而是一份把课本算法落到像素级可见结果的完整工程适合正在做课程设计、需要交代码和实验报告、或者想自己把《数字图像处理》每章算法跑一遍的人。下面我按实际拆包顺序把每一块代码怎么用、参数怎么调、哪些地方容易翻车一次说清楚。2. 图像读写与灰度化从 BGR 到 YUV 亮度分量这一步决定后面所有结果2.1 为什么灰度图不是简单地取 RGB 平均很多第一次做图像处理的同学会以为灰度化就是(RGB)/3但 OpenCV 里cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)走的是 ITU-R BT.601 标准的加权公式gray 0.299 * R 0.587 * G 0.114 * B这个权重不是拍脑袋定的而是根据人眼对不同颜色敏感度差异来的——绿色贡献最多蓝色最少。如果直接用平均lena 的脸会看起来发灰发闷边缘检测时也会引入不必要的亮度误差。加上 OpenCV 读图默认是 BGR 通道顺序如果你用cv2.imread()读进来后直接拿[:, :, 0]当红色通道做加权结果会完全反掉这是第一个值得注意的坑。参数上cv2.cvtColor()的第二个参数除了COLOR_BGR2GRAY还有COLOR_RGB2GRAY区别只是输入通道顺序。用 matplotlib 显示图像时因为plt.imshow()默认按 RGB 解释所以显示前必须cv2.cvtColor(img, cv2.COLOR_BGR2RGB)否则你看到的 lena 会是一张蓝脸。2.2 读取、显示与保存的完整样板这套资源里所有测试图都在根目录实际写作业时建议统一用相对路径避免到别的机器上跑就找不到文件。import cv2 import numpy as np import matplotlib.pyplot as plt # 读取彩色图并完成灰度转换 img_bgr cv2.imread(mandril_color.tif) img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) img_gray cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # 用 matplotlib 并排显示原图与灰度图 plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.imshow(img_rgb) plt.title(Original Color) plt.axis(off) plt.subplot(1, 2, 2) plt.imshow(img_gray, cmapgray) plt.title(Gray) plt.axis(off) plt.show() # 保存灰度结果后续卷积和傅里叶变换都基于这张图 cv2.imwrite(mandril_gray.png, img_gray)这段代码有两个关键点。一是cv2.imwrite()保存灰度图时不需要指定 cmap它写进文件的就是单通道而 matplotlib 显示灰度图必须带cmapgray否则会按伪彩色映射成绿色。二是img_gray的数据类型是uint8范围 0 到 255如果你后面做傅里叶变换前不转成float32np.fft.fft2()计算出来的结果依然正确但做频谱可视化时对数变换容易出现数值溢出所以建议在读图后就归一化处理img_float img_gray.astype(np.float32) / 255.02.3 批量读入测试图的小工具课程设计要跑多张图对比效果时建议写个小函数统一加载资源包里那十几张 tif、tiff、png 混在一起手动逐个改路径容易出错。import os def load_demo_images(folder.): names [cameraman.tif, house02.tif, lunar_surface.tif, lena_gray_512.tif, rose512.tif, einstein.tif] imgs {} for n in names: path os.path.join(folder, n) if os.path.exists(path): img cv2.imread(path, cv2.IMREAD_GRAYSCALE) imgs[n.split(.)[0]] img.astype(np.float32) / 255.0 print(f{n}: shape{img.shape}, max{img.max()}) return imgs实际拆包时我发现house.tif和house02.tif大小不一样后者尺寸更大、细节更多做边缘检测对比时效果差异很明显。写报告时建议选house02.tif视觉上更有辨识度。另外注意lena512color.tiff是彩色图lena_gray_512.tif是灰度图两个文件名差一点别混用。3. 卷积、相关与高斯核filter2D 的三个隐藏参数3.1 卷积和相关在图像处理里到底差在哪从数学定义上看卷积核kernel与图像f的卷积是f * kernel计算时要先把核旋转 180 度再滑动求和相关运算则不旋转直接逐像素相乘累加。对对称核比如高斯核、均值核来说卷积和相关结果完全一样所以很多人忽略了这个区别。但一旦用到非对称核——比如 Sobel 算子或者自定义的方向检测核——旋转与否直接决定边缘方向是否反向。OpenCV 的cv2.filter2D()实现的是相关运算不是严格意义上的卷积。如果你真的需要卷积可以先把核用cv2.flip(kernel, -1)翻转后再传给filter2D。在这个课程设计里作业多半要求对比卷积与相关的输出差异验证方法很简单构造一个非对称核比如[[1, 2, 3], [4, 5, 6], [7, 8, 9]]对同一张图分别做两次操作结果矩阵会差一个旋转后的版本。3.2 自定义核做锐化与边缘提取import cv2 import numpy as np # 读取灰度图并归一化 img cv2.imread(cameraman.tif, cv2.IMREAD_GRAYSCALE).astype(np.float32) / 255.0 # 非对称核用于对比卷积与相关的差异 kernel_custom np.array([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ], dtypenp.float32) # filter2D 执行的是相关运算 res_corr cv2.filter2D(img, -1, kernel_custom) # 把核翻转 180 度后再做就是严格卷积 kernel_flipped cv2.flip(kernel_custom, -1) res_conv cv2.filter2D(img, -1, kernel_flipped) # 常用的拉普拉斯锐化核 kernel_laplacian np.array([ [0, -1, 0], [-1, 4, -1], [0, -1, 0] ], dtypenp.float32) img_sharp cv2.filter2D(img, -1, kernel_laplacian) img_enhanced cv2.subtract(img, img_sharp) # 原图减去拉普拉斯结果得到锐化图代码里cv2.filter2D()第二个参数ddepth填-1表示输出与输入同深度。如果你输入是uint8而核有负权重中间计算结果可能超出 0 到 255 范围OpenCV 默认会截断导致锐化边缘出现奇怪的黑色轮廓。所以我习惯先把图转成float32并归一化到 0 到 1这样所有中间值都在安全区间最后显示时再乘 255 转回uint8。对比卷积与相关时最直观的方式是计算np.abs(res_corr - res_conv).max()如果核非对称这个差值一定不为 0。报告里可以写对于对称核两个操作等价对于非对称核相关是卷积的核翻转版本。3.3 高斯核的两种生成方式高斯核是唯一一种可分离且各向同性的平滑核二维高斯函数可以分解成两个一维高斯核的乘积所以cv2.GaussianBlur()内部先做横向卷积再做纵向卷积效率远高于直接做二维卷积。# 方式一直接用 OpenCV 内置高斯模糊 blur_builtin cv2.GaussianBlur(img, (5, 5), sigmaX1.2) # 方式二自己生成高斯核后用 filter2D def gaussian_kernel(size5, sigma1.2): k cv2.getGaussianKernel(size, sigma) # 一维列向量 kernel_2d k k.T # 外积得到二维核 return kernel_2d kernel_my gaussian_kernel(5, 1.2) blur_mine cv2.filter2D(img, -1, kernel_my) # 验证两种方式输出是否一致 diff np.abs(blur_builtin - blur_mine).max() print(f最大像素差: {diff:.6f})核大小和 sigma 的匹配是关键。cv2.getGaussianKernel(size, sigma)里 sigma 传0时 OpenCV 会自动按0.3 * ((size - 1) * 0.5 - 1) 0.8计算这个经验公式对 3x3 和 5x5 核比较合适但对 7x7 以上偏小。我一般手动指定 sigma因为 sigma 决定核的频域带宽——sigma 越大平滑越强图像越模糊边缘信息丢失越多sigma 太小则噪声滤不掉。一个很容易踩的坑是cv2.GaussianBlur()的核宽高必须是正奇数传(4, 4)会直接报错。另外注意高斯滤波对椒盐噪声效果很差如果你测试图里有明显颗粒噪点要先用中值滤波cv2.medianBlur()而不是硬上高斯核这个区别在写实验分析时值得单独讨论。4. 二维傅里叶变换实战从空间域到频域这三行代码最容易错4.1 fft2、fftshift 与频谱可视化的标准流程二维傅里叶变换的核心是把图像从空间域映射到频率域低频集中在频谱图的中心附近如果没有做 shift 则在四角高频分布在四周。直接用np.fft.fft2(img)得到的结果是复数数组幅值跨度从零到几万直接plt.imshow()看到的只会是一团白点所以要做两步处理先用np.fft.fftshift把零频移到中心再做对数压缩np.log(1 abs(F))。import numpy as np import matplotlib.pyplot as plt import cv2 # 读取图像、转灰度、归一化 img cv2.imread(lunar_surface.tif, cv2.IMREAD_GRAYSCALE).astype(np.float32) / 255.0 # 二维 FFT 与中心化 F np.fft.fft2(img) F_shifted np.fft.fftshift(F) # 零频移到中心 phase np.angle(F_shifted) # 相位谱 magnitude np.abs(F_shifted) # 幅值谱 # 对数压缩便于可视化 mag_log np.log(1 magnitude) # 逆变换先取消 shift 再 ifft2 img_recovered np.fft.ifft2(np.fft.ifftshift(F_shifted)) img_recovered np.real(img_recovered) # 取实部忽略数值误差带来的虚部 # 显示 plt.figure(figsize(12, 4)) plt.subplot(1, 3, 1) plt.imshow(img, cmapgray) plt.title(Original) plt.axis(off) plt.subplot(1, 3, 2) plt.imshow(mag_log, cmapgray) plt.title(Magnitude Spectrum) plt.axis(off) plt.subplot(1, 3, 3) plt.imshow(phase, cmapgray) plt.title(Phase Spectrum) plt.axis(off) plt.show() # 验证逆变换恢复质量 error np.abs(img - img_recovered).max() print(f最大恢复误差: {error:.2e})这段代码里最容易错的是fftshift和ifftshift的配对。正向流程是fft2 → fftshift逆变换必须是ifftshift → ifft2顺序不能反。有些教程会在逆变换时也用fftshift对偶数尺寸图像结果碰巧一样但遇到奇数尺寸比如stride.tif那种非 2 的幂尺寸就会出错。另外np.fft.ifft2()返回的是复数数组理论上虚部应该为零但由于浮点计算误差会有极小虚部必须取np.real()否则plt.imshow()会因为虚部无法显示而直接抛错。4.2 为什么频谱要中心化低频与高频的分布逻辑没有做fftshift时傅里叶变换结果的左上角是零频分量这意味着图像的直流分量平均亮度被放在角落而谱图像的四角最亮。做了 shift 之后零频在正中心图像的整体亮度信息集中到中心点高频细节分布在四周。对于lunar_surface.tif这种月球表面图你能明显看到中心亮斑周围有一圈十字形亮线——那是图像边界不连续导致的不是真实的月球纹理。写报告时建议把未 shift 和已 shift 的频谱图并排展示说明中心化的意义让低频在视觉上居中便于观察频域滤波效果。4.3 频域滤波实操低通与高通光做傅里叶变换不滤波作业深度不够。常见的做法是在频域构造一个理想低通滤波器把中心以外的频率全部置零然后逆变换回空间域。# 构造理想低通滤波器保留中心半径 D0 以内的频率 rows, cols img.shape crow, ccol rows // 2, cols // 2 D0 30 # 截止频率半径 # 生成网格坐标 u np.arange(cols) - ccol v np.arange(rows) - crow U, V np.meshgrid(u, v) D np.sqrt(U**2 V**2) # 理想低通掩膜 mask_lp (D D0).astype(np.float32) # 频域相乘 F_filtered F_shifted * mask_lp img_lp np.real(np.fft.ifft2(np.fft.ifftshift(F_filtered))) # 理想高通取反掩膜 mask_hp (D D0).astype(np.float32) F_hp F_shifted * mask_hp img_hp np.real(np.fft.ifft2(np.fft.ifftshift(F_hp))) # 显示对比 fig, axes plt.subplots(1, 3, figsize(12, 4)) for ax, im, t in zip(axes, [img, img_lp, img_hp], [Original, Low-pass D030, High-pass D030]): ax.imshow(im, cmapgray) ax.set_title(t) ax.axis(off) plt.show()这个实现里D030是截止半径单位是像素。对lena_gray_512.tif这种 512x512 的图D0 取 30 已经能明显看到平滑效果但图像会发虚取 80 则接近原图。理想低通滤波有一个数学上的副作用——振铃效应因为理想滤波器在频域是矩形窗对应空间域是 sinc 函数逆变换后图像边缘会出现明暗交替的条纹。课程设计报告里如果对比了理想低通和高斯低通这个点一定能加分。4.4 频域滤波和空域卷积的关系高斯核的空域卷积等价于频域乘法。这一点背后是卷积定理f * g的傅里叶变换等于F(f) · F(g)。实操中同样大小的核空域卷积对 512x512 图需要做大约512 * 512 * 25次乘加频域滤波则只需要两次 FFT 和一次逐元素乘法大核时频域快得多。但小核3x3、5x5空域反而更快因为 FFT 有额外开销。课程设计里如果要求分析两种实现的性能差异可以用timeit测一下import timeit # 空域高斯滤波 def spatial_gaussian(): return cv2.GaussianBlur(img, (31, 31), 5.0) # 频域高斯滤波 def freq_gaussian(): kernel cv2.getGaussianKernel(31, 5.0) kernel_2d kernel kernel.T F_kernel np.fft.fft2(kernel_2d, s(rows, cols)) F_img np.fft.fft2(img) return np.real(np.fft.ifft2(F_img * F_kernel)) t1 timeit.timeit(spatial_gaussian, number10) t2 timeit.timeit(freq_gaussian, number10) print(f空域耗时: {t1:.4f}s, 频域耗时: {t2:.4f}s)注意频域做法里np.fft.fft2(kernel_2d, s(rows, cols))的s参数它会把核自动填充到图像尺寸再做变换这是实现卷积定理的关键细节漏掉的话频谱尺寸对不上结果全是乱的。5. 幂次填充与边界处理避坑fill 不对整张图白算5.1 为什么图像要补零到 2 的整数次幂FFT 算法对长度等于 2 的幂的信号效率最高这是 Cooley-Tukey 算法的基本前提。对于rose512.tif这种本身就是 512x512 的图天然满足条件但house.tif尺寸可能比较随意比如 420x360直接做 FFT 虽然能算但速度慢且频谱可能出现混叠。实际操作里最常见的是把图像扩展到最近的 2 的幂尺寸比如 420 扩展到 512360 也扩展到 512。幂次填充的另一个用途是防止循环卷积的边界效应。FFT 本质上是循环卷积如果直接频域滤波图像的右边界会卷到左边界产生错误的边缘。解决办法是先补零到M K - 1的尺寸M 为图像尺寸K 为核尺寸再取 2 的幂滤波完成后再裁剪回原尺寸。def pad_to_power_of_two(img): h, w img.shape new_h 1 int(np.ceil(np.log2(h))) # 向上取最近的 2 的幂 new_w 1 int(np.ceil(np.log2(w))) padded np.zeros((new_h, new_w), dtypeimg.dtype) padded[:h, :w] img return padded, (h, w) img cv2.imread(house.tif, cv2.IMREAD_GRAYSCALE).astype(np.float32) / 255.0 img_padded, original_shape pad_to_power_of_two(img) print(f原始尺寸: {original_shape}, 填充后: {img_padded.shape}) # 逆变换后裁剪回原尺寸 F_padded np.fft.fft2(img_padded) img_back np.real(np.fft.ifft2(F_padded)) img_cropped img_back[:original_shape[0], :original_shape[1]]5.2 边界处理复制、反射、常数填充的取舍做空域卷积时边缘像素没有完整的邻域OpenCV 默认的处理方式是borderTypecv2.BORDER_DEFAULT它实际上用的是反射 101 规则gfedcb|abcdefgh|gfedcba效果接近反射填充但更平滑。如果你不指定filter2D和GaussianBlur都走这个默认值。实操中高频边缘检测时复制填充BORDER_REPLICATE容易出现假边缘——因为边缘像素被向外复制后滤波器在边界处把同一个小区域的值反复加权输出会出现条带。反射填充BORDER_REFLECT则没有这个问题因为边界外是镜像信号连续性更好。常数填充BORDER_CONSTANT比如补零适合频域滤波前使用因为它不会引入额外的频率分量但会让图像边界变黑。5.3 排查为什么我的逆变换图片有网格状条纹这个现象我在拆这套资源时也遇到过。如果你对cameraman.tif做幂次填充后再逆变换裁剪回原尺寸后图像上隐约有一层淡淡的网格纹路大概率是填充区域的零值参与了 FFT导致频谱里混入了高频的矩形窗信号。解决方法有两种。一是让填充区域复用图像边缘的像素值而不是填零这样矩形窗的边界跳变变小振铃会弱很多。二是滤波时只对有效区域做掩膜mask_valid np.zeros_like(img_padded) mask_valid[:original_shape[0], :original_shape[1]] 1.0 # 滤波后把填充区域权重压低 F_filtered F_shifted * mask_lp img_result np.real(np.fft.ifft2(np.fft.ifftshift(F_filtered))) img_result * mask_valid # 直接把填充区域置零5.4 避坑与排查清单坑一显示傅里叶谱时全是黑屏或白屏现象plt.imshow(np.abs(F))显示出来是一张几乎全黑的图或者只有中心一个白点。原因傅里叶变换的幅值动态范围太大中心直流分量可能是几万而边缘高频分量只有几十线性映射后低频全被拉成白色高频全被压成黑色。解决先做np.log(1 np.abs(F))再做plt.imshow(..., cmapgray)并且把vmin和vmax设置成对数幅值的最小最大值必要时用plt.clim()微调。坑二逆变换后图像亮度整体偏移现象ifft2恢复的图像比原图整体偏亮或偏暗边缘还出现亮暗条纹。原因频域滤波时不小心修改了零频分量或者把fftshift过的频谱直接用ifft2变换而没有先ifftshift。零频分量对应图像平均亮度一旦被滤波器置零整张图的灰度均值就会变成 0 附近显示出来偏暗。解决检查代码流程确认逆变换前一定执行np.fft.ifftshift(F_filtered)。用np.mean()对比原图和恢复图的灰度均值差值超过 0.01 就说明零频被动过了。坑三filter2D 处理彩色图像时通道全乱现象对mandril_color.tif直接调用filter2D或GaussianBlur输出图像的颜色完全不对偏色严重。原因RGB 三个通道被同样的核处理后每个通道的边界条件一致不会有问题但如果你先把彩色图用cv2.cvtColor转成灰度再处理颜色信息本来就是丢的。还有一种情况是cv2.imread默认 BGR 顺序matplotlib按 RGB 显示如果你只对单通道做了滤波再合并回三通道通道顺序错位就会偏蓝或偏红。解决彩色图像滤波前先cv2.cvtColor(img, cv2.COLOR_BGR2RGB)把三个通道拆开后分别filter2D再用cv2.merge()合并回去。如果只是想展示滤波对比效果直接用灰度图最省心。坑四2 的幂次填充后尺寸过大内存暴涨现象对lena512color.tiff512x512x3做 2 的幂次填充时如果误把每个通道分别填充到 1024x1024三个 float32 数组加起来内存超过 12MB对老机器可能卡顿。原因512 本来就是 2 的 9 次幂不需要填充。但如果你把int(np.ceil(np.log2(h)))算出来的指数直接拿来1 指数遇到恰好是 2 的幂的尺寸也应该保持原尺寸不需要扩展。解决填充函数里加一个判断if new_h h: new_h h # 已经是 2 的幂则跳过或者用更稳的方式h 1后再取 2 的幂保证至少多出一行用于循环卷积补偿。6. 从理论到验收图像处理项目的验证方法与效果评估技巧傅里叶变换和卷积都做完了课程设计还差最后一步——怎么证明你的结果是可信的。很多同学交上去的代码能跑出图但报告里没有任何量化指标答辩时一问就露馅。我分享三个自己常用的验证手段。第一个是峰值信噪比PSNR。无论做去噪、平滑还是频域滤波你都得有处理前和处理后两张图PSNR 是衡量两幅图像之间差异的最常用指标。数值越高表示失真越小一般在 30dB 以上视觉上就看不太出明显差异低于 25dB 则通常有明显的块效应或噪声。这个计算在 OpenCV 里有现成的cv2.PSNR()可以直接用但要注意输入图像的类型必须是float32取值范围 0 到 1 或 0 到 255两种范围算出来的数值会差一个常数写报告时统一用 0 到 1。def compute_psnr(img1, img2): # img1 和 img2 都是 float32范围 [0, 1] mse np.mean((img1 - img2) ** 2) if mse 0: return float(inf) return 10 * np.log10(1.0 / mse)第二个是结构相似度指数 SSIM。PSNR 计算的是逐像素误差但人眼对局部结构的敏感度远高于全局像素差异所以两幅图 PSNR 相同看起来可能完全不一样。SSIM 比较的是两个图像窗口的亮度、对比度和结构三个分量范围负一到一越接近一表示越相似。OpenCV 的cv2.SSIM()在较新版本里才提供contrib 模块如果环境里没有可以退一步用 scikit-image 的skimage.metrics.structural_similarity。写报告时 SSIM 比 PSNR 更受老师认可因为它更接近人类主观感知。第三个是频谱能量分布分析。做频域滤波后观察频谱图中心的低频能量变化低通滤波后中心亮斑还在但外围变暗高通滤波则相反。能量分布可以定量分析把频谱图的像素值平方求和低频区占比是多少高频区占比是多少滤除前后对比这两个数字。代码实现很简单def spectral_energy_ratio(F_shifted, center_radius30): mag np.abs(F_shifted) energy mag ** 2 h, w energy.shape crow, ccol h // 2, w // 2 # 低频区域掩膜 y, x np.ogrid[:h, :w] mask_low (x - ccol) ** 2 (y - crow) ** 2 center_radius ** 2 energy_low energy[mask_low].sum() energy_total energy.sum() return energy_low / energy_total第四个技巧是写实验报告时把不同参数的结果并排对比而不是只贴一张图。比如高斯核 sigma 分别取 0.5、1.0、3.0看 lena 的平滑效果差异同时记录 PSNR 和 SSIM低通滤波器截止半径 D0 分别取 10、30、80看频谱和恢复图的对应变化。这种对比表格比纯文字描述有说服力得多。我还习惯在代码里把所有中间结果全部保存到output/目录命名带参数后缀比如lena_fft_shift.jpg、lena_lp_D30.jpg。这样做有两个好处一是写报告时不需要重新跑代码就能贴图二是答辩的时候可以现场展示参数改动前后的效果变化直接体现你对算法参数的理解程度。除了验证方法还有一个容易被忽视的坑环境一致性。OpenCV 和 NumPy 版本不同某些 API 行为会有差异比如cv2.findContours()在新版本返回两个值、旧版本返回三个值cv2.GaussianBlur的 sigma 自动计算逻辑在不同版本间也有微调。如果你交上去的代码在别人电脑上能跑自己电脑报错多半是版本问题。建议在代码开头加一个环境检查import cv2 import numpy as np import sys print(fPython: {sys.version.split()[0]}) print(fNumPy: {np.__version__}) print(fOpenCV: {cv2.__version__})这套资源里work_1.ipynb和work_2.ipynb是 Jupyter 笔记本如果你用 VS Code 打开内核必须选对 Python 解释器——我之前就是因为选了 base 环境的解释器OpenCV 装在一个 conda 环境里跑了半天全在ModuleNotFoundError这种玄学问题排查起来比算法问题更耗时间。回到这份课程设计本身ImageProcess.py是把所有算法打包成函数的主文件适合直接运行验证各步骤两个 ipynb 则更像实验报告的可执行版本每一步都有输出可以对照。实际做作业时我的习惯是先跑work_1.ipynb把整体流程走通再改ImageProcess.py中的参数做对比实验最后用独立的验证脚本算 PSNR 和 SSIM。从那以后我每次做完图像处理实验都会强制走一遍灰度化 → 卷积/高斯平滑 → 傅里叶变换 → 频域滤波 → 逆变换 → 量化指标评估这条完整链路确认每一张中间图都有对应的数字结论后再写报告希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑