OpenCV_007-OpenCV 中的图像基本操作

本文主要内容来自于 OpenCV-Python 教程核心操作 部分,这个部分的主要内容如下:

目标

学习:

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

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

访问及修改像素值

让我们先加载一幅彩色图像:

#!/use/bin/env python

import numpy as np
import cv2 as cv

if __name__ == "__main__":
    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    filepath = cv.samples.findFile("messi5.jpg")
    img = cv.imread(filepath)

我们可以根据像素点的行和列坐标访问像素的值。对于 BGR 图像,它返回一个蓝色、绿色和红色值的数组。对于灰度图像,只返回相应的亮度。

    px = img[100, 100]
    print(px)
    print(px.__class__.__name__)

    blue = img[100, 100, 0]
    print(blue)

这几行代码对应的输出如下:

[157 166 200]
ndarray
157

彩色图像像素点的值是由一个 Numpy 的 ndarray 表示的。我们还可以以相同的方式修改像素值。

    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
    red_value = img.item(10, 10, 2)
    print(red_value)

    # modifying RED value
    img.itemset((10, 10, 2), 100)

    red_value = img.item(10, 10, 2)
    print(red_value)

这几行代码对应的输出如下:

59
100

访问图像的属性

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

一幅图像的形状可以通过 img.shape 访问。它返回行数、列数和通道数的元组(如果图像是彩色的):

    print(img.shape)

这行代码对应的输出如下:

(342, 548, 3)

注意
如果图像是灰度图,则返回的元组只包含行数和列数,因而这是一种检查加载的图像是灰度图还是彩色图的好方法。

像素值的总个数通过 img.size 访问,它是行数、列数和通道数三者的乘积,而不是行数和列数两者的乘积:

    print(img.size)
    totoal_pixels = img.shape[0] * img.shape[1] * img.shape[2]
    print(totoal_pixels)

这几行代码对应的输出如下:

562248
562248

图像的数据类型通过 img.dtype 获取:

    print(img.dtype)

这行代码对应的输出如下:

uint8

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

图像 ROI

有时,我们将不得不使用某些图像区域。对于图像中的眼睛检测,首先对整个图像进行人脸检测。当获得人脸时,我们只选择人脸区域并在其中搜索眼睛,而不是搜索整个图像。它提高了精度(因为眼睛总是在脸上:D)和性能(因为我们在一个更小的区域内搜索)。

使用 Numpy 索引再次获得 ROI。这里我们选择足球,并把它拷贝到图像的另一个区域:

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

上面方括号中逗号前面的数字表示选取的区域的行的范围,即区域的垂直方向的范围,后面的数字表示选取的区域的列的范围,即区域的水平方向的范围。检查结果如下:

分割和合并图像通道

有时我们需要分别处理一幅图像的 B,G,R 通道。在这种情况下,我们需要把 BGR 图像分割为单独的通道。在其它情况下,我们可能需要合并这些单独的通道,并创建 BGR 图像。我们可以通过以下方式简单地做到这一点:

    b, g, r = cv.split(img)
    img = cv.merge((b, r, g))

这里有意没有按照原来的数据格式合并数据,而是把所有像素点的绿色通道和红色通道的值做了交换。此外,分割获得的单个色彩通道的值可以作为一幅灰度图来绘制。

或者:

    b = img[:, :, 0]

假设我们想要把所有像素的红色通道值都设置为 0 —— 我们不需要先分割通道。Numpy 的索引更快:

    img[:, :, 2] = 0

警告
cv.split() 是一项代价高昂的操作(就时间而言)。因此只在需要的时候使用它。否则使用 Numpy 的索引。

为图像制作边框(填充)

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

  • src - 输入图像

  • topbottomleftright - 相应方向上边框的以像素为单位的宽度。

  • borderType - 定义了添加何种边框的标记。它可以是以下类型:

  • value - 如果边框类型是 cv.BORDER_CONSTANT 这个是边框的颜色

下面这段代码演示了所有这些边框类型,以使我们获得更好的理解。

def border_type():
    BLUE = [255, 0, 0]

    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    filepath = cv.samples.findFile("opencv-logo.png")
    img1 = cv.imread(filepath)

    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)

    isolated = cv.copyMakeBorder(img1, 10, 10, 10, 10, cv.BORDER_ISOLATED)

    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.subplots_adjust(wspace=0.4, hspace=0.4)

    plt.show()

这里用到的示例图像文件同样在 OpenCV 的示例数据中,因而先查找这个文件的完整路径并加载。图像由 matplotlib 显示。因而 RED 和 BLUE 通道将会被交换 。用 matplotlib 画图时,为了防止不同图之间相互遮盖,这里通过 plt.subplots_adjust(wspace=0.4, hspace=0.4) 对子图做了一些调整。

来看下最终的结果:

Image

参考文档

Basic Operations on Images

Done.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容