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

news/2024/9/21 3:16:48/

SIFT_0">1.SIFT特征点和描述符

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)

在这里插入图片描述
提取图片,以灰度图像输出

#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 个点

%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 consolecx, cy = event2canvas(event, canvas)# the axis of window and image is on the contrastclick = [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 是点击的次数。

# 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 buttoncanvas.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,并显示结果:

# H: homography computing
def hmat(points):A = np.zeros([8,9])b = []for i in range(0,4):# A matrixh1_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) = 1H = H / H[2,2] return H

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

# 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 h1lt = np.array([[0,0,1]])rt = np.array([[0,col,1]])lb = np.array([[row,0,1]])rb = np.array([[row,col,1]])# edge matrixedge = np.concatenate((lt,rt,lb,rb),axis = 0).TT_edge = np.dot(H,edge)# normalizeT_edge = T_edge[0:2,:]/T_edge[2,:]T_edge = T_edgeprint(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 positonp = np.array([[j,k,1]]).TTp = np.dot(H,p)# normalize and move the image to the centralx = 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 h1plt.imshow(h1)
# transfer h1 to h2 plane
h12h2 = transfer(h1,H)
plt.imshow(h12h2)
  1. 图像读取与颜色空间转换
  2. 计算转换后的图像尺寸(get_size 函数)
  3. 图像转换(transfer 函数)
  4. 可视化原始图像

http://www.ppmy.cn/news/1528203.html

相关文章

类型转换等 面试真题

题目1 请问哪个结果为NaN A. 123null B. 123‘1’ C. 123/0 D. 123undefined 在这四个表达式中&#xff0c;只有D. 123 undefined 的结果是 NaN&#xff0c;原因如下&#xff1a; A. 123 null 结果是&#xff1a;123原因&#xff1a;null 在数值运算中会被自动转换为 0&a…

代码随想录打卡Day38

今天真的好累。。。第三道题debug了很久&#xff0c;搞得精疲力竭。。。 322. 零钱兑换 这道题感觉有点思路但是不能完全写对&#xff0c;直接看视频去了&#xff0c;我发现只要不是纯粹的背包问题都是考虑用一维dp数组来做&#xff0c;这道题目就是用一维dp数组来做&#xf…

基于SpringBoot的校园二手商品交易平台的设计与实现

文未可获取一份本项目的java源码和数据库参考。 一、课题研究背景意义及现状 1.课题背景 随着社会的发展&#xff0c;低碳经济生活已成为当今世界发展的主题&#xff0c;物品循环利用、回收再造成为了社会关注的焦点。调查发现&#xff0c;随着大学生购买力的增强&#xff0…

LeetCode746:使用花费最小爬楼梯

题目链接&#xff1a;746. 使用最小花费爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 代码如下 class Solution { public:int minCostClimbingStairs(vector<int>& cost) {int m cost.size();if(m 1) return min(cost[1], cost[0]);if(m 0) return cost[0]…

CAPL_构建基于UDS的刷写学习—03 S19文件的读取

【VCU】详解S19文件&#xff08;S-record&#xff09;_s19文件格式详解-CSDN博客这里给出了另外一个大佬的文章&#xff0c;已经写的很详细了&#xff0c;大家可以参考和学习【VCU】详解S19文件&#xff08;S-record&#xff09;_s19文件格式详解-CSDN博客 本人总结一下S19格式…

腹腔镜工具识别与定位系统源码分享

腹腔镜工具识别与定位检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comp…

共享内存C(Linux)

在学习的时候遇到问题&#xff0c;就是将结构体作为共享内存时将string类型置入结构体内&#xff0c;导致程序出现段错误&#xff0c;后来经过排查发现共享内存是c语言的库不支持string类型&#xff0c;需要用char name[20]代替。 1.在Linux中如何查看共享内存 &#xff08;1&…

C语言习题~day32

请问该程序的输出是多少&#xff08;&#xff09; #include<stdio.h> int main(){ unsigned char i 7; int j 0; for(;i > 0;i - 3){ j; } printf("%d\n", j); return 0; }A.2 B.死循环 C.173 D.172 无符号字符型的取值范围是 0 到 255。 第一次循环…