当前位置:   article > 正文

OpenCV 截取指定区域、黑化背景、透视转换_cv2.warpaffine

cv2.warpaffine

 

一、cv2.getPerspectiveTransform

cv2.getPerspectiveTransform(src, dst) → retval

src:源图像中待测矩形的四点坐标

sdt:目标图像中矩形的四点坐标

 

一、cv2.warpAffine

放射变换函数,可实现旋转,平移,缩放;变换后的平行线依旧平

cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None) --> dst

src:输入图像     dst:输出图像

M:2×3的变换矩阵

dsize:变换后输出图像尺寸

flag:插值方法

borderMode:边界像素外扩方式

borderValue:边界像素插值,默认用0填充

变换矩阵M可通过cv2.getAffineTransfrom(points1, points2)函数获得

变换矩阵的获取需要至少三组变换前后对应的点坐标,设取原图上的三个点组成矩阵points1,变换后的三个点组成的矩阵points2

  1. points1 = np.float32([ [30,30], [100,40], [40,100] ])
  2. points2 = np.float32([ [60,60], [40,100], [80,20] ])
  3. M  =  cv2.getAffineTransform(points1, points2)
  4.    =  array([[-0.333333330.33333333, 60.   ],
  5.              [ 0.66666667, -0.66666667, 60.   ]])
  6. Affine_img = cv2.warpAffine(img, M, (img.shape[1], img.shape[0])) 

 

二、cv2.warpPerspective

透视变换函数,可保持直线不变形,但是平行线可能不再平行

cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None) --> dst

其相关参数和cv2.warpAffine函数的类似,不再做介绍

它的变换矩阵可以通过cv2.getPerspectiveTransform()函数获得,其原理和cv2.getAffineTransfrom()相同,只是投射变换至少需要四组变换前后对应的点坐标,设取原图上的四个点组成矩阵points1,变换后的四个点组成的矩阵points2

如:

  1. points1 = np.float32([ [30,30], [10,40], [40,10], [5,15] ])
  2. points2 = np.float32([ [0,0], [400,0], [0,400], [400,400] ])
  3. M = cv2.getPerspectiveTransform(points1, points2)
  4.   = array([[-9.08777969e+00, -4.54388985e+004.08950086e+02],
  5.            [-5.37005164e+00, -1.07401033e+014.83304647e+02],
  6.            [-1.15318417e-02, -1.35972461e-021.00000000e+00]])
  7. Perspective_img = cv2.warpPerspective(img, M, (img.shape[1], img.shape[0])) 

 

三、快速应用工具

  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: yuki_ho
  4. """
  5. import cv2
  6. import numpy as np
  7. # -----------------------鼠标操作相关------------------------------------------
  8. lsPointsChoose = []
  9. tpPointsChoose = []
  10. pointsCount = 0
  11. count = 0
  12. pointsMax = 6
  13. def on_mouse(event, x, y, flags, param):
  14. global img, point1, point2, count, pointsMax
  15. global lsPointsChoose, tpPointsChoose # 存入选择的点
  16. global pointsCount # 对鼠标按下的点计数
  17. global img2, ROI_bymouse_flag
  18. img2 = img.copy() # 此行代码保证每次都重新再原图画 避免画多了
  19. # -----------------------------------------------------------
  20. # count=count+1
  21. # print("callback_count",count)
  22. # --------------------------------------------------------------
  23. if event == cv2.EVENT_LBUTTONDOWN: # 左键点击
  24. pointsCount = pointsCount + 1
  25. # 为了保存绘制的区域,画的点稍晚清零
  26. # if (pointsCount == pointsMax + 1):
  27. # pointsCount = 0
  28. # tpPointsChoose = []
  29. print('pointsCount:', pointsCount)
  30. point1 = (x, y)
  31. print(x, y)
  32. # 画出点击的点
  33. cv2.circle(img2, point1, 10, (0, 255, 0), 2)
  34. # 将选取的点保存到list列表里
  35. lsPointsChoose.append([x, y]) # 用于转化为darry 提取多边形ROI
  36. tpPointsChoose.append((x, y)) # 用于画点
  37. # ----------------------------------------------------------------------
  38. # 将鼠标选的点用直线连起来
  39. print(len(tpPointsChoose))
  40. for i in range(len(tpPointsChoose) - 1):
  41. print('i', i)
  42. cv2.line(img2, tpPointsChoose[i], tpPointsChoose[i + 1], (0, 0, 255), 2)
  43. # ----------------------------------------------------------------------
  44. # ----------点击到pointMax时可以提取去绘图----------------
  45. cv2.imshow('src', img2)
  46. # -------------------------右键按下清除轨迹-----------------------------
  47. if event == cv2.EVENT_RBUTTONDOWN: # 右键点击
  48. print("right-mouse")
  49. pointsCount = 0
  50. tpPointsChoose = []
  51. lsPointsChoose = []
  52. print(len(tpPointsChoose))
  53. for i in range(len(tpPointsChoose) - 1):
  54. print('i', i)
  55. cv2.line(img2, tpPointsChoose[i], tpPointsChoose[i + 1], (0, 0, 255), 2)
  56. cv2.imshow('src', img2)
  57. # -------------------------双击 结束选取-----------------------------
  58. if event == cv2.EVENT_LBUTTONDBLCLK:
  59. # -----------绘制感兴趣区域-----------
  60. ROI_byMouse()
  61. ROI_bymouse_flag = 1
  62. lsPointsChoose = []
  63. def ROI_byMouse():
  64. global src, ROI, ROI_flag, mask2
  65. mask = np.zeros(img.shape, np.uint8) # (450, 800, 3)
  66. pts = np.array([lsPointsChoose], np.int32) # pts是多边形的顶点列表(顶点集)
  67. pts = pts.reshape((-1, 1, 2))
  68. print(pts) #所勾选的坐标
  69. # 这里 reshape 的第一个参数为-1, 表明这一维的长度是根据后面的维度的计算出来的。
  70. # OpenCV中需要先将多边形的顶点坐标变成顶点数×1×2维的矩阵,再来绘制
  71. # --------------画多边形---------------------
  72. mask = cv2.polylines(mask, [pts], True, (255, 255, 255))
  73. ##-------------填充多边形---------------------
  74. mask2 = cv2.fillPoly(mask, [pts], (255, 255, 255))
  75. cv2.imshow('mask', mask2)
  76. cv2.imwrite('mask.jpg', mask2)
  77. contours, hierarchy = cv2.findContours(cv2.cvtColor(mask2, cv2.COLOR_BGR2GRAY), cv2.RETR_TREE,
  78. cv2.CHAIN_APPROX_NONE)
  79. ROIarea = cv2.contourArea(contours[0])
  80. print("ROIarea:", ROIarea)
  81. ROI = cv2.bitwise_and(mask2, img)
  82. # cv2.imwrite('ROI.jpg', ROI)
  83. cv2.imshow('ROI', ROI)
  84. #暂时 下面 只针对 3 或 4 个点
  85. crop_change = transform4pts(ROI, pts, ROI.shape) if len(pts) > 3 else transform3pts(ROI, pts, ROI.shape)
  86. cv2.imshow('ROI_trans', crop_change)
  87. def ROI_test(img_path):
  88. img = cv2.imread(img_path)
  89. # 自己定义坐标点
  90. pts = np.array(
  91. [[[359,96]],
  92. [[739,97]],
  93. [[1164,661]],
  94. [[83,663]]
  95. ])
  96. crop_img = cropFill(img, pts)
  97. cv2.imshow('crop_img', crop_img)
  98. crop_change = transform4pts(crop_img, pts, crop_img.shape) if len(pts) > 3 else transform3pts(crop_img, pts, crop_img.shape)
  99. cv2.imshow('trans_img', crop_change)
  100. cv2.waitKey(0)
  101. cv2.destroyAllWindows()
  102. # 截取并填充
  103. def cropFill(src_img, pts):
  104. mask = np.zeros(src_img.shape, np.uint8) # 空的画板
  105. # --------------画多边形---------------------
  106. mask = cv2.polylines(mask, [pts], True, (255, 255, 255))
  107. ##-------------填充多边形---------------------
  108. mask2 = cv2.fillPoly(mask, [pts], (255, 255, 255))
  109. contours, hierarchy = cv2.findContours(cv2.cvtColor(mask2, cv2.COLOR_BGR2GRAY), cv2.RETR_TREE,
  110. cv2.CHAIN_APPROX_NONE)
  111. ROI = cv2.bitwise_and(mask2, src_img)
  112. return ROI
  113. # 3点 透视变换
  114. def transform3pts(src_img, pts, out_size):
  115. # print(out_size) #(730, 1176, 3) # H W
  116. pts = pts.astype(np.float32)
  117. # 自定义吧
  118. dpts = np.array([
  119. [0, 0],
  120. [out_size[1], 0],
  121. [out_size[1], out_size[0]],
  122. ], dtype=np.float32)
  123. M = cv2.getAffineTransform(pts, dpts)
  124. img_result = cv2.warpAffine(src_img, M , (out_size[1],out_size[0])) #透视变换
  125. return img_result
  126. # 4点 透视变换
  127. def transform4pts(src_img, pts, out_size):
  128. # print(out_size) #(730, 1176, 3) # H W
  129. pts = pts.astype(np.float32)
  130. # 自定义吧
  131. dpts = np.array([
  132. [0, 0],
  133. [out_size[1], 0],
  134. [out_size[1], out_size[0]],
  135. [0, out_size[0]],
  136. ], dtype=np.float32)
  137. M = cv2.getPerspectiveTransform(pts, dpts) #变换矩阵 ABC变换到A'B'C'
  138. img_result = cv2.warpPerspective(src_img, M , (out_size[1],out_size[0])) #透视变换
  139. return img_result
  140. if __name__ == '__main__':
  141. img = cv2.imread('6.jpg') #1176,730
  142. # ---------------------------------------------------------
  143. # --图像预处理,设置其大小
  144. # height, width = img.shape[:2]
  145. # size = (int(width * 0.3), int(height * 0.3))
  146. # img = cv2.resize(img, size, interpolation=cv2.INTER_AREA)
  147. # ------------------------------------------------------------
  148. ROI = img.copy()
  149. cv2.namedWindow('src')
  150. cv2.setMouseCallback('src', on_mouse)
  151. cv2.imshow('src', img)
  152. cv2.waitKey(0)
  153. cv2.destroyAllWindows()
  154. # ROI_test('6.jpg')

 

四、效果

 

五、调整工具

  1. import argparse
  2. import os
  3. import tkinter as tk
  4. from tkinter import filedialog
  5. import cv2
  6. import numpy as np
  7. parser = argparse.ArgumentParser()
  8. # parser.add_argument('source', type=str)
  9. parser.add_argument('--height', type=int, default=1080)
  10. parser.add_argument('--width', type=int, default=1920)
  11. args = parser.parse_args()
  12. print(args)
  13. root = tk.Tk()
  14. root.title('不规则四边形裁剪')
  15. pos = {
  16. 'TL': (tk.IntVar(root, value=0), tk.IntVar(root, value=0)),
  17. 'TR': (tk.IntVar(root, value=0), tk.IntVar(root, value=1600)),
  18. 'BL': (tk.IntVar(root, value=900), tk.IntVar(root, value=0)),
  19. 'BR': (tk.IntVar(root, value=900), tk.IntVar(root, value=1600)),
  20. }
  21. output = (tk.IntVar(root, value=args.height), tk.IntVar(root, value=args.width))
  22. img = None
  23. fileName = None
  24. fileExtension = None
  25. inputRatio = None
  26. def mark_line(img):
  27. if img is None:
  28. return
  29. img_temp = img.copy()
  30. cv2.line(
  31. img_temp,
  32. (pos['TL'][1].get(), pos['TL'][0].get()),
  33. (pos['TR'][1].get(), pos['TR'][0].get()),
  34. (0, 0, 255), 2, cv2.LINE_AA
  35. )
  36. cv2.line(
  37. img_temp,
  38. (pos['BL'][1].get(), pos['BL'][0].get()),
  39. (pos['BR'][1].get(), pos['BR'][0].get()),
  40. (0, 0, 255), 2, cv2.LINE_AA
  41. )
  42. cv2.line(
  43. img_temp,
  44. (pos['TL'][1].get(), pos['TL'][0].get()),
  45. (pos['BL'][1].get(), pos['BL'][0].get()),
  46. (0, 0, 255), 2, cv2.LINE_AA
  47. )
  48. cv2.line(
  49. img_temp,
  50. (pos['TR'][1].get(), pos['TR'][0].get()),
  51. (pos['BR'][1].get(), pos['BR'][0].get()),
  52. (0, 0, 255), 2, cv2.LINE_AA
  53. )
  54. return img_temp
  55. def crop_image():
  56. sourcePoints = np.array([
  57. (pos['TL'][1].get(), pos['TL'][0].get()),
  58. (pos['TR'][1].get(), pos['TR'][0].get()),
  59. (pos['BR'][1].get(), pos['BR'][0].get()),
  60. (pos['BL'][1].get(), pos['BL'][0].get()),
  61. ], dtype=np.float32)
  62. print(sourcePoints)
  63. dstPoints = np.array([
  64. [0, 0],
  65. [output[1].get(), 0],
  66. [output[1].get(), output[0].get()],
  67. [0, output[0].get()],
  68. ], dtype=np.float32)
  69. print('output',output)
  70. print(dstPoints)
  71. M = cv2.getPerspectiveTransform(sourcePoints, dstPoints)
  72. print('M:',M)
  73. print('zuobiao:',(output[1].get(), output[0].get()) )
  74. img_result = cv2.warpPerspective(img, M, (output[1].get(), output[0].get()))
  75. return img_result
  76. def show_images():
  77. if img is None:
  78. return
  79. resize_img_to_show('input', mark_line(img))
  80. resize_img_to_show('output', crop_image(), small=True)
  81. cv2.setMouseCallback('input', mouseClicked)
  82. def resize_img_to_show(name, img_temp, small=False):
  83. if img_temp is None:
  84. return
  85. height = img_temp.shape[0]
  86. width = img_temp.shape[1]
  87. if small and height <= 700:
  88. ratio = 1
  89. else:
  90. ratio = 700 / height
  91. height = int(height * ratio)
  92. width = int(width * ratio)
  93. cv2.imshow(name, cv2.resize(img_temp, (width, height)))
  94. if name == 'input':
  95. global inputRatio
  96. inputRatio = ratio
  97. def openFile():
  98. global img, fileName, fileExtension
  99. file_path = filedialog.askopenfilename()
  100. print(file_path)
  101. if file_path is not None:
  102. fileName = os.path.basename(file_path)
  103. fileExtension = os.path.splitext(file_path)[1]
  104. img = cv2.imread(file_path, cv2.IMREAD_UNCHANGED)
  105. pos['TL'][0].set(0)
  106. pos['TL'][1].set(0)
  107. pos['TR'][0].set(0)
  108. pos['TR'][1].set(img.shape[1] - 1)
  109. pos['BL'][0].set(img.shape[0] - 1)
  110. pos['BL'][1].set(0)
  111. pos['BR'][0].set(img.shape[0] - 1)
  112. pos['BR'][1].set(img.shape[1] - 1)
  113. show_images()
  114. def saveFile():
  115. global img
  116. if img is None:
  117. return
  118. out_path = filedialog.asksaveasfilename(
  119. filetypes=[
  120. ('PNG File', '*.png'),
  121. ('JPEG File', '*.jpg'),
  122. ('All files', '*'),
  123. ],
  124. initialfile=fileName,
  125. defaultextension=fileExtension,
  126. )
  127. print(out_path)
  128. if out_path:
  129. img_result = crop_image()
  130. cv2.imwrite(out_path, img_result)
  131. def changePos(corner, xy, diff):
  132. def wrapper():
  133. pos[corner][xy].set(pos[corner][xy].get() + diff)
  134. show_images()
  135. return wrapper
  136. def changeOutput(xy, diff):
  137. def wrapper():
  138. output[xy].set(output[xy].get() + diff)
  139. show_images()
  140. return wrapper
  141. def mouseClicked(event, p1, p0, flags, param):
  142. if event == cv2.EVENT_LBUTTONDOWN or (event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON)):
  143. p0 = int(p0 / inputRatio)
  144. p1 = int(p1 / inputRatio)
  145. print(p0, p1)
  146. minDis = 1e9
  147. minCorner = None
  148. for corner in pos:
  149. tempDis = (pos[corner][0].get() - p0)**2 + (pos[corner][1].get() - p1)**2
  150. if tempDis < minDis:
  151. minDis = tempDis
  152. minCorner = corner
  153. pos[minCorner][0].set(p0)
  154. pos[minCorner][1].set(p1)
  155. show_images()
  156. ROW = 0
  157. tk.Button(root, text='Open File', command=openFile).grid(row=ROW, column=0, columnspan=2, sticky='W')
  158. for xy in [0, 1]:
  159. ROW += 1
  160. COL = -1
  161. for corner in ['TL', 'TR']:
  162. COL += 1
  163. tk.Label(root, text='{}{}'.format(corner, xy)).grid(row=ROW, column=COL)
  164. COL += 1
  165. tk.Entry(root, textvariable=pos[corner][xy]).grid(row=ROW, column=COL)
  166. for diff in [1, 10, 100]:
  167. COL += 1
  168. tk.Button(root, text=' {:+d} '.format(-diff), command=changePos(corner, xy, -diff)).grid(row=ROW, column=COL)
  169. COL += 1
  170. tk.Button(root, text=' {:+d} '.format(+diff), command=changePos(corner, xy, +diff)).grid(row=ROW, column=COL)
  171. ROW += 1
  172. tk.Label(root, text='').grid(row=ROW, column=0)
  173. for xy in [0, 1]:
  174. ROW += 1
  175. COL = -1
  176. for corner in ['BL', 'BR']:
  177. COL += 1
  178. tk.Label(root, text='{}{}'.format(corner, xy)).grid(row=ROW, column=COL)
  179. COL += 1
  180. tk.Entry(root, textvariable=pos[corner][xy]).grid(row=ROW, column=COL)
  181. for diff in [1, 10, 100]:
  182. COL += 1
  183. tk.Button(root, text=' {:+d} '.format(-diff), command=changePos(corner, xy, -diff)).grid(row=ROW, column=COL)
  184. COL += 1
  185. tk.Button(root, text=' {:+d} '.format(+diff), command=changePos(corner, xy, +diff)).grid(row=ROW, column=COL)
  186. ROW += 1
  187. tk.Label(root, text='').grid(row=ROW, column=0)
  188. ROW += 1
  189. COL = -1
  190. for xy in [0, 1]:
  191. COL += 1
  192. tk.Label(root, text='Out{}'.format(xy)).grid(row=ROW, column=COL)
  193. COL += 1
  194. tk.Entry(root, textvariable=output[xy]).grid(row=ROW, column=COL)
  195. for diff in [1, 10, 100]:
  196. COL += 1
  197. tk.Button(root, text=' {:+d} '.format(-diff), command=changeOutput(xy, -diff)).grid(row=ROW, column=COL)
  198. COL += 1
  199. tk.Button(root, text=' {:+d} '.format(+diff), command=changeOutput(xy, +diff)).grid(row=ROW, column=COL)
  200. ROW += 1
  201. tk.Button(root, text='Save File', command=saveFile).grid(row=ROW, column=0, columnspan=2, sticky='W')
  202. root.mainloop()

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/678666
推荐阅读
相关标签
  

闽ICP备14008679号