OpenCV-Python 颜色识别(红色)并拟合矫正目标区域

news/2024/7/22 4:18:13 标签: python, opencv, 计算机视觉, CV

OpenCV版本:4.6.0.66

算法实现思路:

  1. 颜色识别(红色)
  2. 形态学去噪
  3. 轮廓检测
  4. 多边形拟合
  5. 透视矫正

代码实现:

python">import cv2
import numpy as np


# 可视化
def img_show(name, img):
    cv2.namedWindow(name, 0)
    cv2.resizeWindow(name, 1000, 500)
    cv2.imshow(name, img)
    cv2.waitKey(0)


def color_warped(path):
    img = cv2.imread(path)
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

    # 颜色识别(红色),过滤红色区域
    lower_red1 = np.array([0, 43, 46])  # 红色阈值下界
    higher_red1 = np.array([10, 255, 255])  # 红色阈值上界
    mask_red1 = cv2.inRange(hsv, lower_red1, higher_red1)
    lower_red2 = np.array([156, 43, 46])  # 红色阈值下界
    higher_red2 = np.array([180, 255, 255])  # 红色阈值上界
    mask_red2 = cv2.inRange(hsv, lower_red2, higher_red2)
    mask_red = cv2.add(mask_red1, mask_red2)  # 拼接过滤后的mask
    img_show('mask_red', mask_red)

    # 形态学去噪,cv2.MORPH_OPEN先腐蚀再膨胀,cv2.MORPH_CLOSE先膨胀再腐蚀
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel, iterations=1)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
    mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel, iterations=3)
    img_show('mask_red', mask_red)

    # 轮廓检测,找出线条的轮廓
    draw_cnt = img.copy()
    cnts = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
    cv2.drawContours(draw_cnt, cnts, -1, (0, 255, 0), 2)
    img_show('draw_cnt', draw_cnt)

    # 四边形拟合,找到相应的的顶点
    draw_approx = img.copy()
    point1, point2 = list(), list()
    for cnt in cnts:
        for epsilon in range(50):
            approx = cv2.approxPolyDP(cnt, epsilon, True)
            if len(approx) == 4:
                break
        cv2.polylines(draw_approx, [approx], True, (0, 0, 255), 2)  # 绘制边
        for i in approx:
            cv2.circle(draw_approx, i[0], 6, (0, 0, 0), -1)  # 绘制顶点

        approx = [i[0] for i in approx.tolist()]
        approx = sorted(approx, key=lambda k: k[1], reverse=False)  # 按y坐标排序,升序

        point1.extend(approx[:2])  # 存放上顶点坐标
        point2.extend(approx[2:])  # 存放下顶点坐标
    point1.sort(key=lambda k: k[0], reverse=False)  # 按x坐标排序,升序
    point2.sort(key=lambda k: k[0], reverse=False)
    img_show('draw_approx', draw_approx)

    # 透视矫正目标区域
    w, h = 900, 300
    rect = [point1[0], point1[-1], point2[-1], point2[0]]  # 顺序为第一个四边形的左上,第四个四边形的右上,第四个四边形的右下,第一个四边形的左下
    pts1 = np.array(rect, dtype="float32")
    pts2 = np.array([rect[0], [rect[0][0] + w, rect[0][1]],
                    [rect[0][0] + w, rect[0][1] + h], [rect[0][0], rect[0][1] + h]], dtype="float32")
    M = cv2.getPerspectiveTransform(pts1, pts2)  # 变换矩阵
    img_warped = cv2.warpPerspective(img, M, (1500, 500))  # 透视变换
    img_show('img_warped1', img_warped)

    img_warped = img_warped[rect[0][1]: rect[0][1] + h, rect[0][0]: rect[0][0] + w]  # 抠出变换后的区域
    img_show('img_warped2', img_warped)


if __name__ == '__main__':
    path = 'data/picture/18.jpg'
    color_warped(path)

原图:

颜色识别(红色)

python">img = cv2.imread(path)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

        颜色识别使用的通道是HSV通道。使用HSV通道识别颜色会比使用BGR通道更容易区分。H表示Hue(色调、色相,取值:0-180),S表示Saturation(饱和度、色彩纯净度,取值:0-255),V表示Value(亮度,取值:0-255),详情可见:HSV颜色空间。各颜色的取值范围如下:

         可见红色的取值范围有两个,大部分情况下这两个范围刚好是互补的,可根据实际情况调整。

python"># 颜色识别(红色),过滤红色区域
lower_red1 = np.array([0, 43, 46])  # 红色阈值下界
higher_red1 = np.array([10, 255, 255])  # 红色阈值上界
mask_red1 = cv2.inRange(hsv, lower_red1, higher_red1)
lower_red2 = np.array([156, 43, 46])  # 红色阈值下界
higher_red2 = np.array([180, 255, 255])  # 红色阈值上界
mask_red2 = cv2.inRange(hsv, lower_red2, higher_red2)
mask_red = cv2.add(mask_red1, mask_red2)  # 拼接过滤后的mask

         过滤出来的二值图如下:

形态学去噪

python"># 形态学去噪,cv2.MORPH_OPEN先腐蚀再膨胀,cv2.MORPH_CLOSE先膨胀再腐蚀
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel, iterations=1)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel, iterations=3)

         颜色识别有时候会有部分噪声被一起识别,这时候可以用形态学进行去噪。在使用cv2.morphologyEx进行形态学去噪之前,需要由cv2.getStructuringElement先定义卷积核形状大小

        cv2.getStructuringElement(shape, ksize)常用参数如下:

  • shape:核的形状,cv2.MORPH_RECT矩形),cv2.MORPH_CROSS十字形),cv2.MORPH_ELLIPSE椭圆形
  • ksize:核的大小,格式为(width, height)

        cv2.morphologyEx(src, op, kernel, iterations) 常用参数如下:

  • src:输入图像
  • op:形态学操作的类型,cv2.MORPH_ERODE腐蚀),cv2.MORPH_DILATE膨胀),cv2.MORPH_OPEN开运算),cv2.MORPH_CLOSE闭运算),cv2.MORPH_GRADIENT梯度运算),cv2.MORPH_TOPHAT礼帽运算),cv2.MORPH_BLACKHAT黑帽运算),详情可见:形态学操作
  • kernel:进行形态学操作的卷积核
  • iterations:形态学操作迭代的次数,默认值为1

        效果如下:

轮廓检测

python"># 轮廓检测,找出线条的轮廓
draw_cnt = img.copy()
cnts = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
cv2.drawContours(draw_cnt, cnts, -1, (0, 255, 0), 2)

        使用cv2.findContours(image, mode, method)检测轮廓,常用参数如下:

  • image:二值图
  • mode:轮廓的检索模式
cv2.RETR_EXTERNAL只检测外轮廓
cv2.RETR_LIST检测的轮廓不建立等级关系
cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层
cv2.RETR_TREE建立一个等级树结构的轮廓
  • method:轮廓的近似方法
cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1
cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标

         使用cv2.drawContours绘制轮廓,效果如下:

近似多边形

python"># 四边形拟合,找到相应的的顶点
draw_approx = img.copy()
point1, point2 = list(), list()
for cnt in cnts:
    for epsilon in range(50):
        approx = cv2.approxPolyDP(cnt, epsilon, True)
        if len(approx) == 4:
            break
    cv2.polylines(draw_approx, [approx], True, (0, 0, 255), 2)  # 绘制边
    for i in approx:
        cv2.circle(draw_approx, i[0], 6, (0, 0, 0), -1)  # 绘制顶点

        使用cv2.approxPolyDP(curve, epsilon, closed)近似多边形,常用参数如下:

  • curve:输入二维点集的数组
  • epsilon:近似结果的精度。这是原曲线和其近似曲线之间的最大距离。epsilon越小,近似结果的折线形状越“接近”曲线。详情可见:近似多边形
  • closed:TrueFalseTrue表示近似曲线是闭合的(第一个顶点与最后一个顶点相互连接),False表示近似曲线是不闭合

        cv2.approxPolyDP返回近似多边形的顶点坐标,这里用范围内的值遍历epsilon精度,当返回的顶点坐标数量4时,即为四边形

        对每个轮廓进行四边形拟合,使用cv2.polylinescv2.circle分别绘制顶点,效果如下:

        四边形拟合完成后,对每个四边形的顶点进行排序

透视矫正

python"># 透视矫正目标区域
w, h = 900, 300
rect = [point1[0], point1[-1], point2[-1], point2[0]]  # 顺序为第一个四边形的左上,第四个四边形的右上,第四个四边形的右下,第一个四边形的左下
pts1 = np.array(rect, dtype="float32")
pts2 = np.array([rect[0], [rect[0][0] + w, rect[0][1]],
                [rect[0][0] + w, rect[0][1] + h], [rect[0][0], rect[0][1] + h]], dtype="float32")
M = cv2.getPerspectiveTransform(pts1, pts2)  # 变换矩阵
img_warped = cv2.warpPerspective(img, M, (1500, 500))  # 透视变换

         使用cv2.warpPerspective进行透视变换之前,需要先由cv2.getPerspectiveTransform获取变换矩阵

        cv2.getPerspectiveTransform(src, dst)常用参数如下:

  • src:变换前图像四边形顶点坐标数组),其中任意三点不共线
  • dst:变换后图像四边形顶点坐标数组),其中任意三点不共线

        这里输入变换前四边形的顶点坐标,分别为第一个四边形的左上坐标第四个四边形的右上坐标第四个四边形的右下坐标第一个四边形的左下坐标;输入变换后四边形的顶点坐标,是以第一个四边形的左上坐标延长宽和高。

         cv2.getPerspectiveTransform(src, M, dsize)常用参数如下:

  • src:输入图像
  • M:变换矩阵
  • dsize:输出图像大小

         变换效果如下:

python">img_warped = img_warped[rect[0][1]: rect[0][1] + h, rect[0][0]: rect[0][0] + w]  # 抠出变换后的区域

         将变换后的区域抠出来,效果如下:

参考链接

三分钟带你快速学习RGB、HSV和HSL颜色空间 - 知乎

第8章 形态学操作 -- 8.3 核函数 cv2.getStructuringElement()_Enzo 想砸电脑的博客-CSDN博客

Python +OpenCV CH9:形态学操作(morphologyEx扩展)_liguoxin1990的博客-CSDN博客

Python OpenCV approxPolyDP()函数

多边形逼近cv2.approxPolyDP和Douglas-Peucker算法_00000cj的博客-CSDN博客_多边形逼近算法


http://www.niftyadmin.cn/n/1093577.html

相关文章

libmad使用一步步进阶

原文地址:http://my.oschina.net/mjRao/blog/57874 libmad是一个开源mp3解码库,其对mp3解码算法做了很多优化,性能较好,很多播放器如mplayer、xmms等都是使用这个开源库进行解码的;如果要设计mp3播放器而又不想研究mp…

log4j2入门教程(二)简介2(propreties配置)

1.简介2(1)Log4j是什么?log4j是一个由Java编写的,可靠、快速和灵活的日志框架(API),它是Apache下的一个开源项目。又是Apache,我表示他们实在是比较强大,很多开源软件都是…

读取*.wav音频文件

1、wav音频文件的格式 wav文件由文件头和采样数据2部分组成。 文件头又分为RIFF(Resource Interchange File Format)、WAVE文件标识段 和 声音数据格式说明段组成。 各段的起始地址分别由RIFF标识符、WAVE标识符、以及波形格式标识符(FMT&…

CentOS、Ubuntu、Debian三个linux比较异同[转]

Linux有非常多的发行版本,从性质上划分,大体分为由商业公司维护的商业版本与由开源社区维护的免费发行版本。 商业版本以Redhat为代表,开源社区版本则以debian为代表。这些版本各有不同的特点,在不同的应用领域发挥着不同的作用&a…

函数返回类型为指针类型时的一些问题

原文出处&#xff1a;http://blog.csdn.net/tigerjb/article/details/7425571#comments 1.先看程序&#xff1a; #include<stdio.h> char *returnStr() {char *p “tigerjibo”;return p; } int main() {char*str;str returnStr();//str[0]’T’;则会引起错误&#x…

模拟电路一-----模拟电路用途

1. 转载于:https://www.cnblogs.com/cui0x01/p/6377516.html

内核模块编程基础

1、应用程序与内核模块 内核模块可以看做内核程序的一部分&#xff0c;类似于Window下的补丁&#xff0c;但安装比补丁方便&#xff0c;可在内核运行时安装与卸载 (1)应用程序&#xff1a;man函数的return执行完后则应用程序退出&#xff0c;释放内存 (2)内核模块&#xff1a;…

Command /usr/bin/codesign failed with exit code 1

转自http://blog.csdn.net/sico2sico/article/details/53068332 1.手机系统升级到10.11.后 Xcode8.0 不支持 就任性的升级到Xcode8.1 那么你猜对了 坑来了 /Users/wudezhi/Library/Developer/Xcode/DerivedData/kyExpress-gwuzzktwodhmlnemsyffjierabxs/Build/Products/Debug-…