OpenCV-Python单应矩阵(Homography Matrix)应用——更换广告牌


前言

Opencv 单应矩阵的应用,学习的时候网上找了很多代码都是C语言写的,小小的研究使用Python实现该功能,顺便写记录一下。


一、前置知识

1. 什么是单应矩阵

关于该方面的解释,可以参考 计算机视觉 公众号中的一篇文章《从零开始一起学习SLAM | 神奇的单应矩阵
这里简单说一下:

  • 单应:
    单应指的概念就是指: 一 一 对应。
  • 单应矩阵(Homography matrix):
    描述同一平面的点不同图像上的映射关系。

二、源图

1. 目标图像(img_dest)

在这里插入图片描述

2. 替换的图片(img_src)

在这里插入图片描述

三、思路与代码

1. 思路:

1.1 获取坐标

由于要将img_src和图像替换到img_dest中的广告牌位置,所以第一步要获取两个位置的坐标信息,
img_src的坐标即为四点个的坐标,而img_dest广告牌需要动态获得(通过自定义鼠标行为动作)。

  # 首先,加载待替换的源图像,并获得该图像的长度等信息,cv.IMREAD_COLOR 表示加载原图
    img_src = cv.imread('img_src.webp', cv.IMREAD_COLOR)
    h, w, c = img_src.shape
    # 将选中的四个点存放在集合中,在收集四个点时,
    # 四个点的点击顺序需要按照 img_src_coordinate 中的点的相对位置的前后顺序保持一致(如下截图顺序)
    img_src_coordinate = np.array([[x, y] for x in (0, w - 1) for y in (0, h - 1)])
    print(img_src_coordinate)

目标图像的坐标获取比较麻烦,需要定义鼠标动作来获取

# 鼠标操作,鼠标选中源图像中需要替换的位置信息
def mouse_action(event, x, y, flags, replace_coordinate_array):
    cv.imshow('collect coordinate', img_dest_copy)
    if event == cv.EVENT_LBUTTONUP:
        # 画圆函数,参数分别表示原图、坐标、半径、颜色、线宽(若为-1表示填充)
        # 这个是为了圈出鼠标点击的点
        cv.circle(img_dest_copy, (x, y), 2, (0, 255, 255), -1)

        # 用鼠标单击事件来选择坐标
        # 将选中的四个点存放在集合中
        print(f'{x}, {y}')
        replace_coordinate_array.append([x, y])
    # 加载目标图像
    img_dest = cv.imread('img_dest.webp', cv.IMREAD_COLOR)
    # 将源数据复制一份,避免后来对该数据的操作会对结果有影响
    img_dest_copy = np.tile(img_dest, 1)
    
    # 源图像中的数据
    # 定义一个数组,用来存放要源图像中要替换的坐标点,该坐标点由鼠标采集得到
    replace_coordinate = []
    cv.namedWindow('collect coordinate')
    cv.setMouseCallback('collect coordinate', mouse_action, replace_coordinate)
    while True:
        # 当采集到四个点后,可以按esc退出鼠标采集行为
        if cv.waitKey(20) == 27:
            # 
            break

    print(replace_coordinate)

在这里插入图片描述

1.2 得到替换后的图像

    replace_coordinate = np.array(replace_coordinate)
    # 根据选中的四个点坐标和代替换的图像信息完成单应矩阵
    matrix, mask = cv.findHomography(img_src_coordinate, replace_coordinate, 0)
    print(f'matrix: {matrix}')
    perspective_img = cv.warpPerspective(img_src, matrix, (img_dest.shape[1],   img_dest.shape[0]))
    cv.imshow('img', perspective_img)

该步骤完成后,结果如下:
在这里插入图片描述

1.3 图像拼接

    # 降噪,去掉最大或最小的像素点
    retval, threshold_img = cv.threshold(perspective_img, 0, 255, cv.THRESH_BINARY)
    # 将降噪后的图像与之前的图像进行拼接
    cv.copyTo(src=threshold_img, mask=np.tile(threshold_img, 1), dst=img_dest)
    cv.copyTo(src=perspective_img, mask=np.tile(perspective_img, 1), dst=img_dest)
    cv.imshow('result', img_dest)

结果图如下:
在这里插入图片描述

四、完整代码

import cv2 as cv
import numpy as np


# 鼠标操作,鼠标选中源图像中需要替换的位置信息
def mouse_action(event, x, y, flags, replace_coordinate_array):
    cv.imshow('collect coordinate', img_dest_copy)
    if event == cv.EVENT_LBUTTONUP:
        # 画圆函数,参数分别表示原图、坐标、半径、颜色、线宽(若为-1表示填充)
        # 这个是为了圈出鼠标点击的点
        cv.circle(img_dest_copy, (x, y), 2, (0, 255, 255), -1)

        # 用鼠标单击事件来选择坐标
        # 将选中的四个点存放在集合中,在收集四个点时,四个点的点击顺序需要按照 img_src_coordinate 中的点的相对位置的前后顺序保持一致
        print(f'{x}, {y}')
        replace_coordinate_array.append([x, y])


if __name__ == '__main__':
    # 首先,加载待替换的源图像,并获得该图像的长度等信息,cv.IMREAD_COLOR 表示加载原图
    img_src = cv.imread('img_src.jpg', cv.IMREAD_COLOR)
    h, w, c = img_src.shape
    # 获得图像的四个边缘点的坐标
    img_src_coordinate = np.array([[x, y] for x in (0, w - 1) for y in (0, h - 1)])
    print(img_src_coordinate)
    # cv.imshow('replace', replace)

    print("===========================")

    # 加载目标图像
    img_dest = cv.imread('img_dest.webp', cv.IMREAD_COLOR)
    # 将源数据复制一份,避免后来对该数据的操作会对结果有影响
    img_dest_copy = np.tile(img_dest, 1)

    # 源图像中的数据
    # 定义一个数组,用来存放要源图像中要替换的坐标点,该坐标点由鼠标采集得到
    replace_coordinate = []
    cv.namedWindow('collect coordinate')
    cv.setMouseCallback('collect coordinate', mouse_action, replace_coordinate)
    while True:
        # 当采集到四个点后,可以按esc退出鼠标采集行为
        if cv.waitKey(20) == 27:
            break

    print(replace_coordinate)

    replace_coordinate = np.array(replace_coordinate)
    # 根据选中的四个点坐标和代替换的图像信息完成单应矩阵
    matrix, mask = cv.findHomography(img_src_coordinate, replace_coordinate, 0)
    print(f'matrix: {matrix}')
    perspective_img = cv.warpPerspective(img_src, matrix, (img_dest.shape[1], img_dest.shape[0]))
    cv.imshow('img', perspective_img)

    # cv.imshow('threshold', threshold_img)
    # 降噪,去掉最大或最小的像素点
    retval, threshold_img = cv.threshold(perspective_img, 0, 255, cv.THRESH_BINARY)
    # 将降噪后的图像与之前的图像进行拼接
    cv.copyTo(src=threshold_img, mask=np.tile(threshold_img, 1), dst=img_dest)
    cv.copyTo(src=perspective_img, mask=np.tile(perspective_img, 1), dst=img_dest)
    cv.imshow('result', img_dest)
    cv.waitKey()
    cv.destroyAllWindows()
  • 10
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值