Python从0到100(六十四):Python OpenCV-图像运算进阶实战

举报
是Dream呀 发表于 2024/10/21 21:30:58 2024/10/21
【摘要】 前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学习学习和学业的先行者!欢迎大家订阅专栏:零基础学Python:Python从0...

在这里插入图片描述

前言: 零基础学Python:Python从0到100最新最全教程 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学习学习和学业的先行者!
欢迎大家订阅专栏:零基础学Python:Python从0到100最新最全教程!

一、图像的基本操作

前言: 要使用OpenCV编写更好的优化代码,需要Numpy的丰富知识。

补充:图片路径问题

(1.相对路径

设置在当前文件夹下:

import cv2
image = cv2.imread("image.jpg")

注意: 读取的图片一定要保存在当前文件夹下

(2.绝对路径

图片可以不保存在当前文件夹下:

import cv2
image = cv2.imread("D:/opencv_python/image.jpg")
image1 = cv2.imread("D:\\opencv_python\\image.jpg")

注意:上述两种形式都可以,但是路径内不要出现中文,否则会出现错误

(3.调用os模块

如果工作路径中有中文,建议使用该方法:

import cv2
import os #导入os模块
os.chdir('D:/计算机视觉/OpenCV图像处理/') #此处路径中可以有中文字符
image = cv2.imread("image.jpg")

os.chdir() 改变当前目录到指定目录中

1.访问和修改像素值

加载彩色图像:

import numpy as np
import cv2 as cv
img = cv.imread("yanhuo.jpg")  # 此处必须为中文

imread(“yanhuo.jpg”) 图片名字必须为中文~

你可以通过行和列坐标来访问像素值。对于 BGR 图像,它返回一个由蓝色、绿色和红色值组成的数组。对于灰度图像,只返回相应的灰度。

px = img[100,100]
print( px )
# 仅访问蓝色像素
blue=img[100,100,2]
print(blue)

你可以用相同的方式修改像素值。

# 还可以使用相同方法修改像素值
img[100,100]=[255,2555,255]
print(img[100,100])

警告:

Numpy是用于快速数组计算的优化库。因此,简单地访问每个像素值并对其进行修改将非常缓慢,因此不建议使用。

  • 注意:
    上面的方法通常用于选择数组的区域,例如前5行和后3列。对于单个像素访问,Numpy数组方法array.item()和array.itemset())被认为更好,但是它们始终返回标量。如果要访问所有B,G,R值,则需要分别调用所有的array.item()。

更好的像素访问和编辑方法:

print(img.item(100,100,2))
# 修改RED值
img.itemset((100,100,2),100)
print(img.item(100,100,2))

2.访问图像属性

图像属性包括行数,列数和通道数,图像数据类型,像素数等。
图像的形状可通过img.shape访问。它返回行,列和通道数的元组(如果图像是彩色的):

print( img.shape )
  • 注意 如果图像是灰度的,则返回的元组仅包含行数和列数,因此这是检查加载的图像是灰度还是彩色的好方法。

像素总数可通过访问img.size

print( img.size )

图像数据类型通过img.dtype获得

print( img.dtype )
  • 注意 img.dtype在调试时非常重要,因为OpenCV-Python代码中的大量错误是由无效的数据类型引起的。

3.图像感兴趣区域ROI

有时候,你不得不处理一些特定区域的图像。对于图像中的眼睛检测,首先对整个图像进行人脸检测。在获取人脸图像时,我们只选择人脸区域,搜索其中的眼睛,而不是搜索整个图像。它提高了准确性(因为眼睛总是在面部上:D )和性能(因为我们搜索的区域很小)。
使用Numpy索引再次获得ROI。在这里,我要选择部分图像并将其复制到图像中的另一个区域:

a=img[100:500,130:390]
img[273:673,100:360]=a
cv.imshow('tuxiang',img)
cv.waitKey(0)

检查以下结果:
在这里插入图片描述
在这里插入图片描述

4.拆分和合并图像通道

有时你需要分别处理图像的B,G,R通道。在这种情况下,你需要将BGR图像拆分为单个通道。在其他情况下,你可能需要将这些单独的频道加入BGR图片。你可以通过以下方式简单地做到这一点:

b,g,r=cv.split(img)
img=cv.merge((b,g,r))
b = img [:, :, 0]
# 假设你要将所有红色像素都设置为零,则无需先拆分通道。numpy索引更快:
# img [:, :, 2] = 0

警告:

cv.split()是一项耗时的操作(就时间而言)。因此,仅在必要时才这样做。否则请进行Numpy索引。

5.为图像设置边框(填充)

如果要在图像周围创建边框(如相框),则可以使用cv.copyMakeBorder()。但是它在卷积运算,零填充等方面有更多应用。此函数采用以下参数:

1. src - 输入图像
2. top,bottom,left,right 边界宽度(以相应方向上的像素数为单位)
3. borderType - 定义要添加哪种边框的标志。它可以是以下类型:

  1. cv.BORDER_CONSTANT - 添加恒定的彩色边框。该值应作为下一个参数给出。
  2. cv.BORDER_REFLECT -边框将是边框元素的镜像,如下所示: fedcba | abcdefgh | hgfedcb
  3. cv.BORDER_REFLECT_101cv.BORDER_DEFAULT与上述相同,但略有变化,例如: gfedcb | abcdefgh | gfedcba
  4. cv.BORDER_REPLICATE最后一个元素被复制,像这样: aaaaaa | abcdefgh | hhhhhhh
  5. cv.BORDER_WRAP难以解释,它看起来像这样: cdefgh | abcdefgh | abcdefg
  6. value -边框的颜色,如果边框类型为cv.BORDER_CONSTANT
# -*-coding:utf-8 -*-
# @Author:到点了,心疼徐哥哥
# 奥利给干!!!
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
BLUE = [255,255,0]
img1 = cv.imread('yanhuo.jpg')
replicate = cv.copyMakeBorder(img1,100,100,100,100,cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(img1,100,100,100,100,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(img1,100,100,100,100,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(img1,100,100,100,100,cv.BORDER_WRAP)
constant= cv.copyMakeBorder(img1,100,100,100,100,cv.BORDER_CONSTANT,value=BLUE)
plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()

在这里插入图片描述

二、图像上的算术运算

1.图像加法

您可以通过OpenCV函数cv.add()或仅通过numpy操作res = img1 + img2添加两个图像。两个图像应具有相同的深度和类型,或者第二个图像可以只是一个标量值。

注意 OpenCV加法和Numpy加法之间有区别。OpenCV加法是饱和运算,而Numpy加法是模运算。

import numpy as np
import cv2 as cv
x=np.uint8([250])
y=np.uint8([10])
print(cv.add(x,y))
print(x+y)

在这里插入图片描述

2.图像融合

2.1图片一致

当添加两个图像时,它将更加可见。OpenCV功能将提供更好的结果。因此,始终最好坚持使用OpenCV功能。
在这里,我拍摄了两个图像,将它们融合在一起。第一幅图像的权重为0.7,第二幅图像的权重为0.3。cv.addWeighted()在图像上应用以下公式。

img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')
dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()

注意: 这种方法,只适用于小、类型(高度/宽度/通道数)相同的图片之间!
cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])

  1. src1 – 输入图片1
  2. alpha – 图像1的加权系数(融合比例)
  3. src2 –输入图像2,必须与图片1的大小、类型(高度/宽度/通道数)相同。
  4. beta –图像2的加权系数(融合比例),beta = 1.0 - alpha
  5. dst – 两个图像加权和后的图像,即输出的图像
  6. gamma –加权和后的图像的偏移量
  7. dtype –输出数组的可选深度;当两个输入数组具有相同的深度时,dtype可以设置为-1(默认值),这将相当于src1.depth()

2.2图片不一致

但是如果这两张图片大小不相同,怎么解决?有两种方法可以解决这个问题:

  1. 重置其中一张图片的大小类型,使其与另一张图片大小类型相同;
  2. 在较大的图片中创建感兴趣区域roi,roi的大小类型应与另一张图片的相同。

注意: 方法1改变图片大小时,图片的分辨率也会发生变化,因此图片的内容会产生形变;方法2没有改变图片的大小,故不会有这种问题产生。
主要函数:

dst = cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])

参数意义:

  1. scr:原图像
  2. dsize:输出的图像大小
  3. dst:输出的图像。当参数dsize不为0时,dst的大小为size;否则,它的大4. 小需要根据src的大小,fx和fy决定。
  4. fx:沿水平轴的比例因子
  5. fy:沿垂直轴的比例因子

参数dsize和参数(fx, fy)不能够同时为0
interpolation:插值方法,共5种:

  1. INTER_LINEAR - 双线性插值(默认)(放大图像推荐使用)
  2. INTER_NEAREST - 最近邻插值
  3. INTER_AREA - 基于像素局部的重采样插值(缩小图像推荐使用)。该方法对于图像抽取(image decimation)来说可能更好,但如果是放大图像,和最近邻插值效果类似。
  4. INTER_CUBIC - 基于4x4像素邻域的3次插值(放大图像推荐使用)
  5. INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos(兰索斯)插值

方法1:重置其中一张图片的大小类型,使其与另一张图片大小类型相同

import cv2
import numpy as np
import matplotlib.pyplot as plt

bear = cv2.imread('jokerxue.jpg')
sky = cv2.imread('yanhuo.jpg')
rows, cols = sky.shape[:2] #获取sky的高度、宽度
print(sky.shape[:2]) #(800, 1200)
print(bear.shape[:2]) #(224, 224)
bear_dst = cv2.resize(bear,(cols,rows),interpolation=cv2.INTER_NEAREST) #放大图像
add_img = cv2.addWeighted(bear_dst,0.7,sky,0.3,0) #图像融合
cv2.imshow('add_img',add_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述
显示多张图片:
用到matplotlib库中的pyplot模块matplotlib.pyplot.subplot(nrows, ncols, index):
此函数可以用于在一个窗口显示多幅图像。

参数:

  1. nrows和ncols表示一张图被分为nrows*ncols个区域
  2. index表示子图所处的位置,起始位置索引为1,即1<=index<=nrows*ncols。

提示:此函数必须在 imshow() 函数之前,要不然无效。

matplotlib.pyplot.axis(‘off’) # 不显示坐标轴

import cv2
import numpy as np
import matplotlib.pyplot as plt

bear = cv2.imread('jokerxue.jpg')
sky = cv2.imread('yanhuo.jpg')
rows, cols = sky.shape[:2] #获取sky的高度、宽度
print(sky.shape[:2]) #(800, 1200)
print(bear.shape[:2]) #(224, 224)
bear_dst = cv2.resize(bear,(cols,rows),interpolation=cv2.INTER_NEAREST) #放大图像
add_img = cv2.addWeighted(bear_dst,0.7,sky,0.3,0) #图像融合
# cv2.imshow('add_img',add_img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# 显示图片
titles = ['BearBrown','Sky','add_img']
imgs = [bear,sky,add_img]
for i in range(len(imgs)):
    plt.subplot(1,3,i+1)
    imgs[i]=cv2.cvtColor(imgs[i],cv2.COLOR_BGR2RGB)
    plt.imshow(imgs[i],'gray')
    plt.title(titles[i])
    plt.axis('off')
plt.show()

效果展示:
在这里插入图片描述

方法2:在较大的图片中创建感兴趣区域roi,roi的大小类型应与另一张图片的相同

注意:必须把小的照片放到大的照片里面,位置关系很重要!
# -*-coding:utf-8 -*-
# @Author:到点了,心疼徐哥哥
# 奥利给干!!!
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os

bear = cv2.imread('yanhuo.jpg')
sky = cv2.imread('jokerxue.jpg')
# print(sky.shape[:2]) #(800, 1200)
# print(bear.shape[:2])
# 根据小图像的大小,在大图像上创建感兴趣区域roi(放置位置任意取)
rows, cols = bear.shape[:2] #获取bear的高度、宽度
print(rows,cols)
roi = sky[0:rows, 0:cols]

dst = cv2.addWeighted(bear,0.3,roi,0.7,0) #图像融合
add_img = sky.copy() #对原图像进行拷贝
add_img[0:rows, 0:cols] = dst  # 将融合后的区域放进原图

# 显示图片
titles = ['BearBrown','Sky','add_img']
imgs = [bear,sky,add_img]
for i in range(len(imgs)):
    plt.subplot(1,3,i+1)
    imgs[i]=cv2.cvtColor(imgs[i],cv2.COLOR_BGR2RGB)
    plt.imshow(imgs[i],'gray')
    plt.title(titles[i])
    plt.axis('off')
plt.show()

效果展示:
在这里插入图片描述

3.按位运算

包括按位与(AND)、按位或(OR)、按位非(NOT)、按位异或(XOR)等运算。
按位运算的用途:比如要得到一个加logo的图像。 如果将两幅图片直接相加会改变图片的颜色,如果用图像混合,则会改变图片的透明度,这时候就需要用按位操作,既不改变图像颜色,又不改变图像透明度,类似PS
这里需要了解一个术语——掩膜(mask) 是用一副二值化图片对另外一幅图片进行局部的遮挡。

3.1主要函数

  1. cv2.bitwise_and():位与运算,有0则为0, 全为1则为1
  2. cv2.bitwise_not():或运算,有1则为1, 全为0则为0
  3. cv2.bitwise_or():非运算,非0为1, 非1为0
  4. cv2.bitwise_xor():异或运算,不同为1, 相同为0
  5. ret, dst = cv2.threshold(src, thresh, maxval,type):阈值(二值化操作),阈值又叫临界值,是指一个效应能够产生的的最低值或最高。
    dst: 输出图像
    src: 输入图像,只能输入单通道图像,一般为灰度图
    thresh: 阈值
    maxval: 当像素值大于阈值(或者小于阈值,根据type来决定),所赋予的值
    type:阈值操作的类型:
    THRESH_BINARY: 二值阈值化
    THRESH_TOZERO_INV: 反二值阈值化
    THRESH_TRUNC: 截断阈值化
    THRESH_TOZERO: 低于阈值被置为0
    THRESH_TOZERO_INV: 超过阈值被置为0

3.2实例演示:

logo:
在这里插入图片描述
照片主体:
在这里插入图片描述

# -*-coding:utf-8 -*-
# @Author:到点了,心疼徐哥哥
# 奥利给干!!!
# 加载两张图片
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
import os
img1 = cv.imread('yanhuo.jpg')
img2 = cv.imread('logo.png')
# 我想把logo放在左上角,所以我创建了ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]
# 现在创建logo的掩码,并同时创建其相反掩码
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
# 现在将ROI中logo的区域涂黑
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
# 仅从logo图像中提取logo区域
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# 将logo放入ROI并修改主图像
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()

效果展示:
在这里插入图片描述

3.3代码详析:

1.rows,cols,channels = img2.shape
把图片2像素的行数,列数以及通道数返回给rows,cols,channels。
2.oi = img1[0:rows, 0:cols ]
然后将图片1从第0行到rows行,第0列到cols列的区域设定为roi(即感兴趣区域)
3.cvtColor函数
此函数的作用是将一个图像从一个颜色空间转换到另一个颜色空间。

3.4过程拆解

过程拆分时,需要我们显示各个时期的图像,需要用到:

cv.namedWindow("image", cv.WINDOW_NORMAL)
cv.imshow("image", img2_fg)

cv.namedWindow()函数:
用法:
cv2.namedWindow(‘窗口标题’,默认参数)

  1. 窗口大小可以改变:cv2.namedWindow(“image”,cv2.WINDOW_NORMAL)
    或者cv2.namedWindow(‘image’,cv2.WINDOW_GUI_NORMAL)

  2. 窗口大小不可以改变:cv2.namedWindow(“image”,cv2.WINDOW_AUTOSIZE)

  3. 窗口大小自适应比例:cv2.namedWindow(“image”,cv2.WINDOW_FREERATIO)

  4. 窗口大小保持比例:cv2.namedWindow(“image”,cv2.WINDOW_KEEPRATIO)

  5. 显示色彩变成暗色:cv2.namedWindow(‘image’,cv2.WINDOW_GUI_EXPANDED)

1.我们可以首先将logo的颜色空间进行转换
转换成灰度图像,然后使用阀值函数,将我们的logo提取出来,然后将其他部分变为黑色,即像素值为0。

img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)  # 颜色空间的转换
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)  # 掩码 黑色

在这里插入图片描述

2.然后我们得到它相反的图像
使用cv.bitwise_not,得到:

mask_inv = cv.bitwise_not(mask)

在这里插入图片描述
3.因为mask的性质就是非0的地方保留原样,其余为0
接下来我们利用mask_inv去取得messi那张图的背景部分,现在将ROI中logo的区域涂黑

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

在这里插入图片描述
4.仅从logo图像中提取logo区域
那么我们接下来就可以嵌入了,首先我们得原图像还原,那么只需要使用:

img2_fg = cv.bitwise_and(img2,img2,mask=mask)

5.将logo放入ROI并修改主图像

dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()

得到最终的结果:
在这里插入图片描述
c_center)

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。