2024.9.13 Python与图像处理新国大EE5731课程大作业,SIFT 特征和描述符,单应性矩阵透视变换

1.SIFT特征点和描述符

py 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
# read image
img =cv2.imread('im01.jpg',cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
plt.imshow(gray,plt.cm.gray)

提取图片,以灰度图像输出

py 复制代码
#SIFT
sift = cv2.SIFT_create()
# keypoints detection
kp,des = sift.detectAndCompute(gray,None)
# plot keypoints
cv2.drawKeypoints(img,kp,img,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# plot
plt.figure(figsize=(8,6),dpi=100)
plt.imshow(img[:,:,::-1])
plt.title('SIFT')
plt.xticks([])
plt.yticks([])
plt.show

这段代码使用了 OpenCV 库中的 SIFT(Scale-Invariant Feature Transform)算法来检测图像的关键点(keypoints)并绘制它们。SIFT 是一种非常强大的图像特征提取算法,它能够检测图像中的局部特征,并且对缩放、旋转、亮度等变换具有鲁棒性。

代码逻辑:

1.创建 SIFT 对象

2.detectAndCompute() 是 SIFT 处理图像的关键函数。它接受一个灰度图像作为输入,并返回两个值:

kp (keypoints): 关键点,表示图像中的某些局部特征点的位置、尺度、方向等。每个关键点都有不同的属性,如坐标位置、尺度(表示特征点的大小)、方向等。

des (descriptors): 描述符,是关于每个关键点的特征向量。描述符是一个 128 维的向量,用于描述该特征点周围区域的特征信息。这些向量可以用于图像匹配或分类。

3.绘制关键点flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS: 使用这个标志,关键点会被以圆圈的形式绘制,并且每个圆圈的大小和方向会根据每个关键点的尺度和角度进行调整,使得你可以看到关键点的特性(即关键点的大小和方向)。

效果如下:

什么样的点会被认为是 SIFT 特征点?

角点(Corners): 图像中像素强度发生急剧变化的地方,比如边缘的交汇处。角点具有良好的局部不变性。

纹理丰富的区域: 图像中具有明显方向性或模式的区域,比如斑点或条纹。

图像中的独特结构: 比如物体的某些显著部分、边缘的转折点等。

SIFT 计算每个特征点周围的梯度信息,生成 128 维的描述符向量。这些描述符不仅能表示特征点周围的图像特征,还具有 旋转、缩放、光照不变性。这些描述符用于后续的图像匹配和识别。

在 SIFT 中,圈圈的大小代表每个关键点的 尺度(scale)。具体来说,SIFT 会在不同的尺度(即图像的不同分辨率)下检测图像中的特征点,这使得它可以检测到图像中大小不同的结构或特征。在绘制关键点时,圈圈的大小越大,表示该关键点的特征在更大的尺度上被检测到,也就是说,它代表了图像中较大的局部结构。

2.编写代码显示一个 GUI(图形用户界面),用户可以单击图像上的 4 个点。让用户选择 h1.jpg 上的 4 个点和 h2.jpg 上的 4 个点

py 复制代码
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import linalg
import random as rm
import math
import cv2
from tkinter import *
from tkinter import messagebox
from PIL import Image, ImageTk

# the interchange of window axis to image axis
event2canvas = lambda e, c: (c.canvasx(e.x), c.canvasy(e.y))

#function to be called when mouse is clicked
def printcoords(event):
    #outputting x and y coords to console
    cx, cy = event2canvas(event, canvas)
    # the axis of window and image is on the contrast
    click = [event.y,event.x,cy,cx]
    print ("(%d, %d) / (%d, %d)" % (event.y,event.x,cy,cx))
    points.append(click)
    i = len(points)
    canvas.create_text(300+i*50, 670, text="(%d,%d)"%(event.y,event.x),font=("Arial",10))

这段代码的主要工作是在引入合适的库,然后图像缩放与保存,坐标转换函数,用于在鼠标点击事件发生时,将窗口坐标系的坐标 e.x 和 e.y 转换为画布上的坐标然后是鼠标点击事件处理函数:

event.x 和 event.y 是鼠标点击的窗口坐标。

使用 event2canvas 函数将窗口坐标 event.x 和 event.y 转换为画布坐标 cx 和 cy。

click = [event.y, event.x, cy, cx] 将窗口坐标和转换后的画布坐标存储在 click 变量中,表示点击的位置。

print() 输出坐标到控制台。

points.append(click) 将点击的坐标点保存到 points 列表中,用于后续处理。

canvas.create_text() 在画布的指定位置(图像下方)显示鼠标点击的坐标,i 是点击的次数。

py 复制代码
# build a window object
root = Tk()

#box for points
points=[]

#size of the window
root.geometry("900x700")
root.title("CA1 Part3")
canvas=Canvas(root,bg='white',width=900,height=700)
canvas.pack()
# label = Label(root,text="You need to click 4 points on h1",font=("Arial",25),fg="black")

#setting up a tkinter canvas with scrollbars
# not use in this task
# frame = Frame(root, bd=2, relief=SUNKEN)
# frame.grid_rowconfigure(0, weight=1)
# frame.grid_columnconfigure(0, weight=1)
# xscroll = Scrollbar(frame, orient=HORIZONTAL)
# xscroll.grid(row=1, column=0, sticky=E+W)
# yscroll = Scrollbar(frame)
# yscroll.grid(row=0, column=1, sticky=N+S)
# canvas = Canvas(frame, bd=0, xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
# canvas.grid(row=0, column=0, sticky=N+S+E+W)
# xscroll.config(command=canvas.xview)
# yscroll.config(command=canvas.yview)
# frame.pack(fill=BOTH,expand=1)

#adding the image
img_h1 = Image.open('h1_1.jpg')
img_h1_photo = ImageTk.PhotoImage(img_h1)
img_h2 = Image.open('h2.jpg')
img_h2_photo = ImageTk.PhotoImage(img_h2)

Im = canvas.create_image(0,0,image=img_h1_photo,anchor=NW)
canvas.config(scrollregion=canvas.bbox(ALL))
Tx = canvas.create_text(200, 620, text="You need to click 4 points on h1",font=("Arial",20))
canvas.create_text(180, 670, text="The position of the points:",font=("Arial",20))

#mouseclick event
canvas.bind("<ButtonPress-1>",printcoords)

def change_img():
    # set the event of button
    canvas.itemconfig(Im,image=img_h2_photo) 
    canvas.itemconfig(Tx,text="You need to click 4 points on h2")


# set button
button = Button(root, text='Change to h2', command=change_img)  
button.place(x=750, y=650)

# start
root.mainloop()

代码逻辑:

1.这里创建了一个 Tkinter 的根窗口 root,并将其大小设置为 900x700 像素。canvas 是一个 Tkinter 的画布对象,用于在窗口中绘制图像和文本,并作为鼠标点击事件的区域。

2.载入与显示图像,并转换成Tkingter的格式

3.显示提示信息

4.鼠标点击事件的绑定

5.图像切换功能

6.mainloop() 方法启动 Tkinter 的事件循环,等待用户的操作,并保持窗口持续运行。

初始状态下,窗口中显示 h1_1.jpg 图像,并在底部提示用户点击四个点。每次点击鼠标左键后,程序会输出点击的坐标。

窗口下方有一个按钮,点击该按钮后,画布上的图像会从 h1_1.jpg 切换为 h2.jpg,提示信息也会随之改变为"你需要在 h2 上点击 4 个点"。

注释掉的部分是窗口滑轮的部分

3.单应性矩阵

计算h1.jpg到h2.jpg的单应性矩阵,并显示单应性矩阵。利用单应性矩阵将h1.jpg转换为h2.jpg,并显示结果:

py 复制代码
# H: homography computing
def hmat(points):
    A = np.zeros([8,9])
    b = []
    for i in range(0,4):
        # A matrix
        h1_x = points[i][2]
        h1_y = points[i][3]
        h2_x = points[i+4][2]
        h2_y = points[i+4][3]
        A[2*i][:] = [h1_x,h1_y,1,0,0,0,-h2_x*h1_x,-h2_x*h1_y,-h2_x]
        A[2*i+1][:] = [0,0,0,h1_x,h1_y,1,-h2_y*h1_x,-h2_y*h1_y,-h2_y]

    # SVD 
    _, _, vt = linalg.svd(A)
    H = vt[-1].reshape(3,3)
    # H(3,3) = 1
    H = H / H[2,2] 

    return H

这段代码用于计算单应性矩阵(Homography matrix),它用于在两个图像平面之间进行变换。单应性矩阵是一种 3x3 的矩阵,通常用于将一个平面的点映射到另一个平面,比如从图像 h1 上的四个点映射到图像 h2 上的四个对应点。通过给定的 4 对对应点,代码可以计算出用于变换的单应性矩阵 H。points 是一个包含 8 个点击点的数组,前 4 个点对应图像 h1,后 4 个点对应图像 h2

这时候我们已经可以计算单应性矩阵了,接下来就是计算环节。

py 复制代码
# import image
h1 = cv2.imread('h1_1.jpg',cv2.IMREAD_COLOR)
h2 = cv2.imread('h2.jpg',cv2.IMREAD_COLOR)
# from BGR to RGB, CV2 default reading BGR
h1 = cv2.cvtColor(h1, cv2.COLOR_BGR2RGB)
h2 = cv2.cvtColor(h2, cv2.COLOR_BGR2RGB)

# compute the size of the image after transmitting
def get_size(h1,H):
    [row,col,c] = h1.shape
    
    # 4 cornor of the image h1
    lt = np.array([[0,0,1]])
    rt = np.array([[0,col,1]])
    lb = np.array([[row,0,1]])
    rb = np.array([[row,col,1]])
    
    # edge matrix
    edge = np.concatenate((lt,rt,lb,rb),axis = 0).T

    T_edge = np.dot(H,edge)
    # normalize
    T_edge = T_edge[0:2,:]/T_edge[2,:]
    T_edge = T_edge
    print(T_edge)
    
    return np.max(T_edge[0,:]),np.min(T_edge[0,:]),np.max(T_edge[1,:]),np.min(T_edge[1,:])

# transform h1 to h2
def transfer(h1,H):
    
    # get output size
    [max_x, min_x, max_y, min_y] = get_size(h1,H)
    print([max_x, min_x, max_y, min_y])
    x_diff = int(round(max_x - min_x+10))
    y_diff = int(round(max_y - min_y+10))

    [row,col,c] = h1.shape
    # initialize the output image 
    h12h2 = np.zeros([x_diff+100,y_diff+100,c])
#     print(h1.shape,h12h2.shape)

    for j in range(0,row):
        for k in range(0,col):
            # h1 positon
            p = np.array([[j,k,1]]).T
            Tp = np.dot(H,p)
            
            # normalize and move the image to the central
            x = int(round(Tp[0,0]/Tp[2,0])-min_x)
            y = int(round(Tp[1,0]/Tp[2,0])-min_y)

            h12h2[x,y] = h1[j,k][:]

    return h12h2.astype(int)

# show the original image h1

plt.imshow(h1)
# transfer h1 to h2 plane
h12h2 = transfer(h1,H)
plt.imshow(h12h2)
  1. 图像读取与颜色空间转换
  2. 计算转换后的图像尺寸(get_size 函数)
  3. 图像转换(transfer 函数)
  4. 可视化原始图像
相关推荐
云空16 分钟前
《Python 与 SQLite:强大的数据库组合》
数据库·python·sqlite
凤枭香1 小时前
Python OpenCV 傅里叶变换
开发语言·图像处理·python·opencv
测试杂货铺1 小时前
外包干了2年,快要废了。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
艾派森1 小时前
大数据分析案例-基于随机森林算法的智能手机价格预测模型
人工智能·python·随机森林·机器学习·数据挖掘
小码的头发丝、2 小时前
Django中ListView 和 DetailView类的区别
数据库·python·django
Chef_Chen2 小时前
从0开始机器学习--Day17--神经网络反向传播作业
python·神经网络·机器学习
千澜空3 小时前
celery在django项目中实现并发任务和定时任务
python·django·celery·定时任务·异步任务
斯凯利.瑞恩3 小时前
Python决策树、随机森林、朴素贝叶斯、KNN(K-最近邻居)分类分析银行拉新活动挖掘潜在贷款客户附数据代码
python·决策树·随机森林
yannan201903133 小时前
【算法】(Python)动态规划
python·算法·动态规划
蒙娜丽宁3 小时前
《Python OpenCV从菜鸟到高手》——零基础进阶,开启图像处理与计算机视觉的大门!
python·opencv·计算机视觉