上学期的时候水了一篇图像压缩与线性代数,是线性代数小组合作探究的一个小部分,然后这学期的选了个数字图像处理。本来以为是个水课,结果听了两节之后发现对于数学不好的我过于硬核,后面的课基本都在划水了。临近期末突然布置了课设,没办法,还是得从头看起。
本来这课是用得Matlab,但问了下老师,用Python可以(也可以用C++, 可我不会啊),所以我是用Python完成的。这里把我的答案记录下来。(据说题目是祖传的,可以造福后辈x)
第一题
将图像的灰度级分辨率调整至{128,64,32,16,8,4,2} ,并在同一个figure窗口上将它们显示出来。
第一题还是挺简单的,就做一下整除就可以了。
import numpy as np import matplotlib.pyplot as plt plt.figure(figsize=[8,16],dpi=50) img_256 = plt.imread('../img/lena.bmp') # 读取图片 img_128 = np.int8(img_256 / 2) img_64 = np.int8(img_256 / 4) img_32 = np.int8(img_256 / 8) img_16 = np.int8(img_256 / 16) img_8 = np.int8(img_256 / 32) img_4 = np.int8(img_256 / 64) img_2 = np.int8(img_256 / 128) # 调整色阶至{128,64,32,16,8,4,2} plt.subplot(4,2,1) # 选择子图 plt.title('Origin') # 设置标题 plt.imshow(img_256,cmap='gray',vmin=0,vmax=255) # 打印图片 # 参数: 图片源,打印的色集(这里全部选择gray),最小色阶,最大色阶 plt.subplot(4,2,2) plt.title('128') plt.imshow(img_128,cmap='gray',vmin=0,vmax=127) plt.subplot(4,2,3) plt.title('64') plt.imshow(img_64,cmap='gray',vmin=0,vmax=63) plt.subplot(4,2,4) plt.title('32') plt.imshow(img_32,cmap='gray',vmin=0,vmax=31) plt.subplot(4,2,5) plt.title('16') plt.imshow(img_16,cmap='gray',vmin=0,vmax=15) plt.subplot(4,2,6) plt.title('8') plt.imshow(img_8,cmap='gray',vmin=0,vmax=7) plt.subplot(4,2,7) plt.title('4') plt.imshow(img_4,cmap='gray',vmin=0,vmax=3) plt.subplot(4,2,8) plt.title('2') plt.imshow(img_2,cmap='gray') plt.savefig('./output.png',dpi='figure',format='png') plt.show() plt.close()
最终的结果
第二题
往图像中叠加不同类型的噪声,并设计一个频域低通滤波器来去除之
如何生成噪声
首先来看一下如何生成随机噪声,scipy.image
提供了这个函数
import matplotlib.pyplot as plt import numpy as np import skimage import skimage.io import skimage.util img = skimage.io.imread('../img/bank.bmp') noise_type = ['gaussian','localvar','poisson','salt','pepper','s&p','speckle'] # 噪声的类型 plt.figure(figsize=[8,16],dpi=150) gimg = {} for index,mode in enumerate(noise_type): plt.subplot(4,2,index+2) gimg[mode] = skimage.util.random_noise(img,mode=mode) plt.title(mode) plt.imshow(gimg[mode],cmap='gray') plt.subplot(4,2,1) plt.title('Origin') plt.imshow(img,cmap='gray',vmin=0,vmax=255) plt.savefig('generate.png',dpi='figure',format='png') plt.show() plt.close()
生成的噪声图片
低通滤波器
低通滤波器分三种,区别在传递函数的不同
理想低通滤波器(ILPF)
H(x)={1D(u,v)≤D00D(u,v)>D0
def ilpf(img,n0): G = np.fft.fft2(img) FG = np.fft.fftshift(G) # 进行傅里叶变换 x = FG.shape[0] y = FG.shape[1] H = np.ones((x,y)) for i in range(1,x): for j in range(1,y): r1 = (i - x/2)**2 + (j - y/2)**2 r = np.sqrt(r1) if r > n0: H[i,j] = 0 con = FG * H # 与传递函数相乘 return abs(np.fft.ifft2(con)) # 反傅里叶变换 plt.figure(dpi=150, figsize=[18,24]) for index,mode in enumerate(noise_type): plt.subplot(7,5,index*5 +1) plt.title(mode) plt.imshow(gimg[mode],cmap='gray') for i in range(1,5): plt.subplot(7,5,index*5+i+1) plt.title(f'n0 = {i*20}') plt.imshow(ilpf(gimg[mode],i*20),cmap='gray') plt.savefig('ilpf.png',dpi='figure',format='png') plt.show() plt.close()
Butterworth 低通滤波器 (BLPF)
H(x)=11+[D(u,v)D0]2n
def blpf(img,n0): G = np.fft.fft2(img) FG = np.fft.fftshift(G) x = FG.shape[0] y = FG.shape[1] H = np.ones((x,y)) for i in range(1,x): for j in range(1,y): r = np.sqrt((i-x/2)**2 + (j-y/2)**2) if r > n0: H[i,j] = 1/(1 + (r/n0)**3) con = FG * H return abs(np.fft.ifft2(con))
高斯低通滤波器 (GLPF)
H(u,v)=exp{−[D(u,v)D0]n}
def glpf(img,n0): G = np.fft.fft2(img) FG = np.fft.fftshift(G) x = FG.shape[0] y = FG.shape[1] H = np.ones((x,y)) for i in range(1,x): for j in range(1,y): r = np.sqrt((i-x/2)**2 + (j-y/2)**2) H[i,j] = np.exp(-(r/n0)**3) con = H * FG return abs(np.fft.ifft2(con))
第三题
举例说明顶帽变换在图像阴影校正方面的应用
其实这个问题直接看顶帽变换的Wiki就有答案了
代码实现(选择rice.bmp更容易得出结论)
import numpy as np import matplotlib.pyplot as plt import cv2 img = plt.imread('../img/rice.bmp') kernel = np.ones((100,100),np.uint8) tophat = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel) # 顶帽变换 ret, ostu = cv2.threshold(img,0,255,cv2.THRESH_OTSU) # 对原图做阈值分割 th_ret, th_ostu = cv2.threshold(tophat,0,255,cv2.THRESH_OTSU) # 对顶帽变换后的图片做阈值分割 plt.figure(figsize=[9,10],dpi=100) plt.subplot(2,2,1) plt.title('Origin') plt.imshow(img,cmap='gray',vmin=0,vmax=255) plt.subplot(2,2,2) plt.title('Origin OTSU') plt.imshow(ostu,cmap='gray',vmin=0,vmax=255) plt.subplot(2,2,3) plt.title('Top Hat') plt.imshow(tophat,cmap='gray',vmin=0,vmax=255) plt.subplot(2,2,4) plt.title('Top Hat OTSU') plt.imshow(th_ostu,cmap='gray',vmin=0,vmax=255) plt.savefig('output.png',dpi='figure',format='png')
第四题
利用Hough变换来检测图像中的直线,与变换过程相关的系列约束条件(线段的最小长度等)可自行叠加。
这里主要有两个操作,一个是 cv2.Canny()
得到图像边缘,一个是 cv2.HoughLines()
检测图像中的直线。
import numpy as np import matplotlib.pyplot as plt import cv2 img = cv2.imread('../img/bank.bmp') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray,100,100,apertureSize=3) # 检测边缘 def print_lines(index): length = index*10 + 50 lines = cv2.HoughLines(edges,1,np.pi/180,length) # 检测直线 img_copy = img.copy() for line in lines: for rho,theta in line: a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) cv2.line(img_copy,(x1,y1),(x2,y2),(0,0,255),1) plt.subplot(2,3,index) plt.title(f'Min Length {length}') plt.imshow(img_copy) # 绘制直线 plt.figure(figsize=[12,8],dpi=150) plt.subplot(2,3,1) plt.title('Origin') plt.imshow(img) for i in range(2,7): print_lines(i) # 用最小长度做约束条件 plt.savefig('output.png',dpi='figure',format='png') plt.show() plt.close()
第五题
对图像执行阈值分割操作并统计出每一个区域块的属性,然后,将每个区域的中心和外接矩形给标注出来。
这里还是建议用rice.bmp
那张图片,因为容易分割。
import matplotlib.pyplot as plt import numpy as np import cv2 # 读取图像 img = cv2.imread('../img/rice.bmp') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # 使用顶帽变换修复阴影 tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,np.ones((100,100),np.uint8)) # 使用OTSU阈值分割 ret,otsu = cv2.threshold(tophat,50,127,cv2.THRESH_OTSU) # 寻找区块边缘 contours,hierarchy = cv2.findContours(otsu, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) for contour in contours: # 获取区块的外界矩形,以及区域中心 rect = cv2.minAreaRect(contour) box = np.int0(cv2.boxPoints(rect)) max_x = box[:,0].max() min_x = box[:,0].min() max_y = box[:,1].max() min_y = box[:,1].min() # 绘制外接矩形和中心点 center = (int((max_x + min_x)/2), int((max_y + min_y)/2)) img = cv2.drawContours(img,[box],-1,(0,255,0),2) img = cv2.circle(img,center,3,(255,0,0),-1) cv2.imwrite('./output.png',cv2.cvtColor(img,cv2.COLOR_BGR2RGB)) plt.imshow(img) plt.show() plt.close()
(这里有一点问题,因为我直接调用库了,不太清楚需要标的属性是啥)
第六题
设计一个简易的Matlab GUI界面程序,要求其具有如下的功能:
- 打开与保存图像时均打开文件名设置对话框;
- 当下拉菜单中的条目被选中时,列表框之中实时的记录下当前的选择;
- 通过编辑框来实现相关参数的交互式输入;
- 将输入图像及处理后结果显示在相应的坐标轴之上;
- 含有工具栏和菜单栏,当选择其下的组件成分时,要有相应的图像处理行为发生;
- 将figure窗口的“Name”属性修改为自己的姓名和学号;
- 将所设计的GUI程序编译为“.exe”形式的可执行文件
这题我就不放代码了,大概说一下方法吧。我使用PyQt来做的,实现的功能是第四题的那个直线检测,用滑动条以更改最小长度。
先用QtDesigner把.ui设置出来,然后对着PyQt的文档查每个组件的使用方式,很快就可以完成了(记得做异常处理)。
最后来说下我对这个课的感受吧。这课感觉不应该给大一的学生开,有点太硬核了,如果你是想认真学的话建议等到大二再选,不然可能就跟我一样成为调包侠,对其中的原理并没有多少认知。当然水学分就是另一回事了x。
Update
2020.07.29 老师给分很大方,i了i了
引用
- https://gist.github.com/Prasad9/28f6a2df8e8d463c6ddd040f4f6a028a#gistcomment-2933012
- https://scikit-image.org/docs/stable/api/skimage.util.html#skimage.util.random_noise
- Image Processing and Acquisition using Python - Ravishankar Chityala, Sridevi Pudipeddi
- https://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html
- https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_canny/py_canny.html
- https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html
- https://cloud.tencent.com/developer/article/1052878
牛哇牛哇
By 轩轩子 at October 19th, 2021 at 07:15 pm.