zoukankan      html  css  js  c++  java
  • [OpenCV-Python] OpenCV 核心操作 部分 III

    部分 III
    核心操作

    OpenCV-Python 中文教程(搬运)目录

    9 图像的基础操作


    目标
      • 获取像素值并修改
      • 获取图像的属性(信息)
      • 图像的 ROI()
      • 图像通道的拆分及合并
    几乎所有这些操作与 Numpy 的关系都比与 OpenCV 的关系更加紧密,因此熟练 Numpy 可以帮助我们写出性能更好的代码。
    (示例将会在 Python 终端中展示,因为他们大部分都只有一行代码)


    9.1 获取并修改像素值
      首先我们需要读入一幅图像:

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

    你可以根据像素的行和列的坐标获取他的像素值。对 BGR 图像而言,返回值为 B,G,R 的值。对灰度图像而言,会返回他的灰度值(亮度?intensity)

    import cv2
    import numpy as np
    img=cv2.imread('messi5.jpg')
    px=img[100,100]
    print(px)
    blue=img[100,100,0]
    print(blue)

    你可以以类似的方式修改像素值。

    import cv2
    import numpy as np
    img=cv2.imread('messi5.jpg')
    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() 分割他们。

    获取像素值及修改的更好方法。

    import cv2
    import numpy as np
    img=cv2.imread('messi5.jpg')
    print(img.item(10,10,2))
    img.itemset((10,10,2),100)
    print(img.item(10,10,2))

    # 59

    # 100

    9.2 获取图像属性
      图像的属性包括:行,列,通道,图像数据类型,像素数目等img.shape 可以获取图像的形状。他的返回值是一个包含行数,列数,通道数的元组。

    import cv2
    import numpy as np
    img=cv2.imread('messi5.jpg')
    print(img.shape)

    ##(342, 548, 3)

    注意:如果图像是灰度图,返回值仅有行数和列数。所以通过检查这个返回值就可以知道加载的是灰度图还是彩色图。
    img.size 可以返回图像的像素数目:

    import cv2
    import numpy as np
    img=cv2.imread('messi5.jpg')
    print(img.size, img.dtype) # 返回的是图像的数据类型.

    # 562248 uint8

    # uint8

    注意:在debug时 img.dtype 非常重要。因为在 OpenCV Python 代码中经常出现数据类型的不一致。


    9.3 图像 ROI
    有时你需要对一幅图像的特定区域进行操作。例如我们要检测一副图像中眼睛的位置,我们首先应该在图像中找到脸,再在脸的区域中找眼睛,而不是直接在一幅图像中搜索。这样会提高程序的准确性和性能。
    ROI 也是使用 Numpy 索引来获得的。现在我们选择球的部分并把他拷贝到图像的其他区域。

    import cv2
    import numpy as np
    img=cv2.imread('messi5.jpg')
    ball=img[280:340,330:390]
    img[273:333,100:160]=ball
    img=cv2.imshow('test', img)
    cv2.waitKey(0)

    看看结果吧:


    9.4 拆分及合并图像通道
    有时我们需要对 BGR 三个通道分别进行操作。这是你就需要把 BGR 拆分成单个通道。有时你需要把独立通道的图片合并成一个 BGR 图像。你可以这样做:

    import cv2
    import numpy as np
    img=cv2.imread('/home/duan/workspace/opencv/images/roi.jpg')
    b,g,r=cv2.split(img)
    img=cv2.merge(b,g,r)

    或者:

    import cv2
    import numpy as np
    img=cv2.imread('/home/duan/workspace/opencv/images/roi.jpg')
    b=img[:,:,0]

    假如你想使所有像素的红色通道值都为 0,你不必先拆分再赋值。你可以直接使用 Numpy 索引,这会更快。

    import cv2
    import numpy as np
    img=cv2.imread('/home/duan/workspace/opencv/images/roi.jpg')
    img[:,:,2]=0

    警告:cv2.split() 是一个比较耗时的操作。只有真正需要时才用它,能用Numpy 索引就尽量用。


    9.5 为图像扩边(填充)
      如果你想在图像周围创建一个边,就像相框一样,你可以使用 cv2.copyMakeBorder()函数。这经常在卷积运算或 0 填充时被用到。这个函数包括如下参数:
      • src 输入图像
      • top, bottom, left, right 对应边界的像素数目。
      • borderType 要添加那种类型的边界,类型如下:
        – cv2.BORDER_CONSTANT 添加有颜色的常数值边界,还需要下一个参数(value)。
        – cv2.BORDER_REFLECT 边界元素的镜像。比如: fedcba|abcde-fgh|hgfedcb
        – cv2.BORDER_REFLECT_101 or cv2.BORDER_DEFAULT跟上面一样,但稍作改动。例如: gfedcb|abcdefgh|gfedcba
        – cv2.BORDER_REPLICATE 重复最后一个元素。例如: aaaaaa|abcdefgh|hhhhhhh
        – cv2.BORDER_WRAP 不知道怎么说了, 就像这样: cdefgh|abcdefgh|abcdefg
      • value 边界颜色,如果边界的类型是 cv2.BORDER_CONSTANT

    为了更好的理解这几种类型请看下面的演示程序。

    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    BLUE=[255,0,0]
    img1=cv2.imread('opencv_logo.png')
    replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE)
    reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)
    reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)
    wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)
    constant= cv2.copyMakeBorder(img1,10,10,10,10,cv2.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 绘制,所以交换 R 和 B 的位置,OpenCV 中是按 BGR,matplotlib 中是按 RGB 排列):

    10 图像上的算术运算


    目标
      • 学习图像上的算术运算,加法,减法,位运算等。
      • 我们将要学习的函数与有:cv2.add(),cv2.addWeighted() 等。


    10.1 图像加法
      你可以使用函数 cv2.add() 将两幅图像进行加法运算,当然也可以直接使用 numpy,res=img1+img。两幅图像的大小,类型必须一致,或者第二个图像可以使一个简单的标量值。
    注意:OpenCV 中的加法与 Numpy 的加法是有所不同的。OpenCV 的加法是一种饱和操作,而 Numpy 的加法是一种模操作。
    例如下面的两个例子:

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

    这种差别在你对两幅图像进行加法时会更加明显。OpenCV 的结果会更好一点。所以我们尽量使用 OpenCV 中的函数。


    10.2 图像混合
      这其实也是加法,但是不同的是两幅图像的权重不同,这就会给人一种混合或者透明的感觉。图像混合的计算公式如下:
        g (x) = (1 − α)f 0 (x) + αf 1 (x)
      通过修改 α 的值(0 → 1),可以实现非常酷的混合。
      现在我们把两幅图混合在一起。第一幅图的权重是 0.7,第二幅图的权重是 0.3。函数 cv2.addWeighted() 可以按下面的公式对图片进行混合操作。
        dst = α · img1 + β · img2 + γ
      这里 γ 的取值为 0。

    img1 = cv2.imread('ml.png')
    img2 = cv2.imread('opencv_logo.jpg')
    
    dst = cv2.addWeighted(img1,0.7,img2,0.3,0)
    
    cv2.imshow('dst',dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    dst = cv2.addWeighted(img1,0.7,img2,0.3,0)
    error: C:projectsopencv-pythonopencvmodulescoresrcarithm.cpp:659:
    error: (-209) The operation is neither 'array op array' (where arrays have
    the same size and the same number of channels), nor 'array op scalar',
    nor 'scalar op array' in function cv::arithm_op

    下面就是结果:#  这个运行有问题

    10.3 按位运算
      这里包括的按位操作有:AND,OR,NOT,XOR 等。当我们提取图像的一部分,选择非矩形 ROI 时这些操作会很有用(下一章你就会明白)。下面的例子就是教给我们如何改变一幅图的特定区域。我想把 OpenCV 的标志放到另一幅图像上。如果我使用加法,颜色会改变,如果使用混合,会得到透明效果,但是我不想要透明。如果他是矩形我可以象上一章那样使用 ROI。但是他不是矩形。但是我们可以通过下面的按位运算实现:

    import cv2
    import numpy as np
    # Load two images
    img1 = cv2.imread('messi5.jpg')
    img2 = cv2.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 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
    ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
    mask_inv = cv2.bitwise_not(mask)

    # Now black-out the area of logo in ROI
    img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

    # Take only region of logo from logo image.
    img2_fg = cv2.bitwise_and(img2,img2,mask = mask)

    # Put logo in ROI and modify the main image
    dst = cv2.add(img1_bg,img2_fg)
    img1[0:rows, 0:cols ] = dst

    cv2.imshow('res',img1)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    结果如下。左面的图像是我们创建的掩码。右边的是最终结果。为了帮助大家理解我把上面程序的中间结果也显示了出来,特别是 img1_bg 和 img2_fg。

        Otsu's Thresholding

        

    练习
    1. 创建一个幻灯片用来演示一幅图如何平滑的转换成另一幅图(使用函数cv2.addWeighted)

    11 程序性能检测及优化


    目标
    在图像处理中你每秒钟都要做大量的运算,所以你的程序不仅要能给出正确的结果,同时还必须要快。所以这节我们将要学习:
      • 检测程序的效率
      • 一些能够提高程序效率的技巧
      • 你要学到的函数有:cv2.getTickCount,cv2.getTickFrequency等
    除了 OpenCV,Python 也提供了一个叫 time 的的模块,你可以用它来测量程序的运行时间。另外一个叫做 profile 的模块会帮你得到一份关于你的程序的详细报告,其中包含了代码中每个函数运行需要的时间,以及每个函数被调用的次数。如果你正在使用 IPython 的话,所有这些特点都被以一种用户友好的方式整合在一起了。我们会学习几个重要的,要想学到更加详细的知识就打开更多资源中的链接吧。


    11.1 使用 OpenCV 检测程序效率

      cv2.getTickCount 函数返回从参考点到这个函数被执行的时钟数。所以当你在一个函数执行前后都调用它的话,你就会得到这个函数的执行时间(时钟数)。
      cv2.getTickFrequency 返回时钟频率,或者说每秒钟的时钟数。所以你可以按照下面的方式得到一个函数运行了多少秒:

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

    我们将会用下面的例子演示。下面的例子是用窗口大小不同(5,7,9)的核函数来做中值滤波:

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

    注 意: 你 也 可 以 中 time 模 块 实 现 上 面 的 功 能。 但 是 要 用 的 函 数 是time.time() 而不是 cv2.getTickCount。比较一下这两个结果的差别吧。


    11.2 OpenCV 中的默认优化

      OpenCV 中的很多函数都被优化过(使用 SSE2,AVX 等)。也包含一些没有被优化的代码。如果我们的系统支持优化的话要尽量利用只一点。在编译时优化是被默认开启的。因此 OpenCV 运行的就是优化后的代码,如果你把优化关闭的话就只能执行低效的代码了。你可以使用函数 cv2.useOptimized()来查看优化是否被开启了,使用函数 cv2.setUseOptimized() 来开启优化。
    让我们来看一个简单的例子吧。

    # check if optimization is enabled
    In [5]: cv2.useOptimized()
    Out[5]: True
    
    In [6]: %timeit res = cv2.medianBlur(img,49)
    10 loops, best of 3: 34.9 ms per loop
    
    # Disable it
    In [7]: cv2.setUseOptimized(False)
    
    In [8]: cv2.useOptimized()
    Out[8]: False
    
    In [9]: %timeit res = cv2.medianBlur(img,49)
    10 loops, best of 3: 64.1 ms per loop

    看见了吗,优化后中值滤波的速度是原来的两倍。如果你查看源代码的话,你会发现中值滤波是被 SIMD 优化的。所以你可以在代码的开始处开启优化(你要记住优化是默认开启的)。


    11.3 在 IPython 中检测程序效率

      有时你需要比较两个相似操作的效率,这时你可以使用 IPython 为你提供的魔法命令%time。他会让代码运行好几次从而得到一个准确的(运行)时间。它也可以被用来测试单行代码的。
    例如,你知道下面这同一个数学运算用哪种行式的代码会执行的更快吗?
    x = 5; y = x ∗ ∗2
    x = 5; y = x ∗ x
    x = np.uint([5]); y = x ∗ x
    y = np.squre(x)
    我们可以在 IPython 的 Shell 中使用魔法命令找到答案。

    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

    竟然是第一种写法,它居然比 Nump 快了 20 倍。如果考虑到数组构建的话,能达到 100 倍的差。
    注意:Python 的标量计算比 Nump 的标量计算要快。对于仅包含一两个元素的操作 Python 标量比 Numpy 的数组要快。但是当数组稍微大一点时Numpy 就会胜出了。
    我们来再看几个例子。我们来比较一下 cv2.countNonZero() 和
    np.count_nonzero()。

    In [35]: %timeit z = cv2.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 对视图(而非复制)进行操作时。


    11.4 更多 IPython 的魔法命令
    还有几个魔法命令可以用来检测程序的效率,profiling,line profiling,内存使用等。他们都有完善的文档。所以这里只提供了超链接。感兴趣的可以自己学习一下。


    11.5 效率优化技术
      有些技术和编程方法可以让我们最大的发挥 Python 和 Numpy 的威力。

      我们这里仅仅提一下相关的,你可以通过超链接查找更多详细信息。我们要说的最重要的一点是:首先用简单的方式实现你的算法(结果正确最重要),当结果正确后,再使用上面的提到的方法找到程序的瓶颈来优化它。
      1. 尽量避免使用循环,尤其双层三层循环,它们天生就是非常慢的。
      2. 算法中尽量使用向量操作,因为 Numpy 和 OpenCV 都对向量操作进行了优化。
      3. 利用高速缓存一致性。
      4. 没有必要的话就不要复制数组。使用视图来代替复制。数组复制是非常浪费资源的。
    就算进行了上述优化,如果你的程序还是很慢,或者说大的训话不可避免的话,你你应该尝试使用其他的包,比如说 Cython,来加速你的程序。


    更多资源
    1. Python Optimization Techniques
    2. Scipy Lecture Notes - Advanced Numpy
    3. Timing and Profiling in IPython

  • 相关阅读:
    开始Flask项目
    夜间模式的开启与关闭,父模板的制作
    从首页问答标题到问答详情页
    首页列表显示全部问答,完成问答详情页布局
    制作首页的显示列表
    发布功能完成
    登录之后更新导航
    完成登录功能,用session记住用户名
    完成注册功能
    通过用户模型,对数据库进行增删改查操作
  • 原文地址:https://www.cnblogs.com/Undo-self-blog/p/8424220.html
Copyright © 2011-2022 走看看