基于Python OpenCV實現(xiàn)圖像的覆蓋
前言
在本文中,我將展示如何將對象從一個圖像添加到另一個圖像。為此,我們需要:
1.背景圖像;
2.對象
3.對象的mask(mask為黑色,其他空間為白色)。
在我們的例子中,背景是一張大海的照片,對象是一杯咖啡。在這里,他們是:

1.導入相關庫
現(xiàn)在,使用jupiter notebook創(chuàng)建一個新文件。首先,我們需要導入必要的模塊:
import cv2 # OpenCV import numpy as np import matplotlib.pyplot as plt
2.使用OpenCV讀取和顯示圖像
讓我們在cv2.imread()函數(shù)的幫助下打開圖像并顯示它們。
注意!
由于某些原因,OpenCV以BGR格式讀取圖像(藍色和紅色被交換)。我們需要借助cv2.cvtColor()函數(shù)將BGR轉換為RGB格式。
# Original image, which is the background?
background = cv2.imread('background.jpg')
background = cv2.cvtColor(background, cv2.COLOR_BGR2RGB)
# Image of the object
img = cv2.imread('cup.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Image the object's mask
mask = cv2.imread('cup_mask.png')
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2RGB)
print("Background shape:", background.shape)
print("Image shape:", img.shape)
print("Mask shape:", img.shape)
# Background shape: (1280, 1920, 3)
# Image shape: (860, 1151, 3)
# Mask shape: (860, 1151, 3)我們看到背景圖像的高度為1280,寬度為1920,目標圖像的高度為860,寬度為1151。
讓我們看看這些圖片:
plt.figure(figsize=(16,16))
plt.title("Background", fontsize=18)
plt.imshow(background);
fig, ax = plt.subplots(1, 2, figsize=(16, 7))
ax[0].imshow(img)
ax[0].set_title('Object', fontsize=18)
ax[1].imshow(mask)
ax[1].set_title('Mask', fontsize=18);
3.從物體的圖像中去除背景
現(xiàn)在我們將定義一個函數(shù),它將對象的mask轉換為布爾數(shù)組。
在原始mask上,對象區(qū)域填充黑色,背景區(qū)域填充白色。
布爾數(shù)組具有與原始mask相同的高度和寬度,但只有一個通道。如果一個像素屬于對象區(qū)域,它的值為True,否則為False。
布爾mask將幫助我們刪除所有的背景像素。
def remove_obj_background(img_path, mask_path):
? ? '''
? ? Function returns:
? ? - image of the object with removed background in CV2 RGB format (numpy array with dimensions (width, height, 3))
? ? - boolean mask of the object (numpy array with dimensions (width, height))
? ? '''
? ? img = cv2.imread(img_path)
? ? img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
? ??
? ? mask = cv2.imread(mask_path)
? ? mask = cv2.cvtColor(mask, cv2.COLOR_BGR2RGB)?
? ??
? ? h, w = mask.shape[0], mask.shape[1]
? ??
? ? # Boolean mask is a numpy array with two dimensions: width and height.
? ? # On the original mask, object area is filled with black color, background area is filled with white color.
? ? # On the boolean mask, object area is filled with True, background area is filled with False.
? ? mask_boolean = mask[:,:,0] == 0
? ? img_with_removed_background = img * np.stack([mask_boolean, mask_boolean, mask_boolean], axis=2)
? ??
? ? return img_with_removed_background, mask_boolean
img_with_removed_background, mask_boolean = remove_obj_background('cup.png', 'cup_mask.png')
print("Shape of the image of the object:", img_with_removed_background.shape)
print("Shape of the boolean mask:", mask_boolean.shape)
print("\n")
# Image with removed background shape: (860, 1151, 3)
# Boolean mask shape: (860, 1151)
fig, ax = plt.subplots(1, 2, figsize=(16, 7))
ax[0].imshow(img_with_removed_background)
ax[0].set_title('Object with removed background', fontsize=18)
ax[1].imshow(mask_boolean)
ax[1].set_title('Boolean mask', fontsize=18);
4.添加對象到背景圖像
在我們定義向背景圖像添加對象的函數(shù)之前,我需要解釋和可視化幾個圖像重疊的情況。
比如說,背景圖像的高度是h_background,寬度是w_background,而目標圖像的高度是h,寬度是w。
h應該小于h_background, w應該小于w_background。
case1) 如果我們將物體放置在背景的中間,那么一切都很簡單:大小為h x w的背景區(qū)域部分應該被物體替換掉。

case2) 如果我們將物體放置在背景的左上角,那么物體的一部分可能在背景區(qū)域之外。在這種情況下,背景區(qū)域的大小(h - y) x (w - x)的部分應該被替換為對象。
這里-x和-y是對象圖像左上角的坐標。符號' - '在這里是因為背景圖像的左上角坐標x=0和y=0。從背景圖像的左上角到對象左上角的所有區(qū)域的x坐標都是負的,高于背景圖像的左上角的所有區(qū)域的y坐標都是負的。

case3) 如果我們將物體放置在背景的左下角,那么物體的一部分可能在背景區(qū)域之外。在這種情況下,背景區(qū)域大小為(h_background - y) x (w - x)的部分應該被替換為對象。
一般,面積可以計算為(h - max (0, y + h - h_background)) x (w - x),因為如果目標圖像的最低邊界在背景圖像的最低邊界之上,那么h x (w - x)區(qū)域應該被替換為目標。


case4) 如果我們將物體放在背景的右上角,那么物體的一部分可能會在背景區(qū)域之外。在這種情況下,大小為 (h - y) x (w_background - x) 的背景區(qū)域部分應替換為對象。
一般來說,面積可以計算為 (h - y) x (w - max(0, x + w - w_background)),因為如果物體圖像的右邊界在背景圖像右邊界的左側,則 (h - y) x w 區(qū)域應替換為對象。


case5) 如果我們將物體放在背景的右下角,那么物體的一部分可能會在背景區(qū)域之外。在這種情況下,大小為 (h_background - y) x (w_background - x) 的背景區(qū)域部分應替換為對象。
一般來說,面積可以計算為 (h - max(0, y + h - h_background)) x (w - max(0, x + w - w_background)),因為如果物體圖像的右側部分在背景圖像的右部分的左邊,如果對象圖像的最低部分高于背景圖像的最低部分,則應將h x w區(qū)域替換為對象。


現(xiàn)在,考慮到上述所有情況,讓我們定義函數(shù):
def add_obj(background, img, mask, x, y):
'''
Arguments:
background - background image in CV2 RGB format
img - image of object in CV2 RGB format
mask - mask of object in CV2 RGB format
x, y - coordinates of the center of the object image
0 < x < width of background
0 < y < height of background
Function returns background with added object in CV2 RGB format
CV2 RGB format is a numpy array with dimensions width x height x 3
'''
bg = background.copy()
h_bg, w_bg = bg.shape[0], bg.shape[1]
h, w = img.shape[0], img.shape[1]
# Calculating coordinates of the top left corner of the object image
x = x - int(w/2)
y = y - int(h/2)
mask_boolean = mask[:,:,0] == 0
mask_rgb_boolean = np.stack([mask_boolean, mask_boolean, mask_boolean], axis=2)
if x >= 0 and y >= 0:
h_part = h - max(0, y+h-h_bg) # h_part - part of the image which overlaps background along y-axis
w_part = w - max(0, x+w-w_bg) # w_part - part of the image which overlaps background along x-axis
bg[y:y+h_part, x:x+w_part, :] = bg[y:y+h_part, x:x+w_part, :] * ~mask_rgb_boolean[0:h_part, 0:w_part, :] + (img * mask_rgb_boolean)[0:h_part, 0:w_part, :]
elif x < 0 and y < 0:
h_part = h + y
w_part = w + x
bg[0:0+h_part, 0:0+w_part, :] = bg[0:0+h_part, 0:0+w_part, :] * ~mask_rgb_boolean[h-h_part:h, w-w_part:w, :] + (img * mask_rgb_boolean)[h-h_part:h, w-w_part:w, :]
elif x < 0 and y >= 0:
h_part = h - max(0, y+h-h_bg)
w_part = w + x
bg[y:y+h_part, 0:0+w_part, :] = bg[y:y+h_part, 0:0+w_part, :] * ~mask_rgb_boolean[0:h_part, w-w_part:w, :] + (img * mask_rgb_boolean)[0:h_part, w-w_part:w, :]
elif x >= 0 and y < 0:
h_part = h + y
w_part = w - max(0, x+w-w_bg)
bg[0:0+h_part, x:x+w_part, :] = bg[0:0+h_part, x:x+w_part, :] * ~mask_rgb_boolean[h-h_part:h, 0:w_part, :] + (img * mask_rgb_boolean)[h-h_part:h, 0:w_part, :]
return bg
除了將背景、對象和mask圖像傳遞給函數(shù)外,我們還將傳遞坐標x和y,它們定義了對象的中心位置。
坐標(0,0)是背景的左上角。
w_bg和h_bg是背景的寬度和高度。
x和y應滿足以下條件:0 < x < w_bg和0 < y < h_bg。
5.結果展示
讓我們看看這個函數(shù)是如何工作的。
例1). 讓我們把杯子放在背景的中央。背景的寬度是1920,高度是1280,所以對象的中心坐標是x=1920/2=960和y=1280/2=640。
composition_1 = add_obj(background, img, mask, 960, 640) plt.figure(figsize=(15,15)) plt.imshow(composition_1);

例2). 讓我們把杯子放在背景的左下角。這一次,對象的中心坐標是x=200和y=1100。
composition_2 = add_obj(composition_1, img, mask, 200, 1100) plt.figure(figsize=(15,15)) plt.imshow(composition_2);

例 3). 讓我們把杯子放在背景的右下角。這次對象中心的坐標是 x=1800 和 y=1100。
composition_3 = add_obj(composition_2, img, mask, 1800, 1100) plt.figure(figsize=(15,15)) plt.imshow(composition_3);

例 4). 讓我們把杯子放在背景的左上角。這次對象中心的坐標是 x=200 和 y=200。
composition_4 = add_obj(composition_3, img, mask, 200, 200) plt.figure(figsize=(15,15)) plt.imshow(composition_4);

例5). 讓我們把杯子放在背景的右上角。這一次,對象的中心坐標是x=1800和y=200。
composition_5 = add_obj(composition_4, img, mask, 1800, 200) plt.figure(figsize=(15,15)) plt.imshow(composition_5);

以上就是基于Python OpenCV實現(xiàn)圖像的覆蓋的詳細內容,更多關于Python OpenCV圖像覆蓋的資料請關注腳本之家其它相關文章!
相關文章
如何對csv文件數(shù)據分組,并用pyecharts展示
這篇文章主要介紹了如何對csv文件數(shù)據分組,并用pyecharts展示,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11
Python?Web?App開發(fā)Dockerfiles編寫示例
這篇文章主要為大家介紹了Python?Web?App編寫Dockerfiles的示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
使用python編寫批量卸載手機中安裝的android應用腳本
該腳本的功能是卸載android手機中安裝的所有第三方應用,主要是使用adb shell pm、adb uninstall 命令,需要的朋友可以參考下2014-07-07

