初始化畫布¶
概要¶
阿凱在這篇文章中給大家講解了如何分別用numpy的方法, 與numpy與cv2結合的方法創建空白畫布, 創建白色畫布, 與創建彩色畫布. 本節實際上同時也幫大家回顧了numpy
的一些常用的功能.
本節阿凱還介紹了cv2進行通道分割cv2.split
與通道合并cv2.merge
的兩個函數.
keywords OpenCV 初始化畫布 通道分離 通道合并
1. 創建一個空白畫布¶
創建空白畫布的函數,成品如下:
def InitCanvas(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") canvas[:] = color return canvas
調用的時候傳入圖像的寬度與高度,還有畫布的顏色即可. 例如創建一個800*600 顏色為純黑色的畫布:
canvas = InitCanvas(800, 600, color=(255,255,255))

完整源碼
src/init_canvas_v1.py
''' 初始化畫布 ''' import cv2 import numpy as np def init_canvas(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") canvas[:] = color return canvas canvas = init_canvas(200, 200, color=(125, 40, 255)) cv2.imshow('canvas', canvas) cv2.waitKey(0) cv2.destroyAllWindows()
如果你不想深究numpy的ndarray數據結構的索引與賦值的話,下面的內容不需要閱讀.
學霸的神奇的分割線
我們在使用畫圖工具的時候, 第一件事情就是創建一個新的空白畫布, 我們可以指定畫布的大小, 跟顏色.
那我們如何使用opencv來創建一個空白的畫布(值相同的圖片) ?
還記得我們在講解image的數據結構的時候, 提到過python-opencv
上的圖片,本質上就是numpy
里面的ndarray
的對象嗎? 創建一個畫布本質上就是創建一個同等規格的ndarray
創建一個新的特定尺寸的ndarray
我們可以使用np.zeors
函數, 我們將圖像的高度(height), 圖像的寬度(width), 以及圖像的通道數channel
以tuple
類型傳入np.zeros
. 再次聲明是tuple類型
另外由于不是所有的numpy類型的數值, 都可以放到opencv中進行圖像處理.
數值取值范圍在0-255, 我們需要指定數據類型為uint8
unsigned integer 8-bit
np.zeros((height, width, channels), dtype="uint8")
所有的像素點的值, 都是0, 那么其實也就是純純的黑色. 我們新建了一個黑色背景的畫布.
舉個例子, 我們想創建一個800 x 600 x 3 的圖片, 我們就得這么寫:
一個BGR格式的圖像,
# 初始化一個空畫布 300×300 三通道 背景色為黑色 canvas_black = np.zeros((600, 800, 3), dtype="uint8")
注意 height寫在前面 圖像數據結構, 記不牢靠的同學, 請復習教程:
有時候我們也可以偷懶, 如果我們想創建與另外一個圖像尺寸相同的畫布的時候, 我們可以使用np.zeros_like
canvas_black = np.zeros_like(img)
2. 初始化白色的畫布¶
emmm, 老是黑色的背景, 總給人一種很壓抑的感覺.
那么有沒有一種好的方法, 可以讓我自定義顏色 ?
首先我想到的是白色, 又因為比較簡單, 三個通道的值都相同.
ps: 其實灰色的圖片(GRAY2BGR), 三個通道的值都相同.
那么我們創建一個全都是1的矩陣, 然后,乘上某個數值, 問題是不是就解決了.
我們需要用到np.ones
函數
# 初始化一個空畫布 300×300 三通道 背景色為白色 canvas_white = np.ones((300, 300, 3), dtype="uint8")
接下來, 需要乘上一個整數, 255
(你可以填入0-255的任意值)
canvas_white *= 255
還記得阿凱在numpy
教程里面,稱這種運算為什么運算么?
這種運算稱之為 全局乘法 , 忘記的同學, 趕緊復習一下全局函數 ufunc 的概念.
3. 初始化彩色的畫布¶
3.1 利用cv2的內置方法merge與split¶
初始化畫布有兩種方法. 一種方法, 是我們初始化BGR的圖片canvas_white
之后將原來的圖片進行通道分離. 分別乘上BGR三個通道的整數值, 然后將三個通道合并在一起. 就得到我們想要的彩圖純色背景.
那通道的分離我們需要用到的函數是cv2.split(img)
.
# 將原來的三個通道抽離出來, 分別乘上各個通道的值 (channel_b, channel_g, channel_r) = cv2.split(canvas)
channel_b
藍色通道, channel_g
綠色通道, channel_r
紅色通道, 都是二維的ndarray對象.
我們指定一種顏色, 例如 color = (100, 20, 50))
注意, 我們這里的顏色指的BGR格式
也就是
B -> 100 G -> 20 R -> 50
接下來我們分別將其乘上對應的值.
# 顏色的值與個通道的全1矩陣相乘 channel_b *= color[0] channel_g *= color[1] channel_r *= color[2]
接下來我們將三個通道重新合并, 需要用到的函數是cv2.merge
cv2.merge([channel_b, channel_g, channel_r])
注意,三個通道的矩陣, 以list []
的方式, 傳入merge函數.
綜合起來, 就是我們的第一個初始化彩色背景的函數:
src/init_canvas_v2.py
''' 初始化畫布 ''' import cv2 import numpy as np # 初始化一個彩色的畫布 - cv2版本 def init_canvas(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") # 將原來的三個通道抽離出來, 分別乘上各個通道的值 (channel_b, channel_g, channel_r) = cv2.split(canvas) # 顏色的值與個通道的全1矩陣相乘 channel_b *= color[0] channel_g *= color[1] channel_r *= color[2] # cv.merge 合并三個通道的值 return cv2.merge([channel_b, channel_g, channel_r]) canvas = init_canvas(200, 200, color=(125, 40, 255)) cv2.imshow('canvas', canvas) cv2.waitKey(0) cv2.destroyAllWindows()
此函數使用 cv2.split 非常耗時 所以只有在需要的時候才能做到。 否則用Numpy索引。
3.2 利用numpy內置的索引¶
使用numpy原生的方法, 性能會比opencv中的要好. 我們直接使用numpy的ndarray
的索引的方法.
例如 canvas[:,:,0]
選中的是, 所有行, 所有列, 像素元素的第一個值, 也就是, 所有B通道的值.
然后對其進行賦值:
canvas[:,:,0] = color[0]
對numpy索引方式, 忘掉的朋友, 回顧一下教程吧.
在教程中搜索 切片 slicing , 兩者其實是一個東西.
完整版本的函數如下:
src/init_canvas_v3.py
''' 初始化畫布 ''' import cv2 import numpy as np def init_canvas(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") # Blue canvas[:,:,0] = color[0] # Green canvas[:,:,1] = color[1] # Red canvas[:,:,2] = color[2] return canvas canvas = init_canvas(200, 200, color=(125, 40, 255)) cv2.imshow('canvas', canvas) cv2.waitKey(0) cv2.destroyAllWindows()
實際上我們還有更快的方法, 可以實現這個功能, 這就需要你熟練掌握Numpy的使用技巧.
我們可以直接賦值color
canvas[:] = color
完整的函數如下, 這是我們的第三個版本.
src/init_canvas_v4.py
''' 初始化畫布 ''' import cv2 import numpy as np def init_canvas(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") canvas[:] = color return canvas canvas = init_canvas(200, 200, color=(125, 40, 255)) cv2.imshow('canvas', canvas) cv2.waitKey(0) cv2.destroyAllWindows()
4. 綜合實驗-初始化背景¶
在這個綜合實驗里, 阿凱會分別用上述的方法, 創建黑色背景, 白色背景, 彩色背景.
''' 初始化一個空白的畫布 并指定畫布的顏色 ''' import cv2 import numpy as np # 初始化一個空畫布 300×300 三通道 背景色為黑色 canvas_black = np.zeros((300, 300, 3), dtype="uint8") cv2.imshow("canvas_black", canvas_black) # 初始化一個空畫布 300×300 三通道 背景色為白色 canvas_white = np.ones((300, 300, 3), dtype="uint8") canvas_white *= 255 cv2.imshow("canvas_white", canvas_white) ''' 初始化一個彩色的畫布 - cv2版本 此函數使用 cv2.split 非常耗時 所以只有在需要的時候才能做到。 否則用Numpy索引。 ''' def InitCanvasV1(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") # 將原來的三個通道抽離出來, 分別乘上各個通道的值 (channel_b, channel_g, channel_r) = cv2.split(canvas) # 顏色的值與個通道的全1矩陣相乘 channel_b *= color[0] channel_g *= color[1] channel_r *= color[2] # cv.merge 合并三個通道的值 return cv2.merge([channel_b, channel_g, channel_r]) ''' 初始化一個彩色的畫布 - numpy版本 使用numpy的索引 賦值 ''' def InitCanvasV2(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") # Blue canvas[:,:,0] = color[0] # Green canvas[:,:,1] = color[1] # Red canvas[:,:,2] = color[2] return canvas ''' 初始化終極版本 熟練掌握 numpy 才可以提高工作效率哦 ''' def InitCanvasV3(width, height, color=(255, 255, 255)): canvas = np.ones((height, width, 3), dtype="uint8") canvas[:] = color return canvas # 初始化一個彩色的畫布 canvas_color = InitCanvasV2(300, 300, color=(100, 20, 50)) cv2.imshow("canvas_color", canvas_color) # 等待e鍵按下 關閉所有窗口 while cv2.waitKey(0) != ord('e'): continue cv2.destroyAllWindows()
5. 作業-CH2.1¶
阿凱這里教大家如何創建一個三通道的畫布, 那么灰度圖的畫布你會創建么?
阿凱給你提供一個代碼模板, 請填入關鍵的函數.
import numpy as np import cv2 # 初始化灰度圖的畫布 def createGrayscaleCanvas(width, height, color=255): ''' 請在這里填入代碼 ''' return canvas # 創建一個顏色為125的灰度圖 canvas = createGrayscaleCanvas(500, 500, color=125) # 展示畫布 cv2.imshow("canvas", canvas) # 中斷 cv2.waitKey(0) # 關閉窗口 cv2.destroyAllWindows()