番外7. 在 Python OpenCV 尋找目標(biāo)區(qū)域以及邊緣擴(kuò)展的解決方案

本系列專欄寫(xiě)作方式

本系列專欄將采用首創(chuàng)的問(wèn)答式寫(xiě)作形式,快速讓你學(xué)習(xí)到 OpenCV 的初級(jí)、中級(jí)、高級(jí)知識(shí)。

7. 在 Python OpenCV 尋找目標(biāo)區(qū)域以及邊緣擴(kuò)展的解決方案

針對(duì)圖像的特定區(qū)域進(jìn)行操作,在 OpenCV 中被稱作 ROI ,即目標(biāo)區(qū)域或者叫做感興趣區(qū)域,在處理圖像的時(shí)候,可以先定位一個(gè)目標(biāo)區(qū)域,然后再在該區(qū)域進(jìn)行細(xì)節(jié)篩選,這樣可以提高我們程序的速度和準(zhǔn)確性。

實(shí)現(xiàn) ROI 操作其實(shí)就是采用 numpy 對(duì)圖像進(jìn)行操作

例如下述測(cè)試代碼,我們需要尋找圖像指定區(qū)域

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()

運(yùn)行結(jié)果如下所示,尤其需要注意的是代碼注釋部分的說(shuō)明,在做 ROI 的時(shí)候,需要篩選的區(qū)域用偽代碼表示如下 src[起始行像素:結(jié)束行像素,起始列像素:結(jié)束列像素]

2af6a27e117d405e84f8acc9b17cde68[1].png

這個(gè)地方經(jīng)常出現(xiàn)的一個(gè) BUG 如下

error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'cv::imshow'

很多博客中會(huì)寫(xiě)是圖片文件中文路徑的問(wèn)題,但是其實(shí)這只是一種結(jié)果,真正的原因是圖像出現(xiàn)了空結(jié)構(gòu),也就是讀取圖像的 shape 屬性如果展示內(nèi)容為 (0,0,3),那必然出現(xiàn)上述錯(cuò)誤。例如下面代碼就存在該問(wèn)題。

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()

確定目標(biāo)區(qū)域,最大的難點(diǎn)就是坐標(biāo)問(wèn)題,你可以多次嘗試一下,直到記住目標(biāo)區(qū)域的定位方式。

獲取到 ROI 區(qū)域之后,可以對(duì)其進(jìn)行修改,例如,下述代碼將 ROI 區(qū)域設(shè)置為灰度圖像

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()

直接運(yùn)行該代碼,會(huì)出現(xiàn)如下錯(cuò)誤

ValueError: could not broadcast input array from shape (100,50) into shape (100,50,3)

因?yàn)楹喜⒌膱D像通道數(shù)不對(duì),接下來(lái)你需要做的是將灰度圖擴(kuò)展為三通道形式,使用下述代碼即可實(shí)現(xiàn)。

# 灰度圖擴(kuò)展到 3 通道
grays = np.stack((gray,)*3, axis=-1)
print(grays)
src[50:150, 100:150] = grays

其中用到了 numpy.stack(arrays, axis=0) 函數(shù),將數(shù)組進(jìn)行連接。修改代碼之后得到的運(yùn)行結(jié)果如下所示。

b812bdd7e24b435a9d67c32dcaa22a09[1].png

OopenCV 圖像的拆分與合并

上述案例你已經(jīng)掌握了圖像目標(biāo)區(qū)域獲取的方式,接下來(lái)我們對(duì)圖像進(jìn)行一下通道的拆分與合并,具體會(huì)使用到兩個(gè)函數(shù),分別是 cv2.mergecv2.split。

兩個(gè)函數(shù)的原型可以直接獲取

# splite 函數(shù)原型
mv = cv2.split(m[, mv])
# merge 函數(shù)原型
dst = cv2.merge(mv[, dst])

下面進(jìn)行圖像的拆分,并通過(guò) matplotlib 庫(kù)進(jìn)行圖片的展示:

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()

運(yùn)行之后可以獲取每個(gè)通道的灰度圖,cv2.split 執(zhí)行的效率并不高,所以你使用的圖像如果過(guò)大,記得稍等片刻。

2fadc237cc214e739f14d3dbf687b3d5[1].png

針對(duì)每個(gè)通道還可以進(jìn)行拆分賦值,例如,你可以將 BGR 順序顛倒,形成不同色彩的圖片。

## 交換通道順序,進(jìn)行合并
dst = cv2.merge((r,g,b))
cv2.imshow("dst",dst)

cv2.waitKey()

運(yùn)行之后,可以得到對(duì)應(yīng)的效果。

835de1e1789e499bb6493cc5ed2df5c1[1].png

cv2.mergecv2.split 函數(shù)參數(shù)列表,請(qǐng)重點(diǎn)比對(duì)原型進(jìn)行學(xué)習(xí)。

如果你想要單獨(dú)修改某一通道值,可以使用 numpy 進(jìn)行操作,例如我們將 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()
737baac566f74196a14e3dd475243dcf[1].png

OpenCV 圖像邊緣擴(kuò)展

接下來(lái)將要學(xué)習(xí)的函數(shù)是圖像擴(kuò)邊操作,使用 cv2.copyMakeBorder 函數(shù),函數(shù)原型如下:

dst = cv2.copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]])

先掌握幾個(gè)核心參數(shù) src 原圖像,top,bottom,left,right分別表示在原圖四周擴(kuò)充邊緣的大小。

關(guān)于 borderType 參數(shù),表示的需要填充的邊界類型,該值有多種取值,建議是自行嘗試,我們采用其中一個(gè)優(yōu)先進(jìn)行說(shuō)明。

import cv2
from matplotlib import pyplot as plt

src = cv2.imread("color.jpg")
# 后續(xù)繪圖使用 pyplot ,所以切換一下顏色通道的排序,相當(dāng)于從 BGR 轉(zhuǎn)換為 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("擴(kuò)充邊界之后的形狀",replicate.shape)

打印結(jié)果,注意像素的寬度和高度都擴(kuò)展了 40,擴(kuò)展之后的圖片,你可以自行比對(duì)。

原圖形狀 (624, 500, 3)
擴(kuò)充邊界之后的形狀 (664, 540, 3)

圖像擴(kuò)展邊界最后一個(gè)參數(shù) borderType 有如下取值

  • cv2.BORDER_CONSTANT:固定值填充;
  • cv2.BORDER_REFLECT_101 或者 cv2.BORDER_DEFAULT :取鏡像對(duì)稱的像素填充;
  • cv2.BORDER_REPLICATE:重復(fù)最后一個(gè)像素;
  • cv2.BORDER_WRAP:取鏡像。

結(jié)合上文,可以實(shí)現(xiàn)這樣一個(gè)效果,找到 ROI 區(qū)域,然后對(duì)其進(jìn)行邊緣擴(kuò)展。

import cv2
from matplotlib import pyplot as plt

src = cv2.imread("7_img.jpg")
# 后續(xù)繪圖使用 pyplot ,所以切換一下顏色通道的排序,相當(dāng)于從 BGR 轉(zhuǎn)換為 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()

執(zhí)行代碼之后,我們?cè)谠瓐D找到某個(gè)區(qū)域,然后對(duì)其進(jìn)行邊緣擴(kuò)展,實(shí)現(xiàn)了一個(gè)矩形的框選操作,效果如下:

d09d70e2558341789833f87b23112ad8[1].png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容