OpenCV-Python实战(5) —— OpenCV 使用cv.setMouseCallback实现截图功能

583 阅读5分钟

1. 功能分析

  1. 需要实现截图功能,可以采用【OpenCV-Python学习(5)—— OpenCV 图像像素的读写操作】,获取截取区域具体的像素值;
  2. 需要实现截图,就需要获取对应的截取区域,采用【OpenCV-Python学习(15)—— OpenCV 鼠标操作和响应(cv.setMouseCallback)】获取需要截图的区域;
  3. 截图成功后如果需要保存,采用【OpenCV-Python学习(2)—— OpenCV 图像的读取、显示和保存(cv.imread、cv.imshow、cv.imwrite)】实现截取图片的保存。

2. 代码逻辑分析

  1. 获取需要截取图片的地址,使用sys模块;
  2. 使用 cv.imread 读取传入的图片;
  3. 使用 np.ones_like【OpenCV-Python学习(5)—— OpenCV 图像像素的读写操作】复制一份读取的图片;
  4. 创建一个窗口,监听这个窗口的鼠标事件;
  5. 左键点击起点: 当前次鼠标左键开始坐标;
  6. 开始后允许对移动中坐标进行记录;
  7. 截取区域边框矩形的随机颜色生成;
  8. 移动终点: 将上次绘制的结果给当前图片,为了将当前次移动过程中产生的绘制清除;
  9. 当前次移动结束的坐标;
  10. 绘制移动中的当前矩形;
  11. 最后终点: 当前次坐标点绘制结束坐标点,结束鼠标移动监听;
  12. 绘制当前次鼠标左键按下到放开起点和终点组成的矩形;
  13. 截取矩形区域并显示截取图片;
  14. 如果需要保存截图图片;
  15. 判断截取图片保存文件夹是否存在,不存在就创建;
  16. 生成随机的截取图片名称并保存,最后销毁显示截取图片窗口;
  17. 坐标点还原;
  18. 每10毫秒显示一次图片;
  19. 监听每10毫秒是否按退出键;
  20. 销毁所有窗口。

3. 截图函数

# 截图
def screenshot(img_url):
  global oldImg
  # 读取传入的图片地址
  img = cv.imread(img_url)
  # 复制一个一样大小原图片
  oldImg = np.ones_like(img)
  oldImg[:] = img[:]
  # 创建一个窗口
  cv.namedWindow('screenshot_img')
  # 监听这个窗口的鼠标事件
  cv.setMouseCallback('screenshot_img', draw_rectangle, img)
  # 每10毫秒显示一次图片
  while True:
    cv.imshow("screenshot_img", img)
    # 监听每10毫秒是否按退出键
    if cv.waitKey(10) & 0xFF == 27:
      break
  # 销毁所有窗口
  cv.destroyAllWindows()

4. 鼠标事件回调

# 鼠标回调函数,绘制矩形
def draw_rectangle(event,x,y,flags,img):
  global flagMove,startX,startY,endX,endY,oldImg,b,g,r
  # 点击起点
  if event == cv.EVENT_LBUTTONDOWN:
    # 当前次鼠标左键开始坐标
    startX,startY = x,y
    # 开始后允许对移动中坐标进行记录
    flagMove = True
    # 产生随机颜色
    b,g,r = np.random.randint(0,256,size=3)
    b,g,r = int(b),int(g),int(r)
  # 移动终点
  if event == cv.EVENT_MOUSEMOVE and flagMove:
    # 将上次绘制的结果给当前图片,为了将当前次移动过程中产生的绘制清除
    img[:] = oldImg[:]
    # 当前次移动结束的坐标
    endX,endY = x,y
    # 绘制移动中的当前矩形
    cv.rectangle(img, (startX,startY), (endX,endY), (b,g,r), lineType=cv.LINE_AA)
  # 最后终点
  if event == cv.EVENT_LBUTTONUP:
    # 当前次坐标点绘制结束坐标点,结束鼠标移动监听
    endX,endY = x,y
    flagMove = False
    # 绘制当前次鼠标左键按下到放开起点和终点组成的矩形
    cv.rectangle(img, (startX,startY), (endX,endY), (b,g,r), lineType=cv.LINE_AA)
    # 截取矩形区域并显示截取图片
    roi = oldImg[startY:endY, startX:endX]
    cv.imshow('roi', roi)
    # 如果需要保存截图图片
    if cv.waitKey(0) == ord('s'):
      # 判断截取图片保存文件夹是否存在,不存在就创建
      if not os.path.exists(f'./out_images/'):
        os.makedirs(f'./out_images/')
      # 生成随机的截取图片名称并保存,最后销毁显示截取图片窗口
      path_url = f"./out_images/random{np.random.randint(100,1000)}.jpg"
      cv.imwrite(path_url, roi)
      cv.destroyWindow("roi")
    # 坐标点还原
    startX,startY = -1,-1
    endX,endY = -1,-1

5. 完整实现截图功能代码

import sys
import os
import cv2 as cv
import numpy as np

flagMove = False
oldImg = None
startX,startY = -1,-1
endX,endY = -1,-1
b,g,r = 0,0,0

# 截图
def screenshot(img_url):
  global oldImg
  # 读取传入的图片地址
  img = cv.imread(img_url)
  # 复制一个一样大小原图片
  oldImg = np.ones_like(img)
  oldImg[:] = img[:]
  # 创建一个窗口
  cv.namedWindow('screenshot_img')
  # 监听这个窗口的鼠标事件
  cv.setMouseCallback('screenshot_img', draw_rectangle, img)
  # 每10毫秒显示一次图片
  while True:
    cv.imshow("screenshot_img", img)
    # 监听每10毫秒是否按退出键
    if cv.waitKey(10) & 0xFF == 27:
      break
  # 销毁所有窗口
  cv.destroyAllWindows()

# 鼠标回调函数,绘制矩形
def draw_rectangle(event,x,y,flags,img):
  global flagMove,startX,startY,endX,endY,oldImg,b,g,r
  # 点击起点
  if event == cv.EVENT_LBUTTONDOWN:
    # 当前次鼠标左键开始坐标
    startX,startY = x,y
    # 开始后允许对移动中坐标进行记录
    flagMove = True
    # 产生随机颜色
    b,g,r = np.random.randint(0,256,size=3)
    b,g,r = int(b),int(g),int(r)
  # 移动终点
  if event == cv.EVENT_MOUSEMOVE and flagMove:
    # 将上次绘制的结果给当前图片,为了将当前次移动过程中产生的绘制清除
    img[:] = oldImg[:]
    # 当前次移动结束的坐标
    endX,endY = x,y
    # 绘制移动中的当前矩形
    cv.rectangle(img, (startX,startY), (endX,endY), (b,g,r), lineType=cv.LINE_AA)
  # 最后终点
  if event == cv.EVENT_LBUTTONUP:
    # 当前次坐标点绘制结束坐标点,结束鼠标移动监听
    endX,endY = x,y
    flagMove = False
    # 绘制当前次鼠标左键按下到放开起点和终点组成的矩形
    cv.rectangle(img, (startX,startY), (endX,endY), (b,g,r), lineType=cv.LINE_AA)
    # 截取矩形区域并显示截取图片
    roi = oldImg[startY:endY, startX:endX]
    cv.imshow('roi', roi)
    # 如果需要保存截图图片
    if cv.waitKey(0) == ord('s'):
      # 判断截取图片保存文件夹是否存在,不存在就创建
      if not os.path.exists(f'./out_images/'):
        os.makedirs(f'./out_images/')
      # 生成随机的截取图片名称并保存,最后销毁显示截取图片窗口
      path_url = f"./out_images/random{np.random.randint(100,1000)}.jpg"
      cv.imwrite(path_url, roi)
      cv.destroyWindow("roi")
    # 坐标点还原
    startX,startY = -1,-1
    endX,endY = -1,-1

if __name__ == "__main__":
  img_url = sys.argv[1]
  screenshot(img_url)

6. 运行结果

输入图片说明

7. 截图实例

8. 总结

  1. 由于没有对截图时超出图片坐标的处理,因此测试时,请常规操作,在图片内截图;
  2. 执行截图代码时带上截图图片的路径,实例:
python app.py ./images/lena.jpg