0 写在前面
现在好像知道应该做什么了,然后慢慢去努力就好了。mark一下,还有幸福课需要看。
1 图像增强与检测之间的联系——特征匹配
思路选自——《基于机器视觉的水下海参图像识别技术》山东大学 徐萌
原理
SIFT特征匹配算法是一种在不同空间尺度上的一种关键特征点匹配算法,通常键像素点的大小、方向和尺度信息来描述特征。SIFT特征具有旋转、尺度缩放不变性,信息量丰富,匹配过程快速且准确。
流程
原始图像->特征点检测+特征点描述->特征集
基本思路
将用于对照的图像组进行一定程度的旋转和偏置,然后进行SIFT特征匹配度分析。检查特征点是否增多。
2 实验记录
2.1 偏置
import cv2
import math
import numpy as np
path = r'G:\sift\c000113.jpg'
img = cv2.imread(path)
height, width = img.shape[:2]
if img.ndim == 3:
channel = 3
else:
channel = None
angle = 30
if int(angle / 90) % 2 == 0:
reshape_angle = angle % 90
else:
reshape_angle = 90 - (angle % 90)
reshape_radian = math.radians(reshape_angle) # 角度转弧度
# 三角函数计算出来的结果会有小数,所以做了向上取整的操作。
new_height = math.ceil(height * np.cos(reshape_radian) + width * np.sin(reshape_radian))
new_width = math.ceil(width * np.cos(reshape_radian) + height * np.sin(reshape_radian))
if channel:
new_img = np.zeros((new_height, new_width, channel), dtype=np.uint8)
else:
new_img = np.zeros((new_height, new_width), dtype=np.uint8)
radian = math.radians(angle)
cos_radian = np.cos(radian)
sin_radian = np.sin(radian)
dx = 0.5 * new_width + 0.5 * height * sin_radian - 0.5 * width * cos_radian
dy = 0.5 * new_height - 0.5 * width * sin_radian - 0.5 * height * cos_radian
# ---------------前向映射--------------------
# for y0 in range(height):
# for x0 in range(width):
# x = x0 * cos_radian - y0 * sin_radian + dx
# y = x0 * sin_radian + y0 * cos_radian + dy
# new_img[int(y) - 1, int(x) - 1] = img[int(y0), int(x0)] # 因为整体映射的结果会比偏移一个单位,所以这里x,y做减一操作。
# ---------------后向映射--------------------
dx_back = 0.5 * width - 0.5 * new_width * cos_radian - 0.5 * new_height * sin_radian
dy_back = 0.5 * height + 0.5 * new_width * sin_radian - 0.5 * new_height * cos_radian
# for y in range(new_height):
# for x in range(new_width):
# x0 = x * cos_radian + y * sin_radian + dx_back
# y0 = y * cos_radian - x * sin_radian + dy_back
# if 0 < int(x0) <= width and 0 < int(y0) <= height: # 计算结果是这一范围内的x0,y0才是原始图像的坐标。
# new_img[int(y), int(x)] = img[int(y0) - 1, int(x0) - 1] # 因为计算的结果会有偏移,所以这里做减一操作。
# ---------------双线性插值--------------------
if channel:
fill_height = np.zeros((height, 2, channel), dtype=np.uint8)
fill_width = np.zeros((2, width + 2, channel), dtype=np.uint8)
else:
fill_height = np.zeros((height, 2), dtype=np.uint8)
fill_width = np.zeros((2, width + 2), dtype=np.uint8)
img_copy = img.copy()
# 因为双线性插值需要得到x+1,y+1位置的像素,映射的结果如果在最边缘的话会发生溢出,所以给图像的右边和下面再填充像素。
img_copy = np.concatenate((img_copy, fill_height), axis=1)
img_copy = np.concatenate((img_copy, fill_width), axis=0)
for y in range(new_height):
for x in range(new_width):
x0 = x * cos_radian + y * sin_radian + dx_back
y0 = y * cos_radian - x * sin_radian + dy_back
x_low, y_low = int(x0), int(y0)
x_up, y_up = x_low + 1, y_low + 1
u, v = math.modf(x0)[0], math.modf(y0)[0] # 求x0和y0的小数部分
x1, y1 = x_low, y_low
x2, y2 = x_up, y_low
x3, y3 = x_low, y_up
x4, y4 = x_up, y_up
if 0 < int(x0) <= width and 0 < int(y0) <= height:
pixel = (1 - u) * (1 - v) * img_copy[y1, x1] + (1 - u) * v * img_copy[y2, x2] + u * (1 - v) * img_copy[y3, x3] + u * v * img_copy[y4, x4] # 双线性插值法,求像素值。
new_img[int(y), int(x)] = pixel
cv2.imshow('res', new_img)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite(r'G:\sift\img_save.jpg',new_img)
2.2 特征点检验
import io
from PIL import Image, ImageTk
import tkinter as tk
import cv2
import numpy as np
MIN_MATCH_COUNT = 4
img1 = cv2.imread(r"G:\sift\c000113.jpg")
img2 = cv2.imread(r"G:\sift\img_save.jpg")
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
match = cv2.FlannBasedMatcher(dict(algorithm =2, trees =1), {})
kp1, de1 = sift.detectAndCompute(g1,None)
kp2, de2 = sift.detectAndCompute(g2,None)
m = match.knnMatch(de1, de2, 2)
m = sorted(m,key = lambda x:x[0].distance)
ok = [m1 for (m1, m2) in m if m1.distance < 0.7 * m2.distance]
med = cv2.drawMatches(img1, kp1, img2, kp2, ok, None)
cv2.imwrite(r"G:\sift\b.jpg", med)
#
# cv2.imshow("0", med)
# cv2.waitKey()
# cv2.destroyAllWindows()
def resize(w, h, w_box, h_box, pil_image):
f1 = 1.0 * w_box / w # 1.0 forces float division in Python2
f2 = 1.0 * h_box / h
factor = min([f1, f2])
width = int(w * factor)
height = int(h * factor)
return pil_image.resize((width, height), Image.ANTIALIAS)
root = tk.Tk()
# size of image display box you want
# 期望图像显示的大小
w_box = 800
h_box = 1000
# 以一个PIL图像对象打开
pil_image = Image.open(r'G:\sift\b.jpg')
# get the size of the image
# 获取图像的原始大小
w, h = pil_image.size
# resize the image so it retains its aspect ration
# but fits into the specified display box
# 缩放图像让它保持比例,同时限制在一个矩形框范围内
pil_image_resized = resize(w, h, w_box, h_box, pil_image)
# convert PIL image object to Tkinter PhotoImage object
# 把PIL图像对象转变为Tkinter的PhotoImage对象
tk_image = ImageTk.PhotoImage(pil_image_resized)
# put the image on a widget the size of the specified display box
# Label: 这个小工具,就是个显示框,小窗口,把图像大小显示到指定的显示框
label = tk.Label(root, image=tk_image, width=w_box, height=h_box)
# padx,pady是图像与窗口边缘的距离
label.pack(padx=5, pady=5)
root.mainloop()
实验结果
偏置结果
特征匹配结果
错误记录
错误描述:解决opencv错误AttributeError: module ‘cv2.cv2‘ has no attribute ‘xfeatures2d‘
错误原因:这个错误是由于opencv-contrib-python版本太新导致出现的问题,应该降低版本
解决方案:
pip install opencv-contrib-python==3.3.0.10