• 【计算机视觉】OpenCV篇(3)


    图像的几何变换从原理上看主要包括两种:基于2×3矩阵的仿射变换(平移、缩放、旋转和翻转等)、基于3×3矩阵的透视变换

    •  仿射变换

    基本的图像变换就是二维坐标的变换:从一种二维坐标(x,y)到另一种二维坐标(u,v)的线性变换:

    如果写成矩阵的形式,那就是:

    作如下定义:

    矩阵T(2×3)就称为仿射变换的变换矩阵,R为线性变换矩阵,t为平移矩阵,简单来说,仿射变换就是线性变换+平移。变换后直线依然是直线,平行线依然是平行线,直线间的相对位置关系不变,因此非共线的三个对应点便可确定唯一的一个仿射变换,线性变换4个自由度+平移2个自由度→仿射变换自由度为6。

    仿射变换在OpenCV中的实现如下:

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    img = cv2.imread('drawing.jpg')
    rows, cols = img.shape[:2]
    
    # 变换前的三个点
    pts1 = np.float32([[50, 65], [150, 65], [210, 210]])
    # 变换后的三个点
    pts2 = np.float32([[50, 100], [150, 65], [100, 250]])
    
    # 生成变换矩阵,维数:2*3
    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)平移

    平移就是x和y方向上的直接移动,可以上下/左右移动,自由度为2,变换矩阵可以表示为:

    # 平移图片
    import numpy as np
    
    rows, cols = img.shape[:2]
    
    # 定义平移矩阵,需要是numpy的float32类型
    # x轴平移100,y轴平移50
    M = np.float32([[1, 0, 100], [0, 1, 50]])
    dst = cv2.warpAffine(img, M, (cols, rows))
    
    cv2.imshow('shift', dst)
    cv2.waitKey(0)
    

      

     

    (1)通过比例进行缩放

    import cv2 as cv
    import numpy as np
    
    # 图片缩放
    img = cv.imread('images/animal.jpg', flags=1)  # flags=1读取为彩色,flags=0读取为灰度
    cv.imshow('i', img)
    h, w, channel = img.shape  # 以行列形式存储, 第几行到第几行为图像高度
    dst_h = int(h*0.5)
    dst_w = int(w*0.5)
    # 最近邻域差值 双线性插值 像素关系重采样 立方差值
    dst = cv.resize(img, (dst_w, dst_h))  # 默认双线性差值
    cv.imshow('img', dst)
    cv.waitKey(0)
    

    OpenCV提供了resize函数来改变图像的大小,C++中的函数原型如下:

    void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR );

    函数参数说明:

    src:输入,原图像,即待改变大小的图像;

    dst:输出,改变大小之后的图像,这个图像和原图像具有相同的内容,只是大小和原图像不一样而已;

    dsize:输出图像的大小。如果这个参数不为0,那么就代表将原图像缩放到这个Size(width,height)指定的大小;如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:

    dsize = Size(round(fx*src.cols), round(fy*src.rows))

    其中,fx和fy就是下面要说的两个参数,是图像width方向和height方向的缩放比例。

    fx:width方向的缩放比例,如果它是0,那么它就会按照(double)dsize.width/src.cols来计算;

    fy:height方向的缩放比例,如果它是0,那么它就会按照(double)dsize.height/src.rows来计算;

    interpolation:这个是指定插值的方式,图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,有以下几种:

    • INTER_NEAREST - 最邻近插值
    • INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
    • INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
    • INTER_CUBIC - 4x4像素邻域内的双立方插值
    • INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值

    函数使用说明:

    1. dsize和fx/fy不能同时为0,要么你就指定好dsize的值,让fx和fy空置直接使用默认值,就像resize(img, imgDst, Size(30,30)); 要么你就让dsize为0,指定好fx和fy的值,比如fx=fy=0.5,那么就相当于把原图两个方向缩小一倍!
    2. 至于最后的插值方法,正常情况下使用默认的双线性插值就够用了。几种常用方法的效率是:最邻近插值>双线性插值>双立方插值>Lanczos插值;但是效率和效果成反比,所以根据自己的情况酌情使用。
    3. 正常情况下,在使用之前dst图像的大小和类型都是不知道的,类型从src图像继承而来,大小也是从原图像根据参数计算出来。但是如果你事先已经指定好dst图像的大小,那么你可以通过下面这种方式来调用函数:
    resize(src, dst, dst.size(), 0, 0, interpolation);
    

    (2)通过矩阵变换进行缩放  

    import cv2 as cv
    import numpy as np
    
    # 图片缩放
    img = cv.imread('../images/moon.jpg', flags=1)  # flags=1读取为彩色,flags=0读取为灰度
    h, w = img.shape[:2]
    mat_shift = np.float32([[0.5, 0, 0], [0, 0.5, 0]])  # 缩放矩阵
    dst = cv.warpAffine(img, mat_shift, (int(w/2), int(h/2)))
    cv.imshow('img1', img)
    cv.imshow('img2', dst)
    cv.waitKey(0)
    

     

  • 相关阅读:
    从一个iOS毛头小子到现在的高级工程师, 我总结了一些经验,先跟大家分享一下一些好的资料
    iOS面试题---Objective_C语言特性:分类、扩展、代理、通知、KVO、KVC、属性
    200道iOS面试题面试题整理,底层、技术亮点公司需要的这里都有
    [iOS 开发] iOS 开发从菜鸟到高手?听听他们怎么说
    2020年面向iOS开发人员的知识点总结(更新中)
    OC项目转Swift指南
    来自老程序员的10条中肯建议
    面对职业瓶颈,iOS 开发人员应该如何突破?
    憨憨程序猿,不要让你的技术被简历埋没了
    总结:实现线程同步的八种方式
  • 原文地址:https://www.cnblogs.com/carsonzhu/p/10763438.html
Copyright © 2020-2023  润新知