OpenCV-07 图像算术运算

在图像处理领域,OpenCV 是一个功能强大的库,为我们提供了丰富的工具来操作和处理图像。今天,我们将深入探讨 OpenCV 中的图像算术运算,通过具体的代码示例来理解这些功能的应用。

关注公众号:资小库 回复opencv获取源码,及项目图片等

一、图像相加(add函数)

add函数中,我们主要进行了图像的相加操作。

def add():
    img1 = cv.imread("../img/img1.jpg")
    img2 = cv.imread("../img/img2.jpg")

首先,使用cv.imread函数读取两张图像img1img2。这两张图像是我们进行后续算术运算的基础。

print(img1.shape,img2.shape)

通过shape属性,我们可以获取图像的尺寸信息,包括高度、宽度和通道数。了解这些信息对于后续的图像处理操作非常重要,比如确保要进行运算的图像尺寸相同。

img2 = cv.rotate(img2, cv.ROTATE_90_CLOCKWISE)
height, width = img1.shape[:2]
img2 = cv.resize(img2, (width, height))

由于原始的两张图像尺寸和方向可能不一致,所以我们对img2进行了旋转(使用cv.rotate函数将其顺时针旋转 90 度)和大小调整(使用cv.resize函数将其尺寸调整为与img1相同)。

img3 = img1 + img2

这里使用了 Numpy 的加法操作对两张图像进行相加。需要注意的是,Numpy 的这种加法是一种模运算。例如,如果一个像素的 RGB 值分别为 (200, 150, 100) 和 (100, 100, 50),相加后的结果如果超过了 255(如第一个通道 200 + 100 = 300),那么最终的结果会对 256 取模,得到 (44, 250, 150)。

x = np.uint8([10])
y = np.uint8([250])
print(cv.add(x,y))

接下来,使用cv.add函数进行加法操作。cv.add是 OpenCV 中的加法函数,它的加法是饱和操作。也就是说,如果相加后的结果超过了 255,那么结果会被截断为 255。例如对于上面的xy,Numpy 相加可能会取模,而cv.add的结果是 [255]

openImg(cv.add(img1,img2))

使用cv.add函数对调整后的img1img2进行相加,并通过openImg函数显示结果

openImg(cv.addWeighted(img1,0.3,img2,0.7,0))

cv.addWeighted函数用于按照一定的权重比例对两张图像进行加权求和。在这里,img1的权重是 0.3,img2的权重是 0.7,最后一个参数 0 是添加的常量。

二、图像添加徽标

def logo():
    img = cv.imread("../img/img2.jpg")
    logo = cv.imread("../img/img3.jpg")

同样先读取主图像img和徽标图像logo

rows, cols, channels = logo.shape
roi = img [0:rows,0:cols]

获取徽标图像的行数、列数和通道数,然后从主图像中切出与徽标图像大小相同的区域roi,这个区域将用于放置徽标。

img2gray = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)

将徽标图像转换为灰度图像(cv.cvtColor函数),然后使用阈值处理(cv.threshold函数)将灰度图像转换为二值图像mask。在这个二值图像中,像素值大于 10 的变为 255(白色),小于 10 的变为 0(黑色)。

mask_inv = cv.bitwise_not(mask)

mask进行取反操作,得到mask_inv,原来的白色部分变成黑色,黑色部分变成白色。

img1_bg = cv.bitwise_and(roi, roi, mask=mask_inv)
img2_fg = cv.bitwise_and(logo, logo, mask=mask)

使用按位与操作(cv.bitwise_and函数),img1_bgroi中对应于mask_inv中黑色部分(值为 0)的像素变为 0(变黑),img2_fg从徽标图像中提取出对应于mask中白色部分(值为 255)的像素。

dst = cv.add(img1_bg, img2_fg)
img[0:rows, 0:cols] = dst

最后,将提取出的徽标部分img2_fg添加到主图像的对应区域roi中,实现徽标的添加。

三、提取透明图像

def logo_2():
    img = cv.imread("../img/img2.jpg")
    logo = cv.imread("../img/img3.jpg")
    rows, cols, channels = logo.shape
    roi = img [0:rows, 0:cols]

logo函数开始部分类似,读取图像并切出对应区域。

img2gray = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 240, 255, cv.THRESH_BINARY)

同样将徽标图像转换为灰度图像并进行阈值处理,这里阈值设置为 240,目的是更好地提取徽标中的主要部分。

indices = np.where(img1_bg == 0)
img1_bg[indices] = logo[indices]
img[0:rows, 0:cols] = img1_bg

通过np.where函数找到img1_bg中值为 0 的像素坐标,然后将徽标图像logo中对应坐标的像素值赋给img1_bg,最后将处理后的区域放回主图像中,实现类似透明效果的徽标添加。

四:学习中的问题

roi = img [0:rows,0:cols] 这句使用的是什么语法

这段代码 roi = img[0:rows, 0:cols] 使用的是 Python 中对数组(或者说是类似数组的对象,比如图像矩阵)进行切片(slice)的语法。具体解释如下:

  • img 是一个数组(通常是图像的像素矩阵,可以是 NumPy 数组或类似的数据结构)。
  • roi 是一个变量,用来存储从 img 中切出来的部分,称为感兴趣区域(Region of Interest)。

现在来分解 img[0:rows, 0:cols]

  • img[0:rows, 0:cols] 表示从 img 中切出一个子数组。
  • 0:rows 表示沿着第一个轴(通常是行)从索引 0 开始到索引 rows-1 结束的范围。这里 rows 是一个变量或者常数,表示行数的上限。
  • 0:cols 表示沿着第二个轴(通常是列)从索引 0 开始到索引 cols-1 结束的范围。同样,cols 是表示列数的变量或常数。

因此,img[0:rows, 0:cols] 实际上是从 img 的左上角开始,取出行数为 rows,列数为 cols 的子数组或子矩阵。

这种切片语法在 Python 中非常常见,不仅仅适用于 NumPy 数组,还适用于 Python 中的列表等可迭代对象。

为什么在opencv中0表示黑色,255表示白色

在OpenCV(Open Source Computer Vision Library)中,像素值的范围通常是从0到255,这是因为像素的灰度值通常用一个8位整数(即一个字节)来表示。这种表示方式有几个历史和技术上的原因:

  1. 灰度范围的表示

    • 0 表示最暗的灰度,即黑色。
    • 255 表示最亮的灰度,即白色。
    • 在这个8位灰度范围内,0代表了完全没有光,而255代表了所有可见光的最大强度。
  2. 数值计算的方便性

    • 使用0到255的范围可以方便地进行数值计算和像素操作,因为这正好是一个八位无符号整数(8-bit unsigned integer)的表示范围。
  3. 历史原因

    • 这种表示方式源于早期计算机图像处理的标准化,例如在黑白电视时代,8位灰度图像就是一种常见的格式。
    • 许多传统的图像处理算法和技术,如阈值处理、直方图均衡化等,都是基于这种0到255的像素值范围设计的。

因此,尽管在不同的图像处理库和应用中可能有不同的约定和表示方式,但在OpenCV中,0表示黑色,255表示白色的约定是一种标准化的选择,使得图像处理和计算更加方便和统一。

openCV bitwise_and函数参数是什么

在 OpenCV 中,bitwise_and 函数用于执行图像的按位与(bitwise AND)操作。它的参数通常是两个图像数组(数组可以是单通道或多通道的)或者一个图像数组和一个标量。具体来说,参数如下:

  1. src1: 第一个输入图像数组或标量。
  2. src2: 第二个输入图像数组或标量。
  3. dst: 输出图像数组,用于存储按位与操作的结果。

如果 src1src2 是图像数组,则它们必须具有相同的尺寸和类型(单通道或多通道)。如果其中一个是标量,那么该标量将与另一个数组的每个像素进行按位与操作。

使用示例(Python 中的 OpenCV):

import cv2
import numpy as np

# 创建两个示例图像数组
img1 = np.array([[1, 0, 1],
                 [0, 1, 0],
                 [1, 0, 1]], dtype=np.uint8)

img2 = np.array([[1, 1, 0],
                 [0, 0, 1],
                 [1, 1, 0]], dtype=np.uint8)

# 执行按位与操作
result = cv2.bitwise_and(img1, img2)

print("Result of bitwise AND:")
print(result)

 在这个例子中,img1img2 是两个输入图像数组,它们分别表示如下:

img1:
[[1 0 1]
 [0 1 0]
 [1 0 1]]

img2:
[[1 1 0]
 [0 0 1]
 [1 1 0]]

 执行 bitwise_and 后,输出的 result 图像数组将是按位与操作的结果:

Result of bitwise AND:
[[1 0 0]
 [0 0 0]
 [1 0 0]]

 这是因为按位与操作的规则是对应位置上的两个像素都为 1 时,结果对应位置为 1;否则为 0。

 源码中bitwise_and_test函数也验证了这一点

opencv中灰度图像的转换原理

在OpenCV中,灰度图像的转换原理涉及到将彩色图像转换为灰度图像的过程。下面是转换的基本原理和步骤:

  1. 彩色图像表示

    • 彩色图像通常由红(R)、绿(G)、蓝(B)三个颜色通道组成,每个通道使用一个8位整数(0到255)表示像素的强度。
  2. 灰度图像的生成

    • 灰度图像是一种只有一个通道的图像,每个像素仅使用一个8位整数(0到255)来表示灰度强度,其中0代表黑色,255代表白色。
  3. 颜色通道混合

    • OpenCV中通常使用以下加权平均方法将彩色图像转换为灰度图像: [ \text{灰度值} = 0.299 \times R + 0.587 \times G + 0.114 \times B ] 这个公式是根据人眼对不同颜色敏感度的心理视觉模型确定的,其中红色对比绿色和蓝色更敏感。
  4. 像素级别的转换

    • 对于每个像素,应用上述公式,将其彩色值转换为灰度值。这个过程在整个图像上进行,从而生成一张灰度图像,其中每个像素都代表了相应位置上的灰度强度。
  5. OpenCV函数实现

    • 在OpenCV中,可以使用 cv2.cvtColor() 函数来实现颜色空间转换。具体转换为灰度图像时,使用的是 cv2.COLOR_BGR2GRAY 或 cv2.COLOR_RGB2GRAY 参数,具体取决于图像的颜色通道顺序。
import cv2

# 读取彩色图像
color_image = cv2.imread('input_image.jpg')

# 转换为灰度图像
gray_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2GRAY)

# 显示结果
cv2.imshow('Color Image', color_image)
cv2.imshow('Gray Image', gray_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
彩色图像转变为灰度图像的公式
彩色图像到灰度图像的转换公式通常是基于一种加权平均的方式,其中不同颜色通道的值以不同的权重来计算。在多种公式中,最常见的是使用下列线性组合公式:

[ \text{灰度值} = 0.299 \times R + 0.587 \times G + 0.114 \times B ]

这里:

( R ) 是红色通道的像素值(0到255之间)。
( G ) 是绿色通道的像素值(0到255之间)。
( B ) 是蓝色通道的像素值(0到255之间)。
这些权重(0.299, 0.587, 和 0.114)是根据人眼对不同颜色的感知强度而来的,反映了人眼在视觉感知中对于红色、绿色和蓝色的敏感度差异。

五:全部源码

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

from gui.A_GetStartedWithImages import openImg
"""
图像的算术运算
"""
def add():
    img1 = cv.imread("../img/img1.jpg")
    img2 = cv.imread("../img/img2.jpg")
    # 打印图像大小
    print(img1.shape,img2.shape)
    # 图像2是个横图,图像1为纵图,先旋转一下
    img2 = cv.rotate(img2, cv.ROTATE_90_CLOCKWISE)
    # 获取宽 高
    height, width = img1.shape[:2]
    # 将图像1的宽高给图像2
    img2 = cv.resize(img2, (width, height))
    print(img1.shape,img2.shape)
    # Numpy 添加是模运算 必须图像矩阵大小一致
    img3 = img1+img2
    openImg(img3)
    x = np.uint8([10])
    y = np.uint8([250])
    # >255的数据会被强制变成255
    print(cv.add(x,y))
    # OpenCV 添加是饱和操作
    # 感觉被高曝光了一样,在将两个图象相加时会发现 OpenCV 函数能够提供更好的结果,所以尽可能地选择 OpenCV 函数。
    openImg(cv.add(img1,img2))
    # 上面图像相加两者应该算是55开的,也可以调整比例混合
    '''
    src1: 第一个输入图像,通常为CV_8U、CV_16U、CV_32F之一,应与dst具有相同的通道数和深度。
    alpha: 第一个输入图像的权重系数。
    src2: 第二个输入图像,大小和类型应与src1相同。
    beta: 第二个输入图像的权重系数。
    gamma: 一个可选的增量,它被添加到每个输出像素上。
    dst: 输出图像,与src1和src2有相同的大小和类型。
    '''
    openImg(cv.addWeighted(img1,0.3,img2,0.7,0))

def logo():
    # 读取图像数据
    img = cv.imread("../img/img2.jpg")
    logo = cv.imread("../img/img3.jpg")
    # 获取logo图片的高,宽,通道,对于数组的行数,列数
    rows, cols, channels = logo.shape
    # 获取一个数组 0-rows,0-cols 切出这么一块区域
    roi = img [0:rows,0:cols]
    # 将图像进行灰度处理
    # 计算公式 [ \text{灰度值} = 0.299 \times R + 0.587 \times G + 0.114 \times B ]
    img2gray = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)
    # 颜色去生成数组,表示10以上的全部变成255也就是黑色,10一下的全部变成0也就是白色,10一下的基本是透明色,相加区别也不大
    ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
    # 取反0变成255 255变成0 10变成245,本来全是255,现在全变成0
    mask_inv = cv.bitwise_not(mask)
    # 现在使 ROI 中的徽标区域变黑 按位与操作,参考下面的例子,了解按位与操作,任何
    # 如果提供了 mask 参数,那么只有在 mask 中对应位置的像素值为非零时,src1 和 src2 对应位置的像素才会参与按位与操作。如果 mask 中的像素值为零,那么对应位置的输出像素将被置为零。
    img1_bg = cv.bitwise_and(roi, roi, mask=mask_inv)
    # 经过上面处理img1_bg应该都变成了0
    # 仅从徽标图像中获取徽标区域。 mask因为都是255,则都参与按位与操作,但是因为logo的rgb都基本不是1,所有img2_fg 约等于 logo 不排除有个别mask可能等于0
    img2_fg = cv.bitwise_and(logo, logo, mask=mask)
    # 在 ROI 中放置徽标并修改主图像
    # 所以这里白色加任何颜色都等于任何颜色
    dst = cv.add(img1_bg, img2_fg)
    #将这部分数组替换成那部分数组
    # 虽然替换了一大顿,效果应该等于  img[0:rows, 0:cols] = logo这段代码的效果,但是重点学习过程
    img[0:rows, 0:cols] = dst
    cv.imshow('res', img)
    cv.waitKey(0)
    cv.destroyAllWindows()

# 写个小demo,提取透明图像
def logo_2():
    # 读取图像数据
    img = cv.imread("../img/img2.jpg")
    logo = cv.imread("../img/img3.jpg")
    # 获取logo图片的高,宽,通道,对于数组的行数,列数
    rows, cols, channels = logo.shape
    # 获取一个数组 0-rows,0-cols 切出这么一块区域
    roi = img [0:rows,0:cols]
    # 将图像进行灰度处理
    # 计算公式 [ \text{灰度值} = 0.299 \times R + 0.587 \times G + 0.114 \times B ]
    img2gray = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)
    # 颜色去生成数组,表示10以上的全部变成255也就是黑色,10一下的全部变成0也就是白色,10一下的基本是透明色,相加区别也不大
    ret, mask = cv.threshold(img2gray, 240, 255, cv.THRESH_BINARY)
    # 现在使 ROI 中的徽标区域变黑 按位与操作,参考下面的例子,了解按位与操作,任何
    # 如果提供了 mask 参数,那么只有在 mask 中对应位置的像素值为非零时,src1 和 src2 对应位置的像素才会参与按位与操作。如果 mask 中的像素值为零,那么对应位置的输出像素将被置为零。
    img1_bg = cv.bitwise_and(roi, roi, mask=mask)
    # 寻找黑色的地方
    indices = np.where(img1_bg == 0)
    # 坐标点替换
    img1_bg[indices] = logo[indices]
    #将这部分数组替换成那部分数组
    # 虽然替换了一大顿,效果应该等于  img[0:rows, 0:cols] = logo这段代码的效果,但是重点学习过程
    img[0:rows, 0:cols] = img1_bg
    cv.imshow('res', img)
    cv.waitKey(0)
    cv.destroyAllWindows()
def bitwise_and_test():
    # 相同的就是原来的值,不同的就是0
    # 创建两个示例图像数组
    img1 = np.array([[10, 0, 1],
                     [0, 1, 0],
                     [1, 0, 1]], dtype=np.uint8)

    print(cv.bitwise_not(img1))

    img2 = np.array([[10, 1, 0],
                     [0, 0, 1],
                     [1, 1, 0]], dtype=np.uint8)

    # 执行按位与操作
    result = cv.bitwise_and(img1, img2)

    print("Result of bitwise AND:")
    print(result)


if __name__ == '__main__':
    logo_2()

      关注公众号:资小库,问题快速答疑解惑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会写爬虫的程序员B

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值