如何用opencv找到图片中的橘猫?【python】

如何用opencv找到图片中的橘猫?【python】

橘猫
这张图片中有一只橘猫。我们如何才能让计算机找到它呢?由于橘猫的颜色与草地的颜色有较大的差别,我们可以尝试识别橘猫的颜色。

一、HSV色彩空间

计算机中的图片通常使用RGB色彩模型来保存,但是这三种颜色分量的取值与所生成的颜色之间的联系并不直观。这时我们可以通过HSV来进行颜色筛选,而不是RGB。HSV实际上是一种将RGB色彩模型中的点表示在圆柱坐标系中的方法。HSV即色相、饱和度、明度(英语:Hue, Saturation, Value),又称HSB,其中B即英语:Brightness。

import cv2
import numpy as np


def show_hsv():
    global lower_bound
    global upper_bound
    # 创建一个空白图像,大小为256x256
    h, w = 256, 256
    image = np.zeros((h, w, 3), dtype=np.uint8)

    # 在图像中填充HSV颜色
    for i in range(h):
        for j in range(w):
            # Hue 值从 lower_bound 到 upper_bound 之间均匀分布
            hue = lower_bound[0] + (upper_bound[0] - lower_bound[0]) * j // w
            saturation = lower_bound[1] + (upper_bound[1] - lower_bound[1]) * i // h
            value = lower_bound[2] + (upper_bound[2] - lower_bound[2]) * (h - i) // h

            # 将HSV颜色转换为BGR格式
            hsv_color = np.uint8([[[hue, saturation, value]]])
            bgr_color = cv2.cvtColor(hsv_color, cv2.COLOR_HSV2BGR)
            image[i, j] = bgr_color

    # 显示生成的颜色图像
    cv2.imshow("Color Range", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


if __name__ == '__main__':
    # 定义 HSV 范围
    lower_bound = np.array([0, 0, 0])
    upper_bound = np.array([255, 255, 255])
    show_hsv()

运行以上脚本,即可看到完整的HSV色彩空间。如图所示:

现在我们需要修改 lower_bound 和 upper_bound,筛选颜色范围为橘黄色。经验证,将上文代码中的

lower_bound = np.array([0, 0, 0])
upper_bound = np.array([255, 255, 255])

修改为

lower_bound = np.array([10, 90, 150])
upper_bound = np.array([30, 180, 230])

可以比较好地筛选出橘猫的颜色。修改后运行代码,结果如图所示:

二、寻找猫猫

现在让我们开始寻找图片中的橘猫。
把上方的橘猫图片保存为 test.jpg,放置在 python 脚本相同路径下。脚本如下:

import cv2
import numpy as np


def highlight_color_and_find_centers(frame, roi_area, do_paint_targets=True):
    global lower_bound
    global upper_bound
    y_start, y_end, x_start, x_end = roi_area
    roi = frame[y_start:y_end, x_start:x_end]

    # 转换为 HSV 颜色空间
    hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

    # 创建掩膜,只保留在颜色范围内的区域
    mask = cv2.inRange(hsv_roi, lower_bound, upper_bound)

    # 查找掩膜中的轮廓
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    centers_with_area = []

    for contour in contours:
        area = cv2.contourArea(contour)
        print(f"area = {area}")
        if area > int(2):
            x, y, w, h = cv2.boundingRect(contour)
            print(f"w = {w}, h = {h}")
            M = cv2.moments(contour)
            if M['m00'] != 0:
                cX = int(M['m10'] / M['m00'])
                cY = int(M['m01'] / M['m00'])
                centers_with_area.append(((cX + x_start, cY + y_start), area))

    centers_with_area = sorted(centers_with_area, key=lambda x: x[1], reverse=True)
    centers = [center for center, area in centers_with_area]

    if do_paint_targets:
        cv2.rectangle(frame, (x_start, y_start), (x_end, y_end), (0, 255, 0), 2)
        for contour in contours:
            cv2.drawContours(roi, [contour], -1, (255, 0, 255), thickness=cv2.FILLED)

    return centers


def main():
    input_image_path = "test.jpg"
    output_image_path = "combined_image.jpg"
    roi_area = (0, 720, 0, 1280)  # y1, y2, x1, x2

    # 读取原始图像
    frame = cv2.imread(input_image_path)

    # 处理图像
    processed_frame = frame.copy()  # 创建处理后的图像副本
    highlight_color_and_find_centers(processed_frame, roi_area)

    # 调整图像大小比例
    scale_percent = 50  # 将图像缩放为原始大小的50%
    width = int(frame.shape[1] * scale_percent / 100)
    height = int(frame.shape[0] * scale_percent / 100)
    dim = (width, height)

    # 对原始图像和处理后的图像进行缩放
    resized_frame = cv2.resize(frame, dim, interpolation=cv2.INTER_AREA)
    resized_processed_frame = cv2.resize(processed_frame, dim, interpolation=cv2.INTER_AREA)

    # 将处理前后的图像水平拼接
    combined_frame = cv2.vconcat([resized_frame, resized_processed_frame])  # 水平拼接
    cv2.imwrite(output_image_path, combined_frame)

    # 显示拼接后的图像
    cv2.imshow("Before and After (Resized)", combined_frame)

    # 等待键盘输入
    if cv2.waitKey(0):
        print("Keyboard interrupt")

    # 关闭所有窗口
    cv2.destroyAllWindows()


if __name__ == '__main__':
    # 定义 HSV 范围
    lower_bound = np.array([10, 90, 150])
    upper_bound = np.array([30, 180, 230])
    main()

运行脚本,结果如下:

这个对比图片将会保存为 combined_image.jpg,与脚本在相同路径下。

三、代码解析

上文中的代码已经可以完成任务了,以下是对代码的一些解析。

1. ROI

代码中,roi(Region of interest) 是需要筛选颜色的区域。由于橘猫图片的尺寸为1280×720,脚本中将 roi 设置为

roi_area = (0, 720, 0, 1280)  # y1, y2, x1, x2

这样将会识别整个图片中的目标颜色(本文中为橘黄色)。如果修改 roi,将会只对 roi 范围内的部分进行颜色识别。例如将 roi 修改为

roi_area = (0, 360, 0, 640)  # y1, y2, x1, x2

则只会对(0, 0)至(640, 360)这一矩形范围内进行颜色识别。运行结果如图所示:

2. 缩放图像

代码中以下内容的作用是将原始图像(frame)和处理后的图像(processed_frame)缩放到原尺寸的一半。

    # 调整图像大小比例
    scale_percent = 50  # 将图像缩放为原始大小的50%
    width = int(frame.shape[1] * scale_percent / 100)
    height = int(frame.shape[0] * scale_percent / 100)
    dim = (width, height)

    # 对原始图像和处理后的图像进行缩放
    resized_frame = cv2.resize(frame, dim, interpolation=cv2.INTER_AREA)
    resized_processed_frame = cv2.resize(processed_frame, dim, interpolation=cv2.INTER_AREA)
3. 图像拼接

代码中以下内容的作用是将缩小后的原图与处理后的图像进行拼接,并且把拼接后的图片保存为 combined_image.jpg 。

    # 将处理前后的图像拼接
    combined_frame = cv2.vconcat([resized_frame, resized_processed_frame])  # 拼接
    cv2.imwrite(output_image_path, combined_frame)

如果想要水平拼接,将 vconcat 改为 hconcat 即可。

4. 显示图像

代码中以下内容的作用是将拼接后的图像显示出来。

    # 显示拼接后的图像
    cv2.imshow("Before and After (Resized)", combined_frame)

    # 等待键盘输入
    if cv2.waitKey(0):
        print("Keyboard interrupt")

    # 关闭所有窗口
    cv2.destroyAllWindows()
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值