第5章 - 几何变换

第五章-几何变换

    • one. 缩放:
    • two. 翻转:
    • three. 仿射:
      • 1. 平移:
      • 2. 旋转:
      • 3. 更多复杂的仿射变换:
    • four. 透视:
    • five. 重映射:
      • 1. 映射参数的理解:
      • 2. 复制:
      • 3. 绕x轴旋转:
      • 4. 绕y轴旋转:
      • 5. 绕x、y轴旋转:
      • 6. x、y轴进行互换:
      • 7. 图像缩放:

几何变换是指将一幅图像映射到另 一幅图像内的操作。OpenCV提供了多个与映射有关的函数。

根据OpenCV函数的不同,映射关系可以划分为缩放、翻转、仿射变换、透视、重映射等。

one. 缩放:

在OpenCV中,使用cv2.resize()实现对图像缩放:

  • dst = cv2.resize(src, dsize, [, fx [, fy [, interpolation]]])

    • dst:代表输出的目标图像,该图像的类型与src相同,其大小为dsize(当该值非零时),或者可以通过src.size()、fx、fy计算得到。
    • src:代表需要缩放的原始图像。
    • dsize:代表输出图像大小。
    • fx:代表水平方向的缩放比例。
    • fy:代表垂直方向的缩放比例。
    • interpolation:代表插值方式,具体如下表所示。

    image-20211011194455301

    image-20211011194626571

在cv2.resize()函数中,目标图像的大小可以通过"参数dsize"或者"参数fx和fy"二者之一来确定,具体情况如下:

  1. 通过参数dsize指定:

    ​ 如果指定参数dsize的值,则无论是否指定了参数fx和fy的值,都由参数dsize来决定目标图像的大小。
    ​ 此时需要注意的是,dsize内第1个参数对应缩放后图像的宽度(width,即列数cols,与参数fx相关),第2个参数对应缩放后图像的高度(height,即行数rows,与参数fy相关)。

    指定参数dsize的值时,x方向的缩放大小(参数fx)为:

    • (double)dsize.width/src.cols

    同时,y方向的缩放大小(参数fy)为:

    • (double)dsize.height/src.rows
  2. 通过参数fx和fy指定:
    如果参数dsize的值是None,那么目标图像的大小通过参数fx和fy来决定。此时,目标图像的大小为:dsize=Size(round(fx * src.cols),round(fy * src.rows))

插值

​ 插值是指在对图像进行几何处理时,给无法直接通过映射得到值的像素点赋值。例如,将图像放大为原来的2倍,必然会多出一些无法被直接映射值的像素点,对于这些像素点,插值方式决定了如何确定它们的值。除此以外,还会存在一些非整数的映射值,例如,反向映射可能会把目标图像中的像素点值映射到原始图像中的非整数值对应的位置上,当然原始图像内是不可能存在这样的非整数位置的,即目标图像上的该像素点不能对应到原始图像的某个具体位置上,此时也要对这些像素点进行插值处理,以完成映射。

注意:

​ 函数cv2.resize()能实现对原始图像的缩放功能,需要注意的是,开始运算前,操作前的目标图像dst自身的大小、类型与最终得到的目标图像dst是没有任何关系的。目标图像dst的最终大小和类型是通过src、dsize、fx、fy指定的。如果想让原始图像调整为和目标图像一样大,则必须通过上述属性指定。

​ 当缩小图像时,使用区域插值方式(INTER_AREA)能够得到最好的效果;当放大图像时,使用三次样条插值(INTER_CUBIC)方式和双线性插值(INTER_LINEAR)方式都能够取得较好的效果。三次样条插值方式速度较慢,双线性插值方式速度相对较快且效果并不逊色。

例:

import cv2img = cv2.imread('../lena.bmp')
rows, cols = img.shape[:2]
size = (int(cols * 0.9), int(rows * 0.5))
rst = cv2.resize(img, size)
print('img.shape=', img.shape)
print('rst.shape=', rst.shape)
cv2.imshow('img', img)
cv2.imshow('rst', rst)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
img.shape= (512, 512, 3)
rst.shape= (256, 460, 3)

image-20211011200133472

two. 翻转:

在OpenCV中,图像的翻转采用函数cv2.flip()实现,该函数可以实现图像在水平方向翻转,垂直方向翻转,两个方向同时翻转。

  • dst = cv2.flip( src, filpCode )

    • dst:代表和原始图像具有同样大小、类型的目标图像。
    • src:代表要处理的原始图像。
    • flipCode:代表旋转类型。该参数的意义如表所示。

    image-20211011200633852

该函数中,目标像素点与原始像素点的关系可表述为:
dstij={srcsrc.rows−i−1,j,filcCode=0srci,src.cols−j−1,filcCode>0srcsrc.rows−i−1,src.cols−j−1,filcCode<0dst_{ij} = \begin{cases} src_{src.rows-i-1, \quad j}, \quad filcCode = 0 \\ src_{i, \quad src.cols-j-1}, \quad filcCode > 0 \\ src_{src.rows-i-1, \quad src.cols-j-1}, \quad filcCode < 0 \end{cases}dstij=srcsrc.rowsi1,j,filcCode=0srci,src.colsj1,filcCode>0srcsrc.rowsi1,src.colsj1,filcCode<0

其中,dst是目标像素点,src是原始像素点。

例:

import cv2img = cv2.imread('../lena.bmp')
x = cv2.flip(img, 0)
y = cv2.flip(img, 1)
xy = cv2.flip(img, -1)
cv2.imshow('img', img)
cv2.imshow('x', x)
cv2.imshow('y', y)
cv2.imshow('xy', xy)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211011201407030

three. 仿射:

仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行线是指图像在完成仿射变换后,平行线仍然是平行线。

在OpenCV中的实现仿射函数为cv2.warpAffine(),其通过一个变换矩阵(映射矩阵)M实现变换,具体为:dst(x,y)=src(M11x+M12y+M13,M13x+M22y+M23)dst(x, y) = src(M_{11}x + M_{12}y + M_{13}, M_{13}x + M_{22}y + M_{23} )dst(x,y)=src(M11x+M12y+M13,M13x+M22y+M23)

如图所示,可以通过一个变换矩阵M,将原始图像O变换为仿射图像R

image-20211011203322157

因此,可以采用仿射函数cv2.warpAffine()实现对图像的平移、旋转等操作:

  • dst = cv2.warpAffine( src, M, dsize [, flags [, borderMode [, borderValue ]]])
    • dst:代表仿射后的输出图像,该图像的类型和原始图像的类型相同。dsize决定输出图像的实际大小。
    • src:代表要仿射的原始图像。
    • M:代表一个2×3的变换矩阵。使用不同的变换矩阵,就可以实现不同的仿射变换。
    • dsize:代表输出图像的尺寸大小。
    • flags:代表插值方法,默认为INTER_LINEAR。当该值为WARP_INVERSE_MAP时,意味着M是逆变换类型,实现从目标图像dst到原始图像src的逆变换。
    • borderMode:代表边类型,默认为 BORDER_CONSTANT。当该值为 BORDER_TRANSPARENT 时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。
    • borderValue:代表边界值,默认是0。

​ 通过以上分析可知,在OpenCV中使用函数cv2.warpAffine()实现仿射变换,忽略其可选参数后的语法格式为:

  • dst=cv2.warpAffine(src ,M ,dsize)

其通过转换矩阵M将原始图像src转换为目标图像dst:

  • dst(x,y)=src(M11x+M12y+M13,M13x+M22y+M23)dst(x, y) = src(M_{11}x + M_{12}y + M_{13}, M_{13}x + M_{22}y + M_{23} )dst(x,y)=src(M11x+M12y+M13,M13x+M22y+M23)

因此,进行何种形式的仿射变换完全取决于转换矩阵M。下面分别介绍通过不同的转换矩阵M实现的不同的仿射变换。

1. 平移:

通过转换矩阵M实现将原始图像src转换为目标矩阵dst:

  • dst(x,y)=src(M11x+M12y+M13,M13x+M22y+M23)dst(x, y) = src(M_{11}x + M_{12}y + M_{13}, M_{13}x + M_{22}y + M_{23} )dst(x,y)=src(M11x+M12y+M13,M13x+M22y+M23)

将原始图像src向右侧移动100个像素,向下方移动200个像素其对应的关系为:

  • dst(x, y) = src( x + 100, y + 200)

将上述表达式不成完整,即:

  • dst(x, y) = src( 1 * x + 0 * y + 100, 0 * x + 1 * y + 200)

因此可以初定对饮的转换矩阵M中的各个元素值为:

  • M11=1,M12=0,M13=100,M21=0,M22=1,M23=200M_{11} = 1, \quad M_{12} = 0, \quad M_{13} = 100, M_{21} = 0, \quad M_{22} = 1, \quad M_{23} = 200M11=1,M12=0,M13=100,M21=0,M22=1,M23=200

转换矩阵M:

  • M=[1010001200]M = \begin{bmatrix} 1 & 0 & 100 \\ 0 & 1 & 200 \end{bmatrix}M=[1001100200]
import cv2
import numpy as npimg = cv2.imread('../lena.bmp')
h, w = img.shape[:2]
x = 100
y = 200
M = np.float32([[1, 0, x], [0, 1, y]])
move = cv2.warpAffine(img, M, (w, h))
cv2.imshow('img', img)
cv2.imshow('move', move)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211011205331763

2. 旋转:

​ 在OpenCV中使用函数cv2.warpAffine()对图像进行旋转时,可以通过函数cv2.getRotationMatrix2D()获取转换矩阵。该函数的语法是:

  • retval = cv2.getRotationMatrix2D( center, angle, scale )
    • center:旋转的中心点
    • angle:旋转的角度,正数表示逆时针旋转,负数表示顺时针旋转。
    • scale:变换尺度(缩放大小)

利用函数cv2.getRotationMatrix2D()可以直接生成要使用的转换矩阵M。例如,想要以图像中心为原点,逆时针旋转45度,并将目标图像缩小为原始图像的0.6倍,则在调用函数cv2.getRotationMatrix2D()生成转换矩阵M时的语句为:

  • M = cv2.getRotationMatrix2D( ( height/2, width/2 ), 45, 0.6 )

例如:根据上述要求进行图像旋转

import cv2lena = cv2.imread('../lena.bmp')
h, w = lena.shape[:2]
M = cv2.getRotationMatrix2D((h / 2, w / 2), 45, 0.6)
rotate = cv2.warpAffine(lena, M, (w, h))
cv2.imshow('lena', lena)
cv2.imshow('rotate', rotate)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012120510166

3. 更多复杂的仿射变换:

前面讲了平移和旋转两种仿射变换都比较简单,对于更多复杂的仿射变换,OpenCV提供了函数cv2.getAffineTransFORM()来生成仿射函数cv2.warpAffine()所需要的转换矩阵M。该函数的语法格式是:

  • retval = cv2.getAffineTransform(src, dst)

式中:

  • src:代表输入图像的三个点坐标
  • dst:代表输出图像的三个点坐标

在该函数中,其参数值src和dst是包含三个二维数组(x, y)点的数组。上述参数通过函数cv2.getAffineTransform()定义了两个平行四边形。src和dst中的三个点分别对应平行四边形左上角、右上角、左下角三个点。函数cv2.getAffineTransform()对所指定的点完成映射后,将所有其他点的映射关系按照指定点的关系计算确定。函数cv2.warpAffine()以函数cv2.getAffineTransform()获取的转换矩阵M为参数,将src中的点仿射到dst中。

例:

import cv2
import numpy as npimg = cv2.imread('../lena.bmp')
rows, cols, ch = img.shape
p1 = np.float32([[0, 0], [cols - 1, 0], [0, rows - 1]])
p2 = np.float32([[0, rows * 0.33], [cols * 0.85, rows * 0.25], [cols * 0.15, rows * 0.7]])
M = cv2.getAffineTransform(p1, p2)
dst = cv2.warpAffine(img, M, (cols, rows))
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012145205393

  • 首先构造了两个三分量的点集合p1和p2,分别用来指代原始图像和目标图像内平行四边形的三个顶点(左上角、右上角、左下角)。
  • 然后使用M=cv2.getAffineTransform(p1,p2)获取转换矩阵M。
  • 接下来,dst=cv2.warpAffine(img,M,(cols,rows))完成了从原始图像到目标图像的仿射。

four. 透视:

仿射可以将矩形映射为任意的平行四边形,透视变换则可以将矩形映射为任意的四边形。

透视变换的函数是cv2.warpPerspective(),具体语法是:

  • dst = cv2.warpPerspective( src, M, dsize [, flags [, borderMode [, borderValue]]])
    • dst:代表透视处理后的输出图像,该图像和原始图像具有相同的类型。dsize决定输出图像的实际大小。
    • src: 代表要透视的图像。
    • M:代表一个3×3的变换矩阵。
    • dsize:代表输出图像的尺寸大小。
    • flags:代表插值方法,默认为INTER_LINEAR。当该值为WARP_INVERSE_MAP时,意味着M是逆变换类型,能实现从目标图像dst到原始图像src的逆变换。
    • borderMode:代表边类型,默认为 BORDER_CONSTANT。当该值为 BORDER_TRANSPARENT时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。
    • borderValue:代表边界值,默认是0。

与仿射变换一样,同样使用一个函数来生成函数cv2.warpPerspective()所使用的转换矩阵。该函数是cv2.getPerspectiveTransform(),语法格式为:

  • restval = cv2.getPerspectiveTransform( src, dst )
    • src:代表输入图像的四个顶点的坐标
    • dst:代表输出图像的四个顶点的坐标

需要注意的是,src 参数和 dst 参数是包含四个点的数组。实际使用中,我们可以根据需要控制src中的四个点映射到dst中的四个点。

例:

import cv2
import numpy as npimg = cv2.imread('../demo.bmp')
rows, cols = img.shape[:2]
pts1 = np.float32([[150, 50], [400, 50], [60, 450], [310, 450]])
pts2 = np.float32([[50, 50], [rows-50, 50], [50, cols-50], [rows-50, cols-50]])
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, M, (cols, rows))
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012181112996

  • 指定原始图像中平行四边形的四个顶点pts1,指定目标图像中矩形的四个顶点pts2.
  • 使用 M=cv2.getPerspectiveTransform(pts1,pts2)生成转换矩阵 M。
  • 接下来,使用语句dst=cv2.warpPerspective(img,M,(cols,rows))完成从平行四边形到矩形的转换。

five. 重映射:

把一幅图像内的像素点映射到另一幅图像的指定位置,这个过程称为重映射。OpenCV中有多种重映射的的方式,但是我们有时希望使用自定义的方式来完成重映射。

​ OpenCV内重映射函数cv2.remap()提供了更方便、更自由的映射方式,其语法格式如下:

  • dst = cv2.remap( src, map1, map2, interpolation [, borderMode [, borderValue]])

    • dst:代表目标图像,它和src具有相同的大小和类型。
    • src:代表原始图像。
    • map1:参数有两种可能的值:
      • 表示(x,y)点的一个映射。
      • 表示CV_16SC2 ,CV_32FC1,CV_32FC2类型(x,y)点的x值。
    • map2:参数同样有两种可能的值:
    • 当map1表示(x,y)时,该值为空。
    • 当map1表示(x,y)点的x值时,该值是CV_16UC1,CV_32FC1类型(x,y)点的y值。
    • Interpolation:代表插值方式,这里不支持INTER_AREA方法。
    • borderMode :代表边界模式。当该值为BORDER_TRANSPARENT时,表示目标图像内的对应源图像内奇异点(outliers)的像素不会被修改。
    • borderValue :代表边界值,该值默认为0。

1. 映射参数的理解:

​ **重映射通过修改像素点的位置得到一幅新图像。在构建新图像时,需要确定新图像中每个像素点在原始图像中的位置。因此,映射函数的作用是查找新图像像素在原始图像内的位置。该过程是将新图像像素映射到原始图像的过程,因此被称为反向映射。**在函数 cv2.remap()中,参数map1和参数map2用来说明反向映射,map1针对的是坐标x,map2针对的是坐标y。

​ 需要说明的是,map1和map2的值都是浮点数。因此,目标图像可以映射回一个非整数的值,这意味着目标图像可以“反向映射”到原始图像中两个像素点之间的位置(当然,该位置是不存在像素值的)。这时,可以采用不同的方法实现插值,函数中的interpolation参数可以控制插值方式。正是由于参数map1和参数map2的值是浮点数,所以通过函数cv2.remamp()所能实现的映射关系变得更加随意,可以通过自定义映射参数实现不同形式的映射。

​ 需要注意的是,函数cv2.remap()中参数map1指代的是像素点所在位置的列号,参数map2指代的是像素点所在位置的行号。**例如,我们想将目标图像(映射结果图像)中某个点A映射为原始图像内处于第0行第3列上的像素点B,那么需要将A点所对应的参数map1对应位置上的值设为3,参数map2对应位置上的值设为0。**所以,通常情况下,我们将map1写为mapx,并且将map2写成mapy,以方便理解。

​ 同样,如果想将目标图像(映射结果图像)中所有像素点都映射为原始图像内处于第0行第3列上的像素点B,那么需要将参数map1内的值均设为3,将参数map2内的值均设为0。

例:

  • 用来指定列的参数map1(mapx)内的值均为3。
  • 用来指定行的参数map2(mapy)内的值均为0。
import cv2
import numpy as npimg = np.random.randint(0, 256, size=[4, 5], dtype=np.uint8)
# rows, cols = img.shape
mapx = np.ones(img.shape, np.float32) * 3
mapy = np.ones(img.shape, np.float32) * 0
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
print('img=\n', img)
print('mapx=\n', mapx)
print('mapy=\n', mapy)
print('rst=\n', rst)# 输出结果
img=[[196  22  15  82 189][228 208 215  88 113][159   6 157 101 174][181  95 194 206 111]]
mapx=[[3. 3. 3. 3. 3.][3. 3. 3. 3. 3.][3. 3. 3. 3. 3.][3. 3. 3. 3. 3.]]
mapy=[[0. 0. 0. 0. 0.][0. 0. 0. 0. 0.][0. 0. 0. 0. 0.][0. 0. 0. 0. 0.]]
rst=[[82 82 82 82 82][82 82 82 82 82][82 82 82 82 82][82 82 82 82 82]]

2. 复制:

为了更好地了解重映射函数cv2.remap()的使用方法,下面介绍如何通过该函数实现图像的复制。在映射时,将参数做如下处理:

  • 将map1的值设定为对应位置上的x轴的坐标轴
  • 将map2的值设定为对应位置上的y轴的坐标轴

将map1的值设定为对饮位置上的x轴的坐标轴。

例:

import cv2
import numpy as npimg = cv2.imread('../lena.bmp')
rows, cols = img.shape[:2]
mapx = np.zeros(img.shape[:2], np.float32)
mapy = np.zeros(img.shape[:2], np.float32)
for i in range(rows):for j in range(cols):mapx.itemset((i, j), j)mapy.itemset((i, j), i)
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
cv2.imshow('img', img)
cv2.imshow('rst', rst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012201615601

3. 绕x轴旋转:

映射过程中如果要绕x轴旋转:

  • x坐标轴保持不变
  • y坐标轴的值以x轴为对称轴进行交换。

反映在map1, map2上:

  • map1值保持不变
  • map2的值调整为“总行数-1-当前行号”。

需要注意,OpenCV 中行号的下标是从0开始的,所以在对称关系中存在“当前行号+对称行号=总行数-1”的关系。据此,在绕着x轴翻转时,map2中当前行的行号调整为“总行数-1-当前行号”。

import cv2
import numpy as npimg = cv2.imread('../lena.bmp')
rows, cols = img.shape[:2]
mapx = np.zeros(img.shape[:2], np.float32)
mapy = np.zeros(img.shape[:2], np.float32)
for i in range(rows):for j in range(cols):mapx.itemset((i, j), j)mapy.itemset((i, j), rows-1-i)
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
cv2.imshow('img', img)
cv2.imshow('rst', rst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012202830164

4. 绕y轴旋转:

映射过程中如果要绕y轴旋转:

  • y坐标轴保持不变
  • x坐标轴的值以y轴为对称轴进行交换。

反映在map1, map2上:

  • map2值保持不变
  • map1的值调整为“总列数-1-当前列号”。

需要注意,OpenCV 中行号的下标是从0开始的,所以在对称关系中存在“当前列号+对称列号=总列数-1”的关系。据此,在绕着y轴翻转时,map1中当前列的列号调整为“总列数-1-当前列号”。

import cv2
import numpy as npimg = cv2.imread('../lena.bmp')
rows, cols = img.shape[:2]
mapx = np.zeros(img.shape[:2], np.float32)
mapy = np.zeros(img.shape[:2], np.float32)
for i in range(rows):for j in range(cols):mapx.itemset((i, j), cols - j -1)mapy.itemset((i, j), i)
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
cv2.imshow('img', img)
cv2.imshow('rst', rst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012203527850

5. 绕x、y轴旋转:

映射过程中如果要绕x、y轴旋转:

  • x坐标轴的值以y轴为对称轴进行交换。
  • y坐标轴的值以x轴为对称轴进行交换。

反映在map1, map2上:

  • map1的值调整为“总列数-1-当前列号”
  • map2的值调整为“总行数-1-当前行号”。
import cv2
import numpy as npimg = cv2.imread('../lena.bmp')
rows, cols = img.shape[:2]
mapx = np.zeros(img.shape[:2], np.float32)
mapy = np.zeros(img.shape[:2], np.float32)
for i in range(rows):for j in range(cols):mapx.itemset((i, j), cols - j - 1)mapy.itemset((i, j), rows - i - 1)
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
cv2.imshow('img', img)
cv2.imshow('rst', rst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012203929485

6. x、y轴进行互换:

如果想让图像的x轴、y轴互换,意味着在映射过程中,对于任意一点,都需要将其x轴、y轴坐标互换。反映在mapx和mapy上:

  • mapx的值调整为所在行的行号。
  • mapy的值调整为所在列的列号。

需要注意的是,如果行数和列数不一致,上述运算可能存在值无法映射的情况。默认情况下,无法完成映射的值会被处理为0。

import cv2
import numpy as npimg = cv2.imread('../lena.bmp')
rows, cols = img.shape[:2]
mapx = np.zeros(img.shape[:2], np.float32)
mapy = np.zeros(img.shape[:2], np.float32)
for i in range(rows):for j in range(cols):mapx.itemset((i, j), i)mapy.itemset((i, j), j)
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
cv2.imshow('img', img)
cv2.imshow('rst', rst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012204124880

7. 图像缩放:

前面的映射都是直接完成整数映射,处理起来比较方便。在处理更复杂的问题时,就需要对行、列值进行比较复杂的运算来实现。

例如:使用cv2.remap()缩小图像。

缩小图像后,可以将图像固定在围绕其中心的某个区域。例如,将其x轴、y轴设置为:

  • 在目标图像的x轴(0.25·x轴长度,0.75·x轴长度)区间内生成缩小图像;x轴其余区域的点取样自x轴上任意一点的值。
  • 在目标图像的y轴(0.25·y轴长度,0.75·y轴长度)区间内生成缩小图像;y轴其余区域的点取样自y轴上任意一点的值。

为了处理方便,我们让不在上述区域的点都取(0,0)坐标点的值。

import cv2
import numpy as npimg = cv2.imread('../11111.png')
print(img.shape)
rows, cols = img.shape[:2]
mapx = np.zeros(img.shape[:2], np.float32)
mapy = np.zeros(img.shape[:2], np.float32)
for i in range(rows):for j in range(cols):if 0.25 * cols < j < 0.75 * cols and 0.25 * rows < i < 0.75 * rows:mapx.itemset((i, j), 2 * (j - cols * 0.25) + 0.5)mapy.itemset((i, j), 2 * (i - rows * 0.25) + 0.5)else:mapx.itemset((i, j), 0)mapy.itemset((i, j), 0)
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
cv2.imshow('img', img)
cv2.imshow('rst', rst)
cv2.waitKey()
cv2.destroyAllWindows()

image-20211012210140333

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/400641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装设置Android Studio Win7安装

发一下牢骚和主题无关&#xff1a; 让人等待已久的Google I/O 2013 大会没有给我们带来Android5.0&#xff0c;也没有带来Adnroid4.3等等&#xff0c;但带来了Android Studio&#xff0c;虽说是预览版&#xff0c;又是基于Intellij IDEA&#xff0c; 但是也无不让开辟者们高兴。…

【树莓派学习笔记】一、烧录系统、(无屏幕)配置Wifi和SSH服务

目录系统镜像的准备格式化TF卡烧录镜像配置Wifi开启SSH服务第一次开机平台&#xff1a;树莓派3B 版本&#xff1a; 2021-05-07-raspios-buster-armhf 系统镜像的准备 树莓派资源里有许多资源&#xff0c;包括我们要用到的镜像。 格式化TF卡 将TF卡格式化为FAT32格式。 …

第6章-阈值处理

第六章-阈值处理one. threshold函数&#xff1a;1. 二值化阈值处理&#xff08;cv2.THRESH_BINARY&#xff09;&#xff1a;2. 反二值化阈值处理(cv2.THRESH_BINARY_INV)3. 截断阈值化处理(cv2.THRESH_TRUNC)4. 超阈值零处理(cv2.THRESH_TOZERO_INV)5.低阈值零处理(cv2.THRESH_…

【树莓派学习笔记】二、(无屏幕)SSH远程登录、图形界面及系统配置

目录确定树莓派LAN IP使用PuTTY登陆带图形界面的远程登陆Xming方案VNC Server 方案系统配置换源(可选)备份原文件查询系统版本编辑sources.list文件同步更新源更新软件包重启树莓派固定LAN IP平台&#xff1a;树莓派3B 版本&#xff1a; 2021-05-07-raspios-buster-armhf 确定…

Centos7完全分布式搭建Hadoop2.7.3

(一&#xff09;软件准备 1&#xff0c;hadoop-2.7.3.tar.gz&#xff08;包&#xff09; 2,三台机器装有cetos7的机子 &#xff08;二&#xff09;安装步骤 1&#xff0c;给每台机子配相同的用户 进入root : su root 创建用户s: useradd s 修改用户密码&#xff1a;passwd s 2…

第7章:图像的平滑处理

第7章&#xff1a;图像的平滑处理一、均值滤波&#xff1a;二、方框滤波&#xff1a;三、高斯滤波&#xff1a;四、中值滤波五、双边滤波&#xff1a;六、2D卷积​ 图像的平滑处理是在尽量图像原有信息的情况下&#xff0c;过滤掉图像内部的噪声。由于图像平滑处理的同时通常伴…

【树莓派学习笔记】三、点亮一个LED灯(C语言 - WiringPi、Python - RPi.GPIO/GPIO Zero、bash脚本)

目录C语言WiringPiPythonRPi.GPIOGPIO Zerobash脚本平台&#xff1a;树莓派3B 版本&#xff1a; 2021-05-07-raspios-buster-armhf 若GPIO输出为3.3V 采用压降为1.7V的红色LED灯 设工作电流为15mA&#xff0c;则限流电阻取≥(3.3 - 1.7)/0.015 106.67欧较为安全。 C语言 Wi…

OpenStack 之Nova添加扩展API流程,附带资源的查找功能

例子中涉及到SQLAlchemy 得相关操作&#xff0c;可以参考 上一随笔 Openstack 中规定&#xff0c;扩展openstack得api有两种方式 创建新的WSGI 资源扩展原有得WSGI资源得控制器&#xff08;我得理解是&#xff0c;接受到API请求后&#xff0c;具体得响应逻辑&#xff09;这两种…

第8章:形态学操作

第8章&#xff1a;形态学操作one. 腐蚀操作&#xff1a;two. 膨胀&#xff1a;three. 通用形态学函数&#xff1a;four. 开运算&#xff1a;five. 闭运算&#xff1a;six. 形态学梯度运算&#xff1a;seven. 礼帽运算&#xff1a;eight. 黑帽运算&#xff1a;night. 核函数&…

【树莓派学习笔记】四、OpenCV的安装与卸载

目录安装修改host以连接上Github测试IP修改树莓派的hosts安装各种依赖包安装OpenCV只安装核心模块安装核心模块和opencv_contribC Opencv 测试编写测试源码编译测试卸载平台&#xff1a;树莓派3B 版本&#xff1a; 2021-05-07-raspios-buster-armhf 安装 修改host以连接上Git…

第9章:图像梯度

第9章&#xff1a;图像梯度one. Sobel理论基础1. 计算水平方向偏导数的近似值2. 计算垂直方向偏导数的近似值two. Sobel算子及函数的使用:1. 函数语法&#xff1a;2. 对像素取绝对值&#xff1a;3. 方向&#xff1a;three. Scharr 算子及函数使用:1. 函数语法&#xff1a;2. 实…

【树莓派学习笔记】五、处理、自动重命名并另存为图片

目录编写源码编译测试平台&#xff1a;树莓派3B 版本&#xff1a; 2021-05-07-raspios-buster-armhf 编写源码 所用源码修改自【机器视觉学习笔记】最近邻插值实现图片任意角度旋转&#xff08;C&#xff09; 在合适的地方编写源码 nano main.cpp#include <opencv2/openc…

第10章:Canny图像边缘检测

第10章&#xff1a;Canny图像边缘检测一、Canny边缘检测的基础&#xff1a;1. 应用高斯滤波去除图像噪声&#xff1a;2. 计算梯度3.非极大值抑制4. 应用双阈值确定边缘&#xff1a;二、Canny函数使用&#xff1a;​ Canny边缘检测是一种使用多级边缘检测算法检测边缘的方法。19…

【树莓派学习笔记】六、启用摄像头、实时视频、录像和截图

目录安装摄像头配置使用luvcview平台&#xff1a;树莓派3B 版本&#xff1a; 2021-05-07-raspios-buster-armhf 安装摄像头 配置 sudo raspi-config重启后 cd /dev ls可看到新增了video0设备 使用luvcview 安装 sudo apt-get install luvcview查看摄像设备详细信息 luv…

Android各种屏幕分辨率(VGA、HVGA、QVGA、WQVGA、WVGA、FWVGA) 详解

2019独角兽企业重金招聘Python工程师标准>>> VGA&#xff1a;Video Graphics Array&#xff0c;即&#xff1a;显示绘图矩阵&#xff0c;相当于640480 像素&#xff1b;HVGA&#xff1a;Half-size VGA&#xff0c;即&#xff1a;VGA的一半&#xff0c;分辨率为48032…

敏捷合同-摘自网络

敏捷软件开发实践的文化中存在着一个断层&#xff0c;该断层同样体现在许多敏捷团队中。这个断层就是业务分析人员在敏捷项目中的角色——谁来担任这个角色&#xff1f;它的作用 和价值是什么&#xff1f;它又是如何发生改变的&#xff1f;这种情况的潜台词&#xff08;其实我曾…

第11章:图像金字塔

第11章&#xff1a;图像金字塔一、理论基础&#xff1a;1. 向下采样&#xff1a;2. 向上采样&#xff1a;二、pyrDown函数使用&#xff1a;三、pyrUp函数及使用&#xff1a;四、采样可逆性研究五、拉普拉斯金字塔1. 定义&#xff1a;2. 应用&#xff1a;什么是图像金子塔&#…

【树莓派学习笔记】七、(免费)内网穿透将树莓派作为服务器管理网站

目录nginx安装开机自启动测试查看nginx安装路径查看配置文件路径(测试用)修改index.nginx-debian.html内网穿透免费方案ngrok原版方案下载ngrok连接账户开启内网穿透httpSSH网云穿方案开通隧道下载开启内网穿透开机自启动花生壳方案下载和安装添加内网穿透映射测试平台&#xf…

第12章:图像轮廓

第12章&#xff1a;图像轮廓一、查找并绘制轮廓&#xff1a;1. 查找图像轮廓&#xff1a;2. 绘制图像轮廓&#xff1a;3. 绘制轮廓实例&#xff1a;二、矩特征1. 矩的计算&#xff1a;moments函数2. 计算轮廓面积&#xff1a;contourArea函数3. 计算轮廓长度&#xff1a;arcLen…

【树莓派学习笔记】八、两步安装VS Code (Visual Studio Code)

目录下载安装使用(可选)安装插件C/CCMake ToolsCode Runner平台&#xff1a;树莓派3B 版本&#xff1a; 2021-05-07-raspios-buster-armhf 下载 官网下载 Download Visual Studio Code 发送到树莓派 安装 sudo dpkg -i Desktop/code_1.60.2-1632316275_armhf.deb 使用 在…