youcans 的 OpenCV 学习课—3.图像的创建与修改
youcans 的 OpenCV 學習課—3.圖像的創建與修改
本系列面向 Python 小白,從零開始實戰解說 OpenCV 項目實戰。
OpenCV 中圖像的數據結構是 ndarray 多維數組,對圖像的任何操作本質上都是對 ndarray 多維數組的操作和運算。
本節介紹圖像的格式和 Numpy 方法圖像處理,提供完整例程和運行結果:查看圖像屬性,像素讀取與編輯,創建空白、黑色、白色、隨機圖像,圖像復制,圖像裁剪,ROI裁剪,圖像拼接、圖像通道的拆分與合并。
歡迎關注 『youcans 的 OpenCV 學習課』 系列,持續更新
youcans 的 OpenCV 學習課—1.安裝與環境配置
youcans 的 OpenCV 學習課—2.圖像讀取與顯示
youcans 的 OpenCV 學習課—3.圖像的創建與修改
youcans 的 OpenCV 學習課—4.圖像的疊加與混合
youcans 的 OpenCV 學習課—5.圖像的幾何變換
youcans 的 OpenCV 學習課—6.灰度變換與直方圖處理
youcans 的 OpenCV 學習課—7.空間域圖像濾波
youcans 的 OpenCV 學習課—8.頻率域圖像濾波(上)
youcans 的 OpenCV 學習課—9.頻率域圖像濾波(下)
1. 圖像基本知識
1.1 圖像顏色的分類
按照顏色對圖像進行分類,可以分為二值圖像、灰度圖像和彩色圖像。
- 二值圖像:只有黑色和白色兩種顏色的圖像。每個像素點可以用 0/1 表示,0 表示黑色,1 表示白色。
- 灰度圖像:只有灰度的圖像。每個像素點用 8bit 數字 [0,255] 表示灰度,如:0 表示純黑,255 表示純白。
- 彩色圖像:彩色圖像通常采用紅色(R)、綠色(G)和藍色(B)三個色彩通道的組合表示。每個像素點可以用 3個 8bit 數字 [0,255] 分別表示紅色、綠色和藍色的顏色分量,如:(0,0,0) 表示黑色,(255,255,255) 表示白色。
彩色圖像可以采用不同的表達方式。OpenCV 使用 BGR 格式,色彩通道按照 B/G/R 的順序排列;而 matplotlib、PyQt5、Pillow 中使用 RGB 格式,色彩通道按照 R/G/B 的順序排列的。
一些彩色圖像格式還支持透明通道(alpha 通道),每個像素點用 8bit 數字 [0,255] 表示透明度,0 表示完全透明,255 表示完全不透明。
在數字圖像處理中,可以根據需要對圖像的通道順序進行轉換,或將彩色圖像轉換為灰度圖像、二值圖像。
1.2 數字圖像的表示
數字圖像是通過柵格排列的像素組成的,在計算機中以多維數據集來表示和處理。
OpenCV 的 Python API 是基于 Numpy 來存儲和處理多維數組,圖像的數據結構是 ndarray 多維數組。OpenCV 中對圖像的任何操作,本質上都是對 ndarray 多維數組的操作和運算。
OpenCV 中的二值圖像和灰度圖像用二維數組 (h, w) 表示,數組中的每個元素表示對應一個像素的灰度,每個像素的位深度為 8位。
OpenCV 中二值圖像被作為特殊的灰度圖像,每個像素點的值為 0(黑色)或 255(白色)。
OpenCV 中的彩色圖像用三維數組 (h, w, ch=3) 表示,數組中的每個元素對應一個像素的某種顏色分量,每個像素的位深度為 24位。
OpenCV 使用 BGR 格式,色彩通道順序為 B/G/R,因此 B 通道是 img[:, :, 0], G 通道是 img[:, :, 1], R 通道是 img[:, :, 2]。
1.3 數字圖像的屬性
OpenCV 中圖像對象的數據結構是 ndarray 多維數組,因此 ndarray 數組的屬性和操作方法也都適用于 OpenCV 的圖像對象。例如:
-
img.ndim:查看圖像的維數,彩色圖像的維數為 3,灰度圖像的維數為 2。
-
img.shape:查看圖像的形狀,即圖像柵格的行數(高度)、列數(寬度)、通道數。
-
img.size:查看圖像數組元素總數,灰度圖像的數組元素總數為像素數量,彩色圖像的數組元素總數為像素數量與通道數的乘積。
基本例程:
# 1.11 圖像數組的屬性imgFile = "../images/imgLena.tif" # 讀取文件的路徑img1 = cv2.imread(imgFile, flags=1) # flags=1 讀取彩色圖像(BGR)img2 = cv2.imread(imgFile, flags=0) # flags=0 讀取為灰度圖像# cv2.imshow("Demo1", img1) # 在窗口顯示圖像# key = cv2.waitKey(0) # 等待按鍵命令# 維數(ndim), 形狀(shape), 元素總數(size), 元素類型(dtype)print("Ndim of img1(BGR): {}, img2(Gray): {}".format(img1.ndim, img2.ndim)) # number of rows, columns and channelsprint("Shape of img1(BGR): {}, img2(Gray): {}".format(img1.shape, img2.shape)) # number of rows, columns and channelsprint("Size of img1(BGR): {}, img2(Gray): {}".format(img1.size, img2.size)) # size = rows * columns * channelsprint("Dtype of img1(BGR): {}, img2(Gray): {}".format(img1.dtype, img2.dtype)) # uint8本例程的運行結果如下:
Ndim of img1(BGR): 3, img2(Gray): 2 Shape of img1(BGR): (512, 512, 3), img2(Gray): (512, 512) Size of img1(BGR): 786432, img2(Gray): 262144 Dtype of img1(BGR): uint8, img2(Gray): uint8通過資源管理器查看彩色圖像和灰度圖像的屬性如下圖,彩色圖像的位深度為 24,灰度圖像的位深度為 8。
2. 像素的編輯
像素是構成數字圖像的基本單位,像素處理是圖像處理的基本操作。
對像素的訪問、修改,可以使用 Numpy 方法直接訪問數組元素。
基本例程:
# 1.13 Numpy 獲取和修改像素值img1 = cv2.imread("../images/imgLena.tif", flags=1) # flags=1 讀取彩色圖像(BGR)x, y = 10, 10 # 指定像素位置 x, y# (1) 直接訪問數組元素,獲取像素值(BGR)pxBGR = img1[x,y] # 訪問數組元素[x,y], 獲取像素 [x,y] 的值print("x={}, y={}\nimg[x,y] = {}".format(x,y,img1[x,y]))# (2) 直接訪問數組元素,獲取像素通道的值print("img[{},{},ch]:".format(x,y))for i in range(3):print(img1[x, y, i], end=' ') # i=0,1,2 對應 B,G,R 通道# (3) img.item() 訪問數組元素,獲取像素通道的值print("\nimg.item({},{},ch):".format(x,y))for i in range(3):print(img1.item(x, y, i), end=' ') # i=0,1,2 對應 B,G,R 通道# (4) 修改像素值:img.itemset() 訪問數組元素,修改像素通道的值ch, newValue = 0, 255print("\noriginal img[x,y] = {}".format(img1[x,y]))img1.itemset((x, y, ch), newValue) # 將 [x,y,channel] 的值修改為 newValueprint("updated img[x,y] = {}".format(img1[x,y]))本例程的運行結果如下:
x=10, y=10img[x,y] = [113 131 226]img[10,10,ch]: 113 131 226 img.item(10,10,ch): 113 131 226 original img[x,y] = [113 131 226]updated img[x,y] = [255 131 226]3. 圖像的創建
OpenCV 中圖像對象的數據結構是 ndarray 多維數組,因此可以用 Numpy 創建多維數組來生成圖像。特別對于空白、黑色、白色、隨機等特殊圖像,用 Numpy 創建圖像非常方便。
Numpy 可以使用 np.zeros() 等方法創建指定大小、類型的圖像對象,也可以使用 np.zeros_like() 等方法創建與已有圖像大小、類型相同的新圖像。
函數說明:
numpy.empty(shape[, dtype, order]) # 返回一個指定形狀和類型的空數組
numpy.zeros(shape[, dtype, order]) # 返回一個指定形狀和類型的全零數組
numpy.ones(shape[, dtype, order]) # 返回一個指定形狀和類型的全一數組
numpy.empty_like(img) # 返回一個與圖像 img 形狀和類型相同的空數組
numpy.zeros_like(img) # 返回一個與圖像 img 形狀和類型相同的全零數組
numpy.ones_like(img) # 返回一個與圖像 img 形狀和類型相同的全一數組
參數說明:
- shape:整型元組,定義返回多維數組的形狀
- dtype:數據類型,定義返回多維數組的類型,可選項
- img:ndarray 多維數組,表示一個灰度或彩色圖像
基本例程:
# 1.14 Numpy 創建圖像# 創建彩色圖像(RGB)# (1) 通過寬度高度值創建多維數組width, height, channels = 400, 300, 3 # 行/高度, 列/寬度, 通道數imgEmpty = np.empty((width, height, channels), np.uint8) # 創建空白數組imgBlack = np.zeros((width, height, channels), np.uint8) # 創建黑色圖像 RGB=0imgWhite = np.ones((width, height, channels), np.uint8) * 255 # 創建白色圖像 RGB=255# (2) 創建相同形狀的多維數組img1 = cv2.imread("../images/imgLena.tif", flags=1) # flags=1 讀取彩色圖像(BGR)imgBlackLike = np.zeros_like(img1) # 創建與 img1 相同形狀的黑色圖像imgWhiteLike = np.ones_like(img1) * 255 # 創建與 img1 相同形狀的白色圖像# (3) 創建彩色隨機圖像 RGB=randomimport osrandomByteArray = bytearray(os.urandom(width * height * channels))flatNumpyArray = np.array(randomByteArray)imgRGBRand = flatNumpyArray.reshape(width, height, channels)# (4) 創建灰度圖像imgGrayWhite = np.ones((width, height), np.uint8) * 255 # 創建白色圖像 Gray=255imgGrayBlack = np.zeros((width, height), np.uint8) # 創建黑色圖像 Gray=0imgGrayEye = np.eye(width) # 創建對角線元素為1 的單位矩陣 randomByteArray = bytearray(os.urandom(width * height))flatNumpyArray = np.array(randomByteArray)imgGrayRand = flatNumpyArray.reshape(width, height) # 創建灰度隨機圖像 Gray=random本例程的運行結果如下:
4. 圖像的復制
使用 Numpy 的 np.copy() 函數可以進行圖像的復制,不能通過直接賦值進行圖像的復制。
函數說明:
arr = numpy.copy(img) # 返回一個復制的圖像
參數說明:
- img:ndarray 多維數組,表示一個灰度或彩色圖像
注意事項:
基本例程:
# 1.15 圖像的復制img1 = cv2.imread("../images/imgLena.tif", flags=1) # flags=1 讀取彩色圖像(BGR)img2 = img1.copy()print("img2=img1.copy(), img2 is img1?", img2 is img1)for col in range(100):for row in range(100):img2[col, row, :] = 0img3 = cv2.imread("../images/imgLena.tif", flags=1) # flags=1 讀取彩色圖像(BGR)img4 = img3print("img4=img3, img4 is img3?", img4 is img3)for col in range(100):for row in range(100):img4[col, row, :] = 0cv2.imshow("Demo1", img1) # 在窗口顯示圖像cv2.imshow("Demo2", img2) # 在窗口顯示圖像cv2.imshow("Demo3", img3) # 在窗口顯示圖像cv2.imshow("Demo4", img4) # 在窗口顯示圖像key = cv2.waitKey(0) # 等待按鍵命令本例程中,img4=img3 直接賦值,改變 img4 的數值后 img3 的數值也被改變了;img2 = img1.copy(),改變 img2 的數值后 img1 并未發生改變。
本例程的運行結果如下,使用 np.copy() 方法得到的新圖像才是深拷貝。
img2=img1.copy(), img2 is img1? False img4=img3, img4 is img3? True5. 圖像的裁剪
用 Numpy 的切片方法可以進行圖像的裁剪,操作簡單方便。
方法說明:
retval = img[y:y+h, x:x+w].copy()
- 對圖像 img 裁剪并返回指定的矩陣區域圖像。
參數說明:
- img:圖像數據,ndarray 多維數組
- x, y:整數,像素值,裁剪矩形區域左上角的坐標值
- w, h:整數,像素值,裁剪矩形區域的寬度、高度
- 返回值 retval:裁剪后獲得的 OpenCV 圖像,nparray 多維數組
注意事項:
基本例程:
# 1.16 圖像的裁剪img1 = cv2.imread("../images/imgLena.tif", flags=1) # flags=1 讀取彩色圖像(BGR)xmin, ymin, w, h = 180, 190, 200, 200 # 矩形裁剪區域 (ymin:ymin+h, xmin:xmin+w) 的位置參數imgCrop = img1[ymin:ymin+h, xmin:xmin+w].copy() # 切片獲得裁剪后保留的圖像區域cv2.imshow("DemoCrop", imgCrop) # 在窗口顯示 彩色隨機圖像key = cv2.waitKey(0) # 等待按鍵命令擴展例程:
函數 cv2.selectROI() 可以通過鼠標選擇感興趣的矩形區域(ROI)。
cv2.selectROI(windowName, img, showCrosshair=None, fromCenter=None):
使用 cv2.selectROI(),可以實現對 ROI 的裁剪,詳見例程 1.17。
# 1.17 圖像的裁剪 (ROI)img1 = cv2.imread("../images/imgLena.tif", flags=1) # flags=1 讀取彩色圖像(BGR)roi = cv2.selectROI(img1, showCrosshair=True, fromCenter=False)xmin, ymin, w, h = roi # 矩形裁剪區域 (ymin:ymin+h, xmin:xmin+w) 的位置參數imgROI = img1[ymin:ymin+h, xmin:xmin+w].copy() # 切片獲得裁剪后保留的圖像區域cv2.imshow("DemoRIO", imgROI)cv2.waitKey(0)6. 圖像的拼接
用 Numpy 的數組堆疊方法可以進行圖像的拼接,操作簡單方便。
方法說明:
retval = numpy.hstack((img1, img2, …)) # 水平拼接
retval = numpy.vstack((img1, img2, …)) # 垂直拼接
- np.hstack() 按水平方向(列順序)拼接 2個或多個圖像,圖像的高度(數組的行)必須相同。
- np.vstack() 按垂直方向(行順序)拼接 2個或多個圖像,圖像的寬度(數組的列)必須相同。
- 綜合使用 np.hstack() 和 np.vstack() 函數,可以實現圖像的矩陣拼接。
- np.hstack() 和 np.vstack() 只是簡單地將幾張圖像直接堆疊而連成一張圖像,并未對圖像進行特征提取和邊緣處理,因而并不能實現圖像的全景拼接。
參數說明:
- img1, img2, …:拼接前的圖像,ndarray 多維數組
- 返回值 retval:拼接后的圖像,ndarray 多維數組
基本例程:
# 1.18 圖像拼接img1 = cv2.imread("../images/imgLena.tif") # 讀取彩色圖像(BGR)img2 = cv2.imread("../images/logoCV.png") # 讀取彩色圖像(BGR)img1 = cv2.resize(img1, (400, 400))img2 = cv2.resize(img2, (300, 400))img3 = cv2.resize(img2, (400, 300))imgStackH = np.hstack((img1, img2)) # 高度相同圖像可以橫向水平拼接imgStackV = np.vstack((img1, img3)) # 寬度相同圖像可以縱向垂直拼接print("Horizontal stack:\nShape of img1, img2 and imgStackH: ", img1.shape, img2.shape, imgStackH.shape)print("Vertical stack:\nShape of img1, img3 and imgStackV: ", img1.shape, img3.shape, imgStackV.shape)cv2.imshow("DemoStackH", imgStackH) # 在窗口顯示圖像 imgStackHcv2.imshow("DemoStackV", imgStackV) # 在窗口顯示圖像 imgStackVkey = cv2.waitKey(0) # 等待按鍵命令本例程的運行結果如下:
Horizontal stack: Shape of img1, img2 and imgStackH: (400, 400, 3) (400, 300, 3) (400, 700, 3) Vertical stack: Shape of img1, img3 and imgStackV: (400, 400, 3) (300, 400, 3) (700, 400, 3)7. 圖像通道的拆分
函數 cv2.split() 將 3 通道 BGR 彩色圖像分離為 B、G、R 單通道圖像。
函數說明:
cv2.split(img[, mv]) -> retval # 圖像拆分為 BGR 通道
- 函數 cv2.split() 傳入一個圖像數組,并將圖像拆分為 B/G/R 三個通道。
參數說明:
- img:圖像數據,ndarray 多維數組
- mv:指定的分拆通道(可選)
注意事項:
基本例程:
# 1.19 圖像拆分通道img1 = cv2.imread("../images/imgB1.jpg", flags=1) # flags=1 讀取彩色圖像(BGR)cv2.imshow("BGR", img1) # BGR 圖像# BGR 通道拆分bImg, gImg, rImg = cv2.split(img1) # 拆分為 BGR 獨立通道cv2.imshow("rImg", rImg) # 直接顯示紅色分量 rImg 顯示為灰度圖像# 將單通道擴展為三通道imgZeros = np.zeros_like(img1) # 創建與 img1 相同形狀的黑色圖像imgZeros[:,:,2] = rImg # 在黑色圖像模板添加紅色分量 rImgcv2.imshow("channel R", imgZeros) # 擴展為 BGR 通道print(img1.shape, rImg.shape, imgZeros.shape)cv2.waitKey(0)cv2.destroyAllWindows() # 釋放所有窗口本例程的運行結果如下:
(512, 512, 3) (512, 512) (512, 512, 3)運行結果表明:
擴展例程:
使用 NumPy 切片得到分離通道更為簡便,而且運行速度比 cv2.split 更快。
本例程的運行結果如下,GR channel 是消除 B通道(保留 G/R 通道的圖像):
8. 圖像通道的合并
函數 cv2.merge() 將 B、G、R 單通道合并為 3 通道 BGR 彩色圖像。
函數說明:
cv2.merge(mv[, dst]) -> retval # BGR 通道合并
參數說明:
- mv:要合并的單通道
- dst:通道合并的圖像,ndarray 多維數組
注意事項:
基本例程:
# 1.21 圖像通道的合并img1 = cv2.imread("../images/imgB1.jpg", flags=1) # flags=1 讀取彩色圖像(BGR)bImg, gImg, rImg = cv2.split(img1) # 拆分為 BGR 獨立通道# cv2.merge 實現圖像通道的合并imgMerge = cv2.merge([bImg, gImg, rImg])cv2.imshow("cv2Merge", imgMerge)# Numpy 拼接實現圖像通道的合并imgStack = np.stack((bImg, gImg, rImg), axis=2)cv2.imshow("npStack", imgStack)print(imgMerge.shape, imgStack.shape)print("imgMerge is imgStack?", np.array_equal(imgMerge, imgStack))cv2.waitKey(0)cv2.destroyAllWindows() # 釋放所有窗口本例程的運行結果如下。imgMerge 與 imgStack 不僅形狀相同,而且每個位置的元素相等,表明 cv2.merge() 與 np.stack() 方法合并圖像通道的結果是相同的。
(512, 512, 3) (512, 512, 3) imgMerge is imgStack? True【本節完】
版權聲明:
youcans 的 OpenCV 學習課 @ youcans 原創作品
轉載必須標注原文鏈接:https://blog.csdn.net/youcans/article/details/121068795
Copyright 2021 youcans, XUPT
Crated:2021-11-01
歡迎關注 『youcans 的 OpenCV 學習課』 系列,持續更新
youcans 的 OpenCV 學習課—1.安裝與環境配置
youcans 的 OpenCV 學習課—2.圖像讀取與顯示
youcans 的 OpenCV 學習課—3.圖像的創建與修改
youcans 的 OpenCV 學習課—4.圖像的疊加與混合
youcans 的 OpenCV 學習課—5.圖像的幾何變換
youcans 的 OpenCV 學習課—6.灰度變換與直方圖處理
youcans 的 OpenCV 學習課—7.空間域圖像濾波
youcans 的 OpenCV 學習課—8.頻率域圖像濾波(上)
youcans 的 OpenCV 學習課—9.頻率域圖像濾波(下)
總結
以上是生活随笔為你收集整理的youcans 的 OpenCV 学习课—3.图像的创建与修改的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第一个鸿蒙程序“hello world“
- 下一篇: eclipemaven本地仓库依赖_只用