OpenCV核心运算(二)—图像基本与算术操作

news2024/11/17 9:44:43

目录

  • 2.1 图像的基本操作
      • 目标
      • 访问和修改像素值
      • 访问图像属性
      • 图像ROI
      • 分割和合并图像通道
      • 为图像制作边框(填充)
  • 2.2 图像上的算术操作
      • 目标
      • 图像加法
      • 图像混合
      • 位操作
      • 练习
  • 2.3 性能测量和改进技术
      • 目标
      • 用OpenCV测量性能
      • OpenCV中的默认优化
      • 更多的IPython魔法命令
      • 性能优化技术
      • 额外的资源

2.1 图像的基本操作

翻译及二次校对:cvtutorials.com

编辑者:廿瓶鲸(和鲸社区Siby团队成员)

目标

学会:

  • 访问像素值并修改它们
  • 访问图像属性
  • 设置感兴趣的区域(ROI)
  • 分割和合并图像

本节中几乎所有的操作都主要与Numpy而不是OpenCV有关。要想用OpenCV写出更好的优化代码,需要有良好的Numpy知识。

(例子将在Python终端中显示,因为大多数只是单行的代码)

访问和修改像素值

让我们先加载一个彩色图像。

>>> import numpy as np
>>> import cv2 as cv
>>> img = cv.imread('messi5.jpg')

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

>>> px = img[100,100]
>>> print( px )
[157 166 200]
# accessing only blue pixel
>>> blue = img[100,100,0]
>>> print( blue )
157

你可以用同样的方法修改像素值。

>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]

警告:Numpy是一个用于快速数组计算的优化库。因此,简单地访问每一个像素值并对其进行修改将是非常缓慢的,我们不鼓励这样做。

注释:上述方法通常用于选择一个数组的某个区域,例如前5行和后3列。对于单个像素的访问,Numpy数组方法,array.item()和array.itemset()被认为更好。然而,它们总是返回一个标量,所以如果你想访问所有的B、G、R值,你将需要为每个值分别调用array.item()。

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

# accessing RED value
>>> img.item(10,10,2)
59
# modifying RED value
>>> img.itemset((10,10,2),100)
>>> img.item(10,10,2)
100

访问图像属性

图像属性包括行、列和通道的数量;图像数据的类型;像素的数量等。

图像的形状是由img.shape访问的。它返回一个包含行数、列数和通道数(如果图像是彩色的)的元组。

>>> print( img.shape )
(342, 548, 3)

如果一个图像是灰度的,返回的元组只包含行和列的数量,所以这是一个很好的方法来检查加载的图像是灰度还是彩色。

总像素数由Img.size访问。

>> print( img.size )
562248

图像数据类型由img.type获得。

>> print( img.dtype )
uint8

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

图像ROI

有时,你必须对图像的某些区域进行处理。对于图像中的眼睛检测,首先在整个图像上进行人脸检测。当得到一个人脸时,我们单独选择人脸区域并在其中搜索眼睛,而不是搜索整个图像。它提高了准确性(因为眼睛总是在脸上)和性能(因为我们在一个小区域内搜索)。

使用Numpy索引再次获得ROI。这里我选择了球,并将其复制到图像的另一个区域。

>> ball = img[280:340, 330:390] 
>> img[273:333, 100:160] = ball

看看下面的结果:

Image Name

分割和合并图像通道

有时你需要分别处理图像的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索引。

为图像制作边框(填充)

如果你想在图像周围创建一个边框,类似于一个相框,你可以使用cv.copyMakeBorder()。但它在卷积操作、零填充等方面有更多应用。这个函数需要以下参数。

  • src - 输入图像
  • top, bottom, left, right - 相应方向的边框宽度,以像素数计
  • borderType - 定义要添加的边界类型的标志。它可以是以下类型: cv.BORDER_CONSTANT - 添加一个恒定颜色的边框。该值应作为下一个参数给出; cv.BORDER_REFLECT - 边框将是边框元素的镜像反射,像这样:Fedcba|abcdefgh|hgfedcb; cv.BORDER_REFLECT_101 或 cv.BORDER_DEFAULT - 与上述相同,但有轻微变化,像这样:gfedcb|abcdefgh|gfedcba; cv.BORDER_REPLICATE - 最后一个元素被整体复制,像这样:aaaaa|abcdefgh|hhhhh; cv.BORDER_WRAP - 无法解释,看起来会像这样:cdefgh|abcdefgh|abcdefg;
  • value - 如果边框类型是cv.BORDER_CONSTANT,它指的是边框的颜色。

下面是一个演示所有这些边框类型的示例代码,以便更好地理解:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
BLUE = [255,0,0]
img1 = cv.imread('opencv-logo.png')
replicate = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_WRAP)
constant= cv.copyMakeBorder(img1,10,10,10,10,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()

请看下面的结果。(图片是用matplotlib显示的。所以红色和蓝色通道将被替换)。

Image Name

2.2 图像上的算术操作

翻译及二次校对:cvtutorials.com

目标

  • 学习图像上的几种算术运算,如加法、减法、位运算等。
  • 学习这些函数:cv.add(), cv.addWeighted(), 等等。

图像加法

你可以用OpenCV函数cv.add()将两幅图像相加,或者简单地用numpy操作res = img1 + img2。两幅图像应该是相同的深度和类型,或者第二幅图像可以只是一个标量值。

注意:OpenCV的加法和Numpy的加法是有区别的。OpenCV加法是一个饱和操作,而Numpy加法是一个模数操作。

例如,考虑下面的例子:

>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print( cv.add(x,y) ) # 250+10 = 260 => 255
[[255]]
>>> print( x+y )          # 250+10 = 260 % 256 = 4
[4]

当你添加两张图片时,这将更加明显。请使用OpenCV函数,因为它们会提供一个更好的结果。

图像混合

这也是图像添加,但对图像给予不同的权重,以便给人以混合或透明的感觉。图像的添加是按照下面的公式进行的:
g ( x ) = ( 1 − α ) f 0 ( x ) + α f 1 ( x ) g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x) g(x)=(1α)f0(x)+αf1(x)

通过改变α从0→1,你可以在一个图像和另一个图像之间进行很酷的过渡。

这里我取了两张图片来混合。第一张图片的权重为0.7,第二张图片的权重为0.3。cv.addWeighted()对图片应用了以下公式。
d s t = α ⋅ i m g 1 + β ⋅ i m g 2 + γ dst = \alpha \cdot img1 + \beta \cdot img2 + \gamma dst=αimg1+βimg2+γ

这里的γ取0。

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()

请看下面的结果:

Image Name

位操作

这包括按位的AND、OR、NOT和XOR操作。它们在提取图像的任何部分(正如我们将在接下来的章节中看到的那样)、定义和处理非矩形的ROI等方面将非常有用。下面我们将看到一个如何改变图像中某一区域的例子。

我想把OpenCV的标志放在一张图片上面。如果我将两张图片相加,它将改变颜色。如果我把它们混合起来,我就会得到一个透明的效果。但我希望它是不透明的。如果它是一个矩形区域,我可以像我们在上一章做的那样使用ROI。但是OpenCV的标志不是一个矩形的形状。所以你可以用位操作来做,如下图所示。

# Load two images
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.png')
# I want to put logo on top-left corner, So I create a ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols]
# Now create a mask of logo and create its inverse mask also
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
# Now black-out the area of logo in ROI
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
# Take only region of logo from logo image.
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# Put logo in ROI and modify the main image
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()

请看下面的结果。左图是我们创建的遮罩。右图是最终的结果。为了更好地理解,显示上述代码中的所有中间图像,特别是img1_bg和img2_fg。

Image Name

练习

使用cv.addWeighted函数创建一个文件夹中图片的幻灯片,并在图片之间平滑过渡。

2.3 性能测量和改进技术

翻译及二次校对:cvtutorials.com

目标

在图像处理中,由于你要每秒处理大量操作,你的代码不仅要提供正确的解决方案,而且要以最快的方式提供,这是必须的。因此,在本章中,你将学习:

  • 测试代码的性能。
  • 一些提高代码性能的技巧。
  • 你会看到这些函数:cv.getTickCount, cv.getTickFrequency,等等。

除了OpenCV之外,Python还提供了一个模块time,这对测量执行时间很有帮助。另一个模块profile有助于获得代码的详细报告,比如代码中每个函数花了多少时间,函数被调用了多少次,等等。但是,如果你使用的是IPython,所有这些功能都以一种用户友好的方式整合在一起。我们将看到一些重要的功能,更多的细节,请查看附加资源部分的链接。

用OpenCV测量性能

cv.getTickCount函数返回一个参考事件(比如机器被打开的那一刻)到这个函数被调用的那一刻之后的时钟周期的数量。因此,如果你在函数执行之前和之后调用它,你可以得到执行一个函数所使用的时钟周期数。

cv.getTickFrequency函数返回时钟周期的频率,或每秒的时钟周期数。所以要找到以秒为单位的执行时间,你可以做以下工作。

e1 = cv.getTickCount()
# your code execution
e2 = cv.getTickCount()
time = (e2 - e1)/ cv.getTickFrequency()

我们将用下面的例子来证明。下面的例子应用中值滤波,其内核大小从5到49不等。 不要担心结果会是什么样子–那不是我们的目标:

img1 = cv.imread('messi5.jpg')
e1 = cv.getTickCount()
for i in range(5,49,2):
    img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1)/cv.getTickFrequency()
print( t )
# Result I got is 0.521107655 seconds

你可以用时间模块做同样的事情。不使用cv.getTickCount,而使用time.time()函数。然后取这两个时间的差值。

OpenCV中的默认优化

OpenCV的许多函数都使用SSE2,AVX等进行了优化。它也包含未经优化的代码。因此,如果我们的系统支持这些功能,我们应该利用它们(几乎所有的现代处理器都支持它们)。在编译的时候,它是默认启用的。所以,如果OpenCV启用了优化代码,它就会运行优化的代码,否则就会运行未优化的代码。你可以使用cv.useOptimized()来检查它是否被启用/禁用,cv.setUseOptimized()来启用/禁用它。让我们看一个简单的例子。

In [10]: x = 5
In [11]: %timeit y=x**2
10000000 loops, best of 3: 73 ns per loop
In [12]: %timeit y=x*x
10000000 loops, best of 3: 58.3 ns per loop
In [15]: z = np.uint8([5])
In [17]: %timeit y=z*z
1000000 loops, best of 3: 1.25 us per loop
In [19]: %timeit y=np.square(z)
1000000 loops, best of 3: 1.16 us per loop

你可以看到,x = 5 ; y = x*x是最快的,与Numpy相比,它大约快20倍。如果你也考虑到数组的创建,它可能达到100倍的速度。(Numpy的开发者们正在解决这个问题)。

注意:Python的标量操作要比Numpy的标量操作快。所以对于包括一个或两个元素的操作,Python标量比Numpy数组更好。当数组的大小稍微大一点时,Numpy有优势。

我们将再试一个例子。这一次,我们将比较cv.countNonZero()和np.count_nonzero()对同一图像的性能:

In [35]: %timeit z = cv.countNonZero(img)
100000 loops, best of 3: 15.8 us per loop
In [36]: %timeit z = np.count_nonzero(img)
1000 loops, best of 3: 370 us per loop

看,OpenCV函数比Numpy函数快了近25倍。

注意:通常情况下,OpenCV函数比Numpy函数快。所以对于同样的操作,OpenCV函数是首选。但是,也可能有例外,特别是当Numpy使用视图而不是拷贝时。

更多的IPython魔法命令

还有其他一些神奇的命令来测量性能、剖析、行剖析、内存测量等等。它们都有很好的文档。所以这里只提供这些文档的链接。建议有兴趣的读者可以尝试一下。

性能优化技术

有几种技术和编码方法可以发挥Python和Numpy的最大性能。这里只指出了相关的技术和方法,并给出了重要来源的链接。这里需要注意的是,首先尝试以一种简单的方式实现算法。一旦它开始工作,对它进行剖析,找到瓶颈,并对其进行优化。

1.尽可能避免在Python中使用循环,特别是双倍/三倍循环等。它们本身就很慢。
2.尽可能地将算法/代码矢量化,因为Numpy和OpenCV是为矢量操作而优化的。
3.利用高速缓存的一致性。
4.除非有必要,否则不要对数组进行复制。尽量使用视图来代替。阵列的复制是一个昂贵的操作。

如果你的代码在做完所有这些操作后仍然很慢,或者不可避免地要使用大的循环,请使用额外的库,如Cython,使其更快。

额外的资源

  • Python优化技术
  • Scipy讲义–高级Numpy
  • IPython中的计时和剖析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/452058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[HBZ分享] 小米手机如何解BL锁

第一步: 进入【设置—>我的设备–>全部参数–>连续疯狂的点MIUI版本那一行】 第二步:进入【更多设置–>开发者模式】,打开USB调试 与 USB安装 第三步:进入【更多设置–>开发者模式】,进入【设别解锁状…

spring eurake中使用IP注册

在开发spring cloud的时候遇到一个很奇葩的问题,就是服务向spring eureka中注册实例的时候使用的是机器名,然后出现localhost、xxx.xx等这样的内容,如下图: eureka.instance.perferIpAddresstrue 我不知道这朋友用的什么spring c…

单片机设计_自动追光系统、光源跟踪系统(AT89C51 光敏电阻 步进电机)

想要更多项目私wo!!! 一、电路设计 51 单片机双轴自动追光系统主要由 STC89C52RC 5516 光敏电阻 ADC0832 ULN2803 步进电机 LCD1602 显示屏组成。 1.通过子电路板的上、下、左、右四个光敏电阻来感受四个方向的光强,自 动寻找光强最强的方向。四个光敏电阻的…

MySQL存储引擎InnoDB、MyISAM和MEMORY介绍详解和区别

文章目录 MySQL存储引擎InnoDB、MyISAM和MEMORY介绍详解和区别InnoDB存储引擎特点操作示例创建InnoDB表修改表引擎为InnoDB MyISAM存储引擎特点操作示例创建MyISAM表修改表引擎为MyISAM MEMORY存储引擎特点操作示例创建MEMORY表修改表引擎为MEMORY 总结 MySQL存储引擎InnoDB、M…

代码随想录算法训练营第六天|242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和

先了解一下什么是哈希表 哈希表是根据关键码的值而直接进行访问的数据结构 所以数组就是哈希表 盗个卡哥的图 哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,如下图所示: 哈希表能解决的问题: 一般哈…

【LeetCode】987.二叉树的垂序遍历

二叉树遍历系列 144.二叉树的前序遍历94.二叉树的中序遍历145.二叉树的后续遍历102.二叉树的层序遍历107.二叉树的层序遍历II103.二叉树的锯齿形层序遍历(之字形遍历)199.二叉树的右视图 二叉树的垂序遍历目录 二叉树遍历系列1.问题示例 1示例 2示例 3 …

校招又临近了,怎么在面试中应对设计模式相关问题呢?

夏天开始了,那么夏天结束时的毕业季也不远了。毕业是个伤感、期待而又略带残酷的时节,就像蜜桃无论成熟与否都会在这个时间被采摘,如果毫无准备就踏入社会,就会……马上变成低级社畜。所以说还是要早点为了毕业找工作做点准备&…

Stereo-Detection:YOLO v5与双目测距结合,实现目标的识别和定位测距

简介:Stereo-Detection 是一个传统的SGBM深度测距yolov5目标检测,并部署在Jeston nano的开源教程。它致力于让更多的大四学生毕业,以及让研一学生入门 开源链接:yzfzzz/Stereo-Detection: Conventional SGBM depth ranging yolov…

【android专题】学习android,第一天学习:软件和组件了解

开发软件和手机建立连接 1.建立连接 2.运行App程序到手机,通过USB线 选择你的小米设备 run按钮 下面这个,就是你设备的日志 通过USB安装时,报错,大概意思就是,默认手机是禁止通过usb安装软件的, 要打开…

Automa自动化爬取图片(二)

Automa插件可以扩展Automa的功能,使其可以与其他应用程序进行交互。例如,Automa插件可以用于自动化测试Web应用程序,批量发送邮件,自动化填写表单等。通过Automa插件,我们可以更加灵活地定制自己的自动化测试工具&…

Linux 配置YUM源(FTP方式获取软件源、使用阿里云yum源、同时使用本地源与在线源)YUM获取安装包并生成YUM软件仓库

YUM介绍 YUM(yellow dog updater modified) 基于RPM包构建的软件更新机制 自动解决依赖关系 yum软件仓库集中管理软件包 RPM软件包的来源 centos发布的RPM包集合第三方组织发布的RPM包集合用户自定义的RPM包集合 软件仓库的提供方式 FTP服务:…

阿里4年测试经验分享 —— 测试外包干了3年后,我废了...

去年国庆,我分享了一次一位阿里朋友的技术生涯,大家反响爆蓬,感觉十分有意思,今天我来分享一下我另一位朋友的真实经历,是不是很想听? 没错,我这位朋友是曾经外包公司的测试开发,而…

SAPJNet:小样本多序列MRI诊断的序列自适应原型联合网络

文章目录 SAPJNet: Sequence-Adaptive Prototype-Joint Network for Small Sample Multi-sequence MRI Diagnosis摘要方法Sequence-Adaptive Transformer原型优化策略 实验结果 SAPJNet: Sequence-Adaptive Prototype-Joint Network for Small Sample Multi-sequence MRI Diagn…

【2023-4-8 美团春招笔试题 开发岗(技术综合-后端数开软开)】

题目一&#xff1a; 代码一&#xff1a; #include <iostream> #include <string> using namespace std;int main() {int n,m,a;cin>>n>>m>>a;string s[n][m];for(int i0;i<n;i){for(int j0;j<m;j){cin>>s[i][j];}}int count0;for(i…

【MySQL】(7)复合查询

文章目录 单表查询回顾与练习多表查询自连接多行子查询&#xff08;单列&#xff09;in 运算符all 关键字any 关键字 多列子查询from 子句中的子查询合并查询 单表查询回顾与练习 注&#xff1a;下面的依旧基于 scott 数据库 MariaDB [scott]> select * from emp; -------…

【历史上的今天】4 月 23 日:YouTube 上传第一个视频;数字音频播放器的发明者出生

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 4 月 23 日&#xff0c;世界读书日。在 1564 年的这一天&#xff0c;全世界最卓越的文学家之一莎士比亚出生&#xff1b;1616 年的这一天&#xff0c;莎士比亚…

ubuntu22.04普通用户配置cuda

ubuntu22.04普通用户配置cuda 1. 问题描述2. 解决方法2.1 查看安装cuda版本2.2 修改普通用户自己的环境变量2.3 重新执行初始化文档2.4 查看nvcc版本&#xff0c;测试是否成功 1. 问题描述 在ubuntu22.04服务器上使用root用户安装了cuda&#xff0c;普通用户登录时仍然没办法直…

中国社科院与美国杜兰大学金融管理硕士项目——你永远可以,成为想要的自己

有人说过&#xff0c;世界上最好的保鲜就是不断进步&#xff0c;每一次改变都是新生的开始&#xff0c;让自己成为更好的更值得爱的人。你要相信&#xff0c;不论任何时候&#xff0c;你都可以成为想要的自己。就像我们在职攻读硕士学位&#xff0c;经过在社科院与杜兰大学金融…

聚观早报|周鸿祎360员工不会被GPT淘汰;蚂蚁集团再捐1亿种树治沙

今日要闻&#xff1a;周鸿祎称360员工不会被GPT淘汰&#xff1b;特斯拉ModelS/X美国售价全系上涨&#xff1b;蚂蚁集团再捐1亿支持种树治沙&#xff1b;复旦大学MOSS大模型正式开源&#xff1b;电影《灌篮高手》票房突破2亿元 周鸿祎称360员工不会被GPT淘汰 4 月 21 日下午&am…

链表中的递归算法C语言带你看看

25. K 个一组翻转链表 难度困难1998收藏分享切换为英文接收动态反馈 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么…