Python 和 OpenCV 庫使處理圖像或視頻等視覺輸入變得非常容易。在本篇文章中,我將討論有關(guān)圖像混合的內(nèi)容并提出一種合并兩個圖像的像素信息的方法。為了實現(xiàn)這一點,我使用了第三張圖像(所謂的蒙版),作為混合過程的模板。因為這個模板是一個加權(quán)因子矩陣,所以有可能產(chǎn)生結(jié)果,這是通常在網(wǎng)上找到的混合方法無法獲得的(或者至少我沒有找到)。
簡介
Photoshop 和其他圖像處理程序(例如,GIMP 或 Paint.net 等免費軟件替代品)提供了廣泛的可能性來編輯和處理圖像和照片。然而,有時,人們希望執(zhí)行標(biāo)準(zhǔn)軟件中沒有的操作,或者只是想了解一下具體的照片編輯程序是如何工作的。這兩個目標(biāo)都可以通過嘗試將照片編輯程序轉(zhuǎn)換為編程代碼來實現(xiàn)。在這里,在這段文字中,我將這樣做并提供用于將圖像的部分(或整個圖像)與另一個圖像合并的代碼。
要理解本教程的內(nèi)容,您應(yīng)該具備 Python (Python 3) 的基本知識以及圖像在計算機(jī)上的表示方式(即 RGB 或 BGR 模型)。但是,由于代碼并不過分復(fù)雜,因此具有其他編程語言背景的人也應(yīng)該可以訪問它。如果您打算執(zhí)行或使用我加載的代碼,則需要在您的機(jī)器上安裝 OpenCv 和 NumPy(使用?pip
?命令或?Anaconda Navigator
?這樣做)當(dāng)然還有 Python。OpenCV 是一個免費提供的圖像處理庫(接口可用于 C++、Python 等),包含許多有助于處理視頻和圖像等視覺輸入的功能。這里,在這段代碼中,我只使用了一些基本的方法。我使用命令在屏幕上加載和顯示圖像,并設(shè)置一個 while 循環(huán)來啟用與程序的一些交互(但這也非常實際)。如前所述,還需要 NumPy。NumPy 是 Python 處理數(shù)字、數(shù)組和矩陣的第一大庫,它非常方便,可以使用 NumPy 方法操作和修改 OpenCv 圖像數(shù)據(jù)。
背景
混合圖像
關(guān)于如何使用 OpenCV 將存儲在一個圖像中的信息與第二個圖像的信息合并的示例代碼在網(wǎng)絡(luò)上廣泛可用。例如,?cv2.addWeighted(image1, weighting_factor1, image2, weighting_factor2)
?OpenCV 函數(shù)是一種單線函數(shù),能夠以簡單的方式執(zhí)行圖像混合。如果兩個加權(quán)因子都選擇 0.5,則該函數(shù)返回兩個圖像的等權(quán)重(或均值)混合。選擇 0.7 和 0.3 作為輸入權(quán)重,將導(dǎo)致圖像由第一幅圖像的 70% 像素顏色和第二幅圖像的 30% 像素顏色組成。再舉一個例子,選擇 1 和 0 將返回圖像一的未更改版本,而 0 和 1 將返回圖像二的未更改版本。我提到這一點,因為它在下面變得很重要,當(dāng)我進(jìn)入我編寫的代碼的細(xì)節(jié)時。
使用掩碼插入圖像
該? cv2.addWeighted()
?當(dāng)兩個相同大小的圖像的信息(或更準(zhǔn)確地說,感興趣的區(qū)域)的信息被組合并且相同的加權(quán)因子應(yīng)用于所有像素時,該函數(shù)特別有用。然而,有時人們計劃將特定區(qū)域從一張圖像轉(zhuǎn)移到另一張圖像。這可以通過不同的方式完成,但一種常見的方法是使用面具。掩碼通常是一個二進(jìn)制圖像(僅包含黑色和白色像素),用作包圍插入或混合區(qū)域的模板。為了澄清這一點,我舉了一個例子。如果希望將圖像的天空替換為第二張圖像的背景,則蒙版(或二值圖像)需要有一個黑色區(qū)域代表圖像一的天空,以便程序“知道”該放在哪里圖二的背景。換句話說,其中像素顏色為黑色(8 位圖像中的 0;[0,0,
使用加權(quán)因子矩陣進(jìn)行圖像疊加
在我在這里展示的示例代碼中,我將圖像混合方法與基于掩碼的圖像插入方法結(jié)合起來。與“標(biāo)準(zhǔn)”方法相比,我使用非二進(jìn)制掩碼(可以保存 0 到 255 之間的所有值),其中每個像素都用作單獨的權(quán)重。因此,我沒有定義圖像一的整體加權(quán)因子(例如,0.7)和圖像二的整體加權(quán)因子(例如,0.3),而是使用掩碼的每個像素作為加權(quán)因子(參見下圖)。例如,如果掩碼中位于 [X, Y] 位置的像素的值為 0(即黑色),則圖像 1 的像素將放置在該位置。如果掩碼的一個像素保持值 255(白色),則圖像 2 的相應(yīng)像素將占據(jù)該位置。如果它介于兩者之間(例如,170),則會混合圖像一和圖像二。使用掩碼作為由權(quán)重因子組成的地圖在疊加兩個圖像時提供了更大的靈活性。它可以產(chǎn)生過渡或“軟”邊界(甚至更多)。
使用代碼
我將您可以在存儲庫中找到的代碼示例分為四個部分。下面,我只展示了兩個內(nèi)部部分的代碼,因為它們涵蓋了本教程的主要主題。為完整起見,我在下面簡要評論第 1 節(jié)和第 4 節(jié),然后以要點的形式提供第 2 節(jié)和第 3 節(jié)的基本信息。
在第 1 節(jié)中,您可以找到用于加載所需圖像(即?cv2.imread(“X.jpg”,cv2.IMREAD_UNCHANGED)
?)的OpenCV 代碼。有三個圖像:image 1 是在其上的內(nèi)容的表面image 2 粘貼,并且image 3 是限定用于混合操作條件的掩?;蚰0?。還有一些附加功能,但我留給您去了解它們的用途(我上傳的示例代碼中有信息豐富的注釋)。
在第 4 節(jié)中,您會發(fā)現(xiàn)一個“?while
?循環(huán)”,它允許與程序進(jìn)行一種基本的交互。循環(huán)正在等待關(guān)鍵事件。按“ ?q
?”退出程序,按“ ?s
?”保存混合操作結(jié)果后退出程序。
第 2 節(jié)和第 3 節(jié)包含用于根據(jù)掩碼提供的加權(quán)因子混合兩個圖像信息的代碼。
- 該函數(shù) ?
mix_pixel(pix_1, pix_2, perc)
?以類似的方式處理輸入?cv2.addWeighted()
?。它根據(jù)在第三個參數(shù)(即,給定的權(quán)重需要從兩個圖像和共混物像素信息?perc
?的功能的)(例如,當(dāng)?perc
?在0和255的中間,它給出了一個50-50混合像素1和像素 2;另請參見混合圖像部分)。當(dāng)參數(shù)為?NumPy
?數(shù)組時(注意:所有數(shù)組需要具有相同的維度),操作將應(yīng)用于圖像的所有對應(yīng)像素。第三個參數(shù)是一個數(shù)組(掩碼),其中包含每個像素的單獨加權(quán)因子。 - 該?
blend_images_using_mask(img_orig, img_for_overlay,img_mask)
?功能是相當(dāng)簡單的。它獲取三張圖像,檢查掩碼是否為 3 通道灰度圖像(否則,它不會與其他圖像具有相同的維度),調(diào)用該mix_pixel()函數(shù),然后將結(jié)果作為 3 通道 8 位無符號整數(shù)返回(否則用于顯示圖像的 OpenCV 命令將失?。?。 - 您在下面的示例中找到的最終命令用于顯示不同類型的圖像,以使過程的結(jié)果可見并可用于評估。還有一些代碼行用于使圖像變小,因為照片的原始尺寸對于標(biāo)準(zhǔn)屏幕來說通常太大。
# code skeleton
import numpy as np
import cv2
# .......omitted code
# functions for blending operations
# takes a pixel from image 1 (pix_1) and blends it with a pixel from image 2 (pix_2)
# depending on the value given in perc (percentage);
# if perc = 0 or 255 (or 0,0,0 or 255,255,255) it will perform no blending at all
# and return the value of image 1 or image 2;
# by contrast, all values in between (e.g., 140) will give a weighted blend of the two images
# function can be used with scalars or numpy arrays (perc will be greyscale numpy array then)
def mix_pixel(pix_1, pix_2, perc):
return (perc/255 * pix_1) + ((255 - perc)/255 * pix_2)
# function for blending images depending on values given in mask
def blend_images_using_mask(img_orig, img_for_overlay, img_mask):
# turn mask into 24 bit greyscale image if necessary
# because mix_pixel() requires numpy arrays having the same dimension
# if image is 24-bit BGR, the image has 3 dimensions, if 8 bit greyscale 2 dimensions
if len(img_mask.shape) != 3:
img_mask = cv2.cvtColor(img_mask, cv2.COLOR_GRAY2BGR)
# interpolate between two images (img_orig and img_to_insert)
# using the values in img_mask (each pixel serves as individual weight)
# as weighting factors (ranging from [0,0,0] to [255,255,255] or 0 to 100 percent);
# because all three images are numpy arrays standard operators
# for multiplication etc. will be applied to all values in arrays
img_res = mix_pixel(img_orig, img_for_overlay, img_mask)
return img_res.astype(np.uint8)
# blend images and display results
# call function above to perform blending with mask (containing weights for interpolation)
img_blended = blend_images_using_mask(img, img_insert, img_insert_mask)
#print(img_blended.shape)
# rf -> resizing factor; used to make images smaller, so they can be displayed on screen;
# blending operations, however, will be performed on original sized images
rf = 0.4
wi = img.shape[1] # width and
hi = img.shape[0] # height of images
# call OpenCV resize
img_sm = cv2.resize(img, (int(wi * rf), int(hi * rf)),
interpolation=cv2.INTER_CUBIC)
img_insert_sm = cv2.resize(
img_insert, (int(wi * rf), int(hi * rf)), interpolation=cv2.INTER_CUBIC)
img_blended_sm = cv2.resize(
img_blended, (int(wi * rf), int(hi * rf)), interpolation=cv2.INTER_CUBIC)
# display images
cv2.imshow("Original Image", img_sm)
cv2.imshow("Insert This Image", img_insert_sm)
cv2.imshow("Blended Images", img_blended_sm)
# .......omitted code
如果您想要了解更多關(guān)于Python OpenCV的內(nèi)容,推薦大家可以系統(tǒng)地學(xué)習(xí)一下OpenCV相關(guān)教程。