入门OpenCV(Python版)(上)

欢迎关注 “小白玩转Python”,发现更多 “有趣”

引言

在本系列文章中,我将使用 Python 的 OpenCV 库展示一些计算机视觉初学者级别问题的解决方案。

OpenCV 库是最著名的开源计算机视觉库,可以用于许多编程语言中。利用它,我们可以对数字图像进行几何变换、滤波、摄像机标定、特征提取、目标检测等修改。

像素处理

对于第一个问题,作为“ hello world”类型的问题,我们将访问一个图像并操作其像素。

首先导入库。

import cv2

要加载图像,我们使用 imread 函数作为参数传递要读取的文件的名称以及我们希望如何读取它: 0-Black and White,1-Color,-1-changed。对于这个问题,我们只能解读为黑白和彩色。为了显示与读取类似的图像,我们调用一个函数作为参数传递要显示的窗口的名称及其内容,通常是通过调用 imread 创建的图像对象。

因此,让我们读取一个黑白图像,访问它的像素,改变一些,并显示结果。图像加载为一个矩阵(每个元素一个像素) ,如果灰度矩阵有两个维度,每个元素代表灰度的强度。如果是彩色的,则是一组三个矩阵表示的颜色通道 RGB。

image = cv2.imread("lenna.png", 0)


for i in range(200, 210):
  for j in range(10, 200):
      image[i][j] = 0
      
cv2.imshow("image", image)

重要提示: 在 OpenCV 中,矩阵的坐标是不同的。水平坐标为 y,垂直坐标为 x。此外,左上角是(0,0)原点的位置。所以坐标从左上角向右下方向增长。为了更容易理解,当使用称为 x 和 y 的变量在坐标上循环时,我们只会在访问位置时更改 OpenCV 符号。

读取之后,我们可以做一些简单的矩阵访问和改变像素颜色我们想要的。在修改了一些之后,我们用一种方法制作了一个矩形,把结果显示为黑色。

这里需要一个非常有用的命令,这样我们就可以使用相同的代码对图像进行多个操作。waitKey 等待用户按下一个键继续执行代码(这样我们就可以看到平静地显示的结果)。参数0等待要按的任何键在代码上继续。经过这个处理之后,我们再次读取 Lenna 的图像,但是这一次着色并绘制一个红色矩形,就像我们之前所做的那样。

cv2.waitKey(0)


image = cv2.imread("lenna.png", 1)


for i in range (200,210):
    for j in range(10,200):
        image[i, j] = [0, 0, 255]


cv2.imshow("janela", image)
cv2.waitKey(0) 
cv2.destroyAllWindows()

对于有颜色的图像,像素访问的方式是不同的,我们传递给像素的数组以 BGR 顺序表示颜色通道(由于某种“历史”原因被翻转过来)。作为输出,我们有一个最大的亮红色矩形,创建在同一点。

最后,为了破坏为显示图像而创建的窗口,我们调用了 destroyAllWindows 函数,因此在程序停止运行后什么也不显示。

现在,让我们用黑白图像的底片做一个矩形。矩形的位置由用户表示,负面效果是从像素的最大可能值减去像素(这里是8 bit:255)。

import cv2, sys


image = cv2.imread("lenna.png", 0)


for i in range(int(sys.argv[1]),int(sys.argv[2])):
    for j in range(int(sys.argv[3]),int(sys.argv[4])):
        image[i][j] = 255 - image[i][j] 


cv2.imshow("janela", image)    
cv2.waitKey(0)
cv2.destroyAllWindows()

坐标100,200,30,150

进一步探索像素操作,让我们重新排列图像的四个象限。四分之一象限表示正方形格式的图像的1/4,就好像图像是在垂直和水平方向上被切成两半。

对于这个挑战,我们将使用 Numpy 库来简化使用矩阵的操作。Numpy 函数将矩阵分割为给定位置和给定方向。在分割图像之后,我们必须将这些部分以翻转的顺序连接起来。

import cv2
import numpy as np


image = cv2.imread("lenna.png", 0)


height, width = image.shape
#position to be splitted in half of the vertical and 0 for horizontal
[h,w] = np.split(image, [int(height/2)], 0)
#concatenate flipped in horizontal 
image = np.concatenate((w,h))
#position to be splitted in half of the hoizontal and 1 for vertical 
[h,w] = np.split(image,[int(width/2)], 1)
#concatenate flipped in vertical 
image = np.concatenate((w,h),1)


cv2.imshow("image",image)
cv2.waitKey(0)
cv2.destroyAllWindows()

填充区域

在计算机视觉中,一个非常常见的任务是计算被检测场景中的目标。感知对象是检测属于每个对象的像素聚集的必要条件。为了更好地处理这个问题,我们将使用一个二进制图片(只有灰度像素0和255)意味着0黑色背景和1个对象像素。

这里我们假设每个白色像素的聚合都是一个单独的对象。所以,一个可能的做法是贴标签。通常标记算法以二值图像作为输入,并返回多尺度灰度图像。为此,我们将使用一种内置的算法来填充被称为“洪水填充”的区域。该算法实质上是以种子像素作为参考,搜索具有相同颜色的邻居。如果我们给出一个颜色作为参数的函数使所有的像素搜索找到的颜色。

Opencv 中的 floodFill 请求获得图像、掩码(not used = None)、种子像素以及为种子和类似邻居着色的颜色。有了这个,我们只需要逐个像素检查它的颜色,如果是白色,我们改变一些灰色,然后增加灰色,这样下一个对象将有一个不同的灰度。随着这个过程的进行,我们可以计算 floodFill 应用了多少次,并假设这是场景中对象的数量。

import cv2


image = cv2.imread("bolhas.png",0)
height, width = image.shape
cv2.imshow("image", image)
cv2.waitKey(0)


nelem = 0
for x in range(height):
    for y in range(width):
        if image[x,y] == 255:
            nelem += 1
            cv2.floodFill(image, None, (y,x), nelem)


print("Number of elements: ", nelem)


cv2.imshow("image", image)
cv2.waitKey(0)

结果,我们看不到一些物体,因为使用的灰色缩放是同样的变量用来计数对象的数量,所以使它成为一个非常深灰色。

由于我们使用灰度来计算对象的数量,因此在一个场景中进行8位处理时,我们会受到场景中255个对象数量的限制。您可以想出不同的方法来解决这个问题,我建议创建另一个变量,当元素数量达到256时(从0开始)跟踪这个变量。当这种情况发生时,这个变量将递增,nelem 将为零,以便重新开始。在标签的最后,我们会得到同样灰色的物体,但是与 nelem 一起跟踪的变量可以计算出超过极限后检测到的物体的数量。

现在再次看到原始的二进制图片,我们可以认识到有两种不同类型的对象,有一个洞和没有它。我们怎么能把它们分开计算呢?

首先,我们不知道那些被边界切开的是否有洞,所以我们忽略它们(以某种方式)。有了场景中的所有对象,解决这个问题的一个方法是在图像的背景中应用 floodFill 为 white 255(如果场景中有超过255个对象,我们必须编辑代码来保护最大白色不被使用) ,所以现在我们有了对象灰色缩放,背景白色和黑洞(旧背景) ,因为洪水填充背景白色不会击中洞。

for x in range(height):
    if image[x,0] != 0:
        cv2.floodFill(image, None, (0, x),0)
    if image[x, width-1] != 0:
        cv2.floodFill(image, None, (width-1, x),0)


for y in range(width):
    if image[0,y] != 0:
        cv2.floodFill(image, None, (y,0),0)
    if image[height-1, y] != 0:
        cv2.floodFill(image, None, (y, height-1), 0)


cv2.imshow("image", image)
cv2.waitKey(0)

有了这个,我们就解决了这个问题,让黑洞变成黑色,就是在整个图像中搜索黑色像素,每找到一个黑色像素,我们就增加黑洞的数量,然后用洪水填充,这样我们就不会错误地得到和。对于这个例子,我们使用相同的背景颜色,所以我们有洞的感觉。

cv2.floodFill(image, None, (0,0), 255)


nHoles = 0
for x in range(height):
    for y in range(width):
        if image[x,y] == 0:
            cv2.floodFill(image, None, (y,x), 255)
            nHoles += 1


print("Number of holes: ", nHoles)


cv2.imshow("image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

·  END  ·

HAPPY LIFE

觉得有趣就点亮在看吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值