本系列專欄寫作方式
本系列專欄將采用首創的問答式寫作形式,快速讓你學習到 OpenCV 的初級、中級、高級知識,
7. 在 Python OpenCV 尋找目標區域以及邊緣擴展的解決方案
針對影像的特定區域進行操作,在 OpenCV 中被稱作 ROI ,即目標區域或者叫做感興趣區域,在處理影像的時候,可以先定位一個目標區域,然后再在該區域進行細節篩選,這樣可以提高我們程式的速度和準確性,
實作 ROI 操作其實就是采用 numpy 對影像進行操作
例如下述測驗代碼,我們需要尋找影像指定區域
import cv2
import numpy as np
src = cv2.imread("./7_img.jpg")
# cv2.imshow("src",src)
cv2.imshow("src",src)
# 注意后面的串列獲取,格式為 [rows,cols]
roi_img = src[50:150, 100:150]
print(roi_img.shape)
cv2.imshow("roi_img", roi_img)
cv2.waitKey()
cv2.destroyAllWindows()
運行結果如下所示,尤其需要注意的是代碼注釋部分的說明,在做 ROI 的時候,需要篩選的區域用偽代碼表示如下 src[起始行像素:結束行像素,起始列像素:結束列像素]

這個地方經常出現的一個 BUG 如下
error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'cv::imshow'
很多博客中會寫是圖片檔案中文路徑的問題,但是其實這只是一種結果,真正的原因是影像出現了空結構,也就是讀取影像的 shape 屬性如果展示內容為 (0,0,3),那必然出現上述錯誤,例如下面代碼就存在該問題,
import cv2
import numpy as np
src = cv2.imread("./7_img.jpg")
# cv2.imshow("src",src)
roi_img = src[50:50, 100:100]
print(roi_img.shape)
cv2.imshow("roi_img", roi_img)
cv2.waitKey()
cv2.destroyAllWindows()
確定目標區域,最大的難點就是坐標問題,你可以多次嘗試一下,直到記住目標區域的定位方式,
獲取到 ROI 區域之后,可以對其進行修改,例如,下述代碼將 ROI 區域設定為灰度影像
import cv2
import numpy as np
src = cv2.imread("./7_img.jpg")
# cv2.imshow("src",src)
cv2.imshow("src", src)
# 注意后面的串列獲取,格式為 [rows,cols]
roi_img = src[50:150, 100:150]
gray = cv2.cvtColor(roi_img, cv2.COLOR_BGR2GRAY)
src[50:150, 100:150] = gray
cv2.imshow("src", src)
cv2.waitKey()
cv2.destroyAllWindows()
直接運行該代碼,會出現如下錯誤
ValueError: could not broadcast input array from shape (100,50) into shape (100,50,3)
因為合并的影像通道數不對,接下來你需要做的是將灰度圖擴展為三通道形式,使用下述代碼即可實作,
# 灰度圖擴展到 3 通道
grays = np.stack((gray,)*3, axis=-1)
print(grays)
src[50:150, 100:150] = grays
其中用到了 numpy.stack(arrays, axis=0) 函式,將陣列進行連接,修改代碼之后得到的運行結果如下所示,

OopenCV 影像的拆分與合并
上述案例你已經掌握了影像目標區域獲取的方式,接下來我們對影像進行一下通道的拆分與合并,具體會使用到兩個函式,分別是 cv2.merge 和 cv2.split,
兩個函式的原型可以直接獲取
# splite 函式原型
mv = cv2.split(m[, mv])
# merge 函式原型
dst = cv2.merge(mv[, dst])
下面進行影像的拆分,并通過 matplotlib 庫進行圖片的展示:
import cv2
import numpy as np
import matplotlib.pyplot as plt
src = cv2.imread("7_img.jpg")
b, g, r = cv2.split(src)
plt.subplot(131)
plt.imshow(b, "gray")
plt.title("b")
plt.subplot(132)
plt.imshow(g, "gray")
plt.title("g")
plt.subplot(133)
plt.imshow(r, "gray")
plt.title("r")
plt.show()
運行之后可以獲取每個通道的灰度圖,cv2.split 執行的效率并不高,所以你使用的影像如果過大,記得稍等片刻,

針對每個通道還可以進行拆分賦值,例如,你可以將 BGR 順序顛倒,形成不同色彩的圖片,
## 交換通道順序,進行合并
dst = cv2.merge((r,g,b))
cv2.imshow("dst",dst)
cv2.waitKey()
運行之后,可以得到對應的效果,

cv2.merge 和 cv2.split 函式引數串列,請重點比對原型進行學習,
如果你想要單獨修改某一通道值,可以使用 numpy 進行操作,例如我們將 R 通道的像素值修改為 0,使用下述代碼即可
import cv2
import numpy as np
import matplotlib.pyplot as plt
src = cv2.imread("7_img.jpg")
src[:,:,2] = 0
cv2.imshow("src1",src)
src[:,:,2] = 255
cv2.imshow("src2",src)
cv2.waitKey()

OpenCV 影像邊緣擴展
接下來將要學習的函式是影像擴邊操作,使用 cv2.copyMakeBorder 函式,函式原型如下:
dst = cv2.copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]])
先掌握幾個核心引數 src 原影像,top,bottom,left,right分別表示在原圖四周擴充邊緣的大小,
關于 borderType 引數,表示的需要填充的邊界型別,該值有多種取值,建議是自行嘗試,我們采用其中一個優先進行說明,
import cv2
from matplotlib import pyplot as plt
src = cv2.imread("color.jpg")
# 后續繪圖使用 pyplot ,所以切換一下顏色通道的排序,相當于從 BGR 轉換為 RGB
b, g, r = cv2.split(src)
img = cv2.merge([r, g, b])
replicate = cv2.copyMakeBorder(img, 20, 20, 20, 20, cv2.BORDER_REPLICATE)
print("原圖形狀",src.shape)
print("擴充邊界之后的形狀",replicate.shape)
列印結果,注意像素的寬度和高度都擴展了 40,擴展之后的圖片,你可以自行比對,
原圖形狀 (624, 500, 3)
擴充邊界之后的形狀 (664, 540, 3)
影像擴展邊界最后一個引數 borderType 有如下取值
cv2.BORDER_CONSTANT:固定值填充;cv2.BORDER_REFLECT_101或者cv2.BORDER_DEFAULT:取鏡像對稱的像素填充;cv2.BORDER_REPLICATE:重復最后一個像素;cv2.BORDER_WRAP:取鏡像,
結合上文,可以實作這樣一個效果,找到 ROI 區域,然后對其進行邊緣擴展,
import cv2
from matplotlib import pyplot as plt
src = cv2.imread("7_img.jpg")
# 后續繪圖使用 pyplot ,所以切換一下顏色通道的排序,相當于從 BGR 轉換為 RGB
b, g, r = cv2.split(src)
img = cv2.merge([r, g, b])
roi_img = img[120:170,200:250]
dst = cv2.copyMakeBorder(roi_img, 2, 2, 2, 2, cv2.BORDER_CONSTANT,value=[0,0,255])
img[118:172,198:252] = dst
plt.imshow(img)
plt.title('img')
plt.show()
執行代碼之后,我們在原圖找到某個區域,然后對其進行邊緣擴展,實作了一個矩形的框選操作,效果如下:

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294807.html
標籤:其他
