Opencv_Python 官方教程 第九章 几何变换

本文介绍了如何使用OpenCV库进行图像变换,包括缩放、旋转和平移等基本操作,以及更复杂的仿射变换和透视变换。通过cv2.warpAffine和cv2.warpPerspective函数,可以实现对图像的移动、旋转、仿射和透视变换。此外,文章还讨论了不同的插值方法对缩放效果的影响,并提供了交互式选择点进行特定区域放大的示例。
摘要由CSDN通过智能技术生成

目标

• 学习对图像进行各种几个变换,例如移动,旋转,仿射变换等。
• 将要学到的函数有:cv2.getPerspectiveTransform。
变换
OpenCV 提供了两个变换函数,cv2.warpAffinecv2.warpPerspective
使用这两个函数你可以实现所有类型的变换。cv2.warpAffine 接收的参数是
2 × 3 的变换矩阵,而 cv2.warpPerspective 接收的参数是 3 × 3 的变换矩
阵。

图像变换类型

  1. 刚体变换
    刚体变换仅限于平移,旋转,翻转(镜像)
  2. 仿射变换
  3. 投影(透视)变换
    如果一副图像中的直线经过变换.直线仍然保持,但平行关系不保持

扩展缩放

图像缩小

在这里插入图片描述

图像扩大

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 扩展缩放只是改变图像的尺寸大小
  • resize
  • 缩放时我们推荐使用 cv2.INTER_AREA,在扩展时我们推荐使用 v2.INTER_CUBIC(慢) 和 v2.INTER_LINEAR

resize(src, dsize, fx=0, fy=0, interpolation)
- src,输入图像
- dsize:输出图像的大小
- fx,fy:缩放因子;可以通过对图像进行倍数的放大和缩小,也可以通过dsize参数直接的输入尺寸大小
-interpolation(插值):这个是指定插值的方式,图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,有以下几种:

  • INTER_NEAREST - 最邻近插值
    INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
    INTER_CUBIC - 4x4像素邻域内的双立方插值
    INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值

img = cv2.imread('./Pytorch.png')
print('resize before: ',img.shape) # resize before:  (235, 529, 3)
# 下面的 None 本应该是输出图像的尺寸,但是因为后边我们设置了缩放因子
# 因此这里为 None
res1 = cv2.resize(img,None,fx=2,fy=2,interpolation=cv2.INTER_CUBIC)
print('resize after: ',res1.shape) # resize after:  (470, 1058, 3)

height,width = img.shape[:2]
res2 = cv2.resize(img,(2*height,2*width),interpolation=cv2.INTER_CUBIC)

while(True):
    cv2.imshow('res1 fx=2,fy=2 ',res1)
    cv2.imshow('res2 (2*height,2*width) ',res2)

    if cv2.waitKey(1) & 0XFF == 27:
        break
cv2.destroyAllWindows()

位置变换

平移

在这里插入图片描述

  • 平移就是将对象换一个位置。如果你要沿(x,y)方向移动,移动的距离是(tx,ty),你可以以下面的方式构建移动矩阵:
    在这里插入图片描述

cv2.warpAffine(src,M,dsize,dst,flags,borderMode);

  • M:变换矩阵(2*3的转换矩阵),可以通过其他函数获得,或者通过手动输入

  • dsize:输出图像的大小

  • dst:变换后的图像

  • flags:插值实验法

  • borderMode:边界处理方式.
    -cv2.BORDER_CONSTANT 添加常数值为边界值,还需要指定另外一个参数 borderValue,borderValue默认值为(0, 0, 0),当然你可以指定其他值,三个元素分别表示 BGR
    cv2.BORDER_REPLICATE 重复最后一个元素。例如: aaaaaa| abcdefgh|hhhhhhh ------- 边界复制
    cv2.BORDER_REFLECT 边界元素的镜像。比如: fedcba|abcdefgh|hgfedcb
    cv2.BORDER_REFLECT — 轴对称

  • 将图像移动移动了(100,50)个像素。

img = cv2.imread('./Pytorch.png', 0)
rows, cols = img.shape
print(rows,cols)

M = np.float32([[1, 0, 100], [0, 1, 50]])  # x移动100,y移动50
dst = cv2.warpAffine(img, M, (cols, rows))  # 进行变换,img是原图像,M变换矩阵,(cols,rows)是输出图像大小
print(cols,rows)


cv2.imshow('img', dst)
cv2.imshow('img1', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

函数 cv2.warpAffine() 的第三个参数的是输出图像的大小,它的格式应该是图像的(宽,高)。应该记住的是图像的宽对应的是列数,高对应的是行 数。

旋转

在这里插入图片描述

在这里插入图片描述
cv2.getRotationMatrix2D(center,angle,scale) ---- 获得M矩阵
- center:表示选择中心(x,y)
- angle:旋转角度
- scale:图像缩放因子

import cv2
import numpy as np
img=cv2.imread('./Pytorch.png')
rows,cols=img.shape[0],img.shape[1]


# 这里的第一个参数为旋转中心,第二个为旋转角度,第三个为旋转后的缩放因子
# 可以通过设置旋转中心,缩放因子,以及窗口大小来防止旋转后超出边界的问题
M=cv2.getRotationMatrix2D((cols/2,rows/2),45,0.6)
# 第三个参数是输出图像的尺寸中心
dst=cv2.warpAffine(img,M,(2*cols,2*rows))
while(1):
    cv2.imshow('img',dst)
    if cv2.waitKey(1)&0xFF==27:
        break
cv2.destroyAllWindows()

14.4仿射变换

在这里插入图片描述
在这里插入图片描述
cv2.getAffineTransform(src,dst)

  • src:原图像的三点位置
  • dst:目标图像三个点的位置

允许图像任意倾斜,而且允许图像在两个方向上任意伸缩的变换,仿射变换可以保持原来的线共点,点共线的关系不变等,但是不能保持原来的线段长度不变,和夹角不变

  • 在仿射变换中,原图中所有的平行线在结果图像中同样平行。为了创建这个矩阵我们需要从原图像中找到三个点以及他们在输出图像中的位置。然后cv2.getAffineTransform 会创建一个 2x3 的矩阵,最后这个矩阵会被传给
    函数 cv2.warpAffine
img = cv2.imread('./Pytorch.png')
rows, cols = img.shape[0], img.shape[1]

pts1 = np.float32([[50, 50], [200, 50], [50, 200]]) # 原图的是三个点
pts2 = np.float32([[10, 100], [200, 50], [100, 250]]) # 仿射变换后的三个点

M = cv2.getAffineTransform(pts1,pts2)
dst = cv2.warpAffine(img,M,(cols,rows))

plt.subplot(121)
plt.imshow(img)
plt.title('Input')
plt.subplot(122)
plt.imshow(dst)
plt.title('Output')
plt.show()
  1. 仿射变换案例 放大指定区域的图片

import cv2
import numpy as np

def OnMouseEvent( event, x, y, flags, param):
    global lbtDownPos
    global pos
    global pointList
    img = param

    # 需要忽略的鼠标事件
    ignoreEvent = [cv2.EVENT_MBUTTONDOWN, cv2.EVENT_MBUTTONUP, cv2.EVENT_MBUTTONDBLCLK, cv2.EVENT_MOUSEWHEEL,
                        cv2.EVENT_MOUSEHWHEEL,cv2.EVENT_MOUSEMOVE,cv2.EVENT_LBUTTONDBLCLK, cv2.EVENT_RBUTTONDBLCLK, cv2.EVENT_RBUTTONDOWN, cv2.EVENT_RBUTTONUP]
    needRecordEvent = [ cv2.EVENT_LBUTTONDOWN, cv2.EVENT_LBUTTONUP]  # 需要记录当前信息的鼠标事件

    if event == cv2.EVENT_LBUTTONUP:  # 鼠标右键释放
        pos = (x,y)
        print("OnMouseEvent EVENT_LBUTTONUP  鼠标右键释放:",pos)
        n = len(pointList)
        if n < 3:
            pointList.append(pos)
            n+=1
            cv2.putText(img, '.', (x - 10, y), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=3, color=(255, 0, 0))
            cv2.putText(img, f'select point{n}:({x},{y})', (x + 20, y), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.3, color=(255, 0, 0))
        else:
            print('n已经超过三个点了')

    elif event == cv2.EVENT_LBUTTONDOWN:  # 鼠标右键点击
        print("OnMouseEvent EVENT_LBUTTONDOWN   鼠标右键点击:", lbtDownPos)

    else:lbtDownPos = None


def getPoint(imgfile):
    global pos   # 正在点击的坐标
    global pointList  # 已经点击的坐标

    pointList = []
    img = cv2.imread(imgfile)
    imgbak = np.array(img)
    rows,cols = img.shape[:2]
    winName = 'select three point'
    cv2.namedWindow(winName)
    cv2.setMouseCallback(winName, OnMouseEvent, img)
    print("请将要单独放大的部分从其左上角、左下角、右下角分别鼠标左键点击选择三个点,选择后在图像上有提示信息,选择完成后按ESC退出")


    while True:#通过鼠标左键点击选择三个点,分别代表要映射到左上、左下和右下三个点
        cv2.imshow(winName, img)
        ch = cv2.waitKey(100)
        if ch == 27: break   # 按Esc后表面三个键已经按完了

    destPoint = [(0,0),(0,rows),(cols,rows)]
    if len(pointList)==3:
        pts1 = np.float32(pointList)
        pts2 = np.float32(destPoint)
        M = cv2.getAffineTransform(pts1, pts2)
        dst = cv2.warpAffine(imgbak, M, (cols, rows))
        cv2.imshow(winName, dst)
        cv2.imshow('img', img)
        ch = cv2.waitKey(0)
    else:
        print("没有选择足够的点")
getPoint('Pytorch.png')

14.5 透视变换

  • 将二维的图片投影到一个三维视图上,然后在转换到二维视图上
    透视变换(Perspective Transformation)是指利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。简而言之,就是将一个平面通过一个投影矩阵投影到指定平面上。
  • 对于视角变换,我们需要一个 3x3 变换矩阵。在变换前后直线还是直线。
    要构建这个变换矩阵,你需要在输入图像上找 4 个点,以及他们在输出图
    像上对应的位置。这四个点中的任意三个都不能共线。这个变换矩阵可以有
    函数 cv2.getPerspectiveTransform() 构建。然后把这个矩阵传给函数
    cv2.warpPerspective。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img=cv2.imread('Pytorch.png')
rows,cols,ch=img.shape
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M=cv2.getPerspectiveTransform(pts1,pts2)
dst=cv2.warpPerspective(img,M,(300,300))
plt.subplot(121)
plt.imshow(img)
plt.title('Input')
plt.subplot(122)
plt.imshow(dst)
plt.title('Output')
plt.show()
  1. 透视变换案例 放大指定区域的图片
import cv2
import numpy as np

def OnMouseEvent( event, x, y, flags, param):
    global lbtDownPos
    global pos
    global pointList
    img = param

    # 需要忽略的鼠标事件
    ignoreEvent = [cv2.EVENT_MBUTTONDOWN, cv2.EVENT_MBUTTONUP, cv2.EVENT_MBUTTONDBLCLK, cv2.EVENT_MOUSEWHEEL,
                        cv2.EVENT_MOUSEHWHEEL,cv2.EVENT_MOUSEMOVE,cv2.EVENT_LBUTTONDBLCLK, cv2.EVENT_RBUTTONDBLCLK, cv2.EVENT_RBUTTONDOWN, cv2.EVENT_RBUTTONUP]
    needRecordEvent = [ cv2.EVENT_LBUTTONDOWN, cv2.EVENT_LBUTTONUP]  # 需要记录当前信息的鼠标事件

    if event == cv2.EVENT_LBUTTONUP:  # 鼠标右键释放
        pos = (x,y)
        print("OnMouseEvent EVENT_LBUTTONUP  鼠标右键释放:",pos)
        n = len(pointList)
        if n < 4:
            pointList.append(pos)
            n+=1
            cv2.putText(img, '.', (x - 10, y), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=3, color=(255, 0, 0))
            cv2.putText(img, f'select point{n}:({x},{y})', (x + 20, y), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.3, color=(255, 0, 0))
        else:
            print('n已经超过三个点了')

    elif event == cv2.EVENT_LBUTTONDOWN:  # 鼠标右键点击
        print("OnMouseEvent EVENT_LBUTTONDOWN   鼠标右键点击:", lbtDownPos)

    else:lbtDownPos = None


def getPoint(imgfile):
    global pos   # 正在点击的坐标
    global pointList  # 已经点击的坐标

    pointList = []
    img = cv2.imread(imgfile)
    imgbak = np.array(img)
    rows,cols = img.shape[:2]
    winName = 'select three point'
    cv2.namedWindow(winName)
    cv2.setMouseCallback(winName, OnMouseEvent, img)
    print("请将要单独放大的部分从其左上角、左下角、右下角,右上角分别鼠标左键点击选择三个点,选择后在图像上有提示信息,选择完成后按ESC退出")


    while True:#通过鼠标左键点击选择三个点,分别代表要映射到左上、左下和右下三个点
        cv2.imshow(winName, img)
        ch = cv2.waitKey(100)
        if ch == 27: break   # 按Esc后表面三个键已经按完了

    destPoint = [(0,0),(0,rows),(cols,rows),(cols,0)]
    if len(pointList)==4:
        pts1 = np.float32(pointList)
        pts2 = np.float32(destPoint)
        M = cv2.getPerspectiveTransform(pts1, pts2)
        dst = cv2.warpPerspective(imgbak, M, (cols, rows))
        cv2.imshow(winName, dst)
        cv2.imshow('img', img)
        ch = cv2.waitKey(0)
    else:
        print("没有选择足够的点")
getPoint('Pytorch.png')

使用透视变换放大图片的效果好于使用仿射变换图片的效果
·透视变换与放射变换的关系·

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值