如果給你一張圖片作為背景,另外一張圖片中的物體作為前景圖,要把前景圖中的物體疊加布置到背景圖的中間位置,并且前景圖中的物體需要在背景圖中有旋轉(zhuǎn)和投影,怎么處理?
如果是一兩張圖,PS應(yīng)該是首選,但是如果是批量的呢?這就是小編要準備處理的一個小問題。挑戰(zhàn)一下,用Python+OpenCV來實現(xiàn)這個小目標。
提出問題和解決思路
作為前景的圖片的背景是白色不透明的,不過這就有條件實現(xiàn)摳圖了
前景圖的尺寸甚至比背景圖大,要縮小并且要算出位置
如果圖片沒有透明層來控制,則需要轉(zhuǎn)換并添加透明層
疊加的前景圖需要可以控制旋轉(zhuǎn)角度,但旋轉(zhuǎn)同時也影響前景圖的尺寸
摳圖進行疊加效果不好怎么辦?要進行羽化模糊化處理
為了讓圖片的疊加更加自然,還要考慮讓前景物體在背景上投個影
代碼驗證
1. 摳圖,取出前景物體的兩種處理方式比較
為了看到中間過程,代碼中添加了加輪廓線和掩碼圖兩個中間圖的輸出。
1.1 通過cv2.bitwise_and()函數(shù)處理輪廓掩碼
下面首先使用cv2.bitwise_and()函數(shù)處理。掩碼圖是有透明度控制層的,每個像素,在輪廓線外的的像素值是(B, G, R, A)=(0,0,0,0),輪廓線內(nèi)的像素值是(B, G, R, A)=(255,255,255,255)。這種情況下,前景圖中任何在輪廓線外的像素和掩碼圖對應(yīng)位置的掩碼像素進行按位“與”操作時,都會變成透明‘人’,而輪廓線內(nèi)部的原圖像素則會保留原值。
這里OpenCV中的一個函數(shù)是個關(guān)鍵。注意閾值thresh。
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
import cv2 import numpy as np import tkinter as tk def get_display_size(): root = tk.Tk() width = root.winfo_screenwidth() height = root.winfo_screenheight() root.quit() return width, height def extract_contour_from_jpg(image_path, output_path): # 讀取JPEG圖像 image = cv2.imread(image_path) # 如果是 JPG 圖像,轉(zhuǎn)換為 BGRA 格式以支持透明度 if image.shape[2] == 3: # Assuming BGR without alpha image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA) # 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 應(yīng)用閾值操作以找到物體 _,thresh=cv2.threshold(gray,240,255,cv2.THRESH_BINARY_INV) # 找到輪廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #創(chuàng)建一個全透明的背景 result = np.zeros_like(image) # 在原圖區(qū)域上填充在掩碼中不透明的區(qū)域 for contour in contours: cv2.drawContours(result, [contour], -1, (255, 255, 255, 255), thickness=cv2.FILLED) # 在原圖上畫出輪廓線 # 獲取圖像的尺寸 original_height, original_width = image.shape[:2] #為輸出中間圖計算縮放比例 display_size = get_display_size() scale_width = display_size[0] / original_width scale_height = display_size[1] / original_height scale = min(scale_width, scale_height) # 計算新的尺寸 new_width = int(original_width * scale) new_height = int(original_height * scale) # 為輸出中間圖而調(diào)整圖像大小 image_contours = image.copy() cv2.drawContours(image_contours, contours, -1, (0, 255, 0, 255), thickness=2) # 綠色的輪廓線 resized_image = cv2.resize(image_contours, (new_width, new_height), interpolation=cv2.INTER_AREA) cv2.imshow('contours', resized_image) cv2.waitKey(0) # 僅保留原圖在不透明區(qū)域(內(nèi)部)的像素 resized_resultimage = cv2.resize(result, (new_width, new_height), interpolation=cv2.INTER_AREA) cv2.imshow('resized_resultimage', resized_resultimage) cv2.waitKey(0) result = cv2.bitwise_and(image, result) # 將結(jié)果圖像保存為支持透明度的文件格式(如PNG) cv2.imwrite(output_path, result) # 使用示例 if __name__=='__main__': input_file = "Your_ForGround_Image_Path.png" output_file = "You_Output_transparent_Img_Path.png" extract_contour_from_jpg(input_file, output_file)
通過上面的代碼,我們可以得到前景圖中的物體,并且使得物體周邊的像素成為透明。下面的示意圖是在PPT中操作的,僅僅是為了顯示這個產(chǎn)品圖已經(jīng)成功取出,在圖的背后添加了藍色。
我們看看細節(jié)。是不是無法忍受這些毛刺?
所以,通過“cv2.bitwise_and()”處理后得到的物體還是有點問題,我們需要對物體的周邊進行平滑處理。于是有了下面優(yōu)化后的代碼,通過“cv2.GaussianBlur()”這個函數(shù)對得到的填充后的輪廓灰度圖的邊沿進行羽化處理,就可以對于輪廓的周邊進行平滑處理。
1.2 通過cv2.GaussianBlur()函數(shù)處理輪廓后的改善
我們直接上代碼。
import cv2 import numpy as np import tkinter as tk def get_display_size(): root = tk.Tk() width = root.winfo_screenwidth() height = root.winfo_screenheight() root.quit() return width, height def feather_object_contour(image_path, feather_size=10): # 讀取圖像 image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) # 如果是 JPG 圖像,轉(zhuǎn)換為 BGRA 格式以支持透明度 / Convert JPG image to BGRA format to support transparency if image.shape[2] == 3: # Assuming BGR without alpha image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA) # 獲取圖像的尺寸 / Get the dimensions of the image original_height, original_width = image.shape[:2] # 計算縮放比例 / Calculate scale ratio display_size = get_display_size() scale_width = display_size[0] / original_width scale_height = display_size[1] / original_height scale = min(scale_width, scale_height) # 計算新的尺寸 / Calculate new dimensions new_width = int(original_width * scale) new_height = int(original_height * scale) # 轉(zhuǎn)換為灰度圖像 / Convert to grayscale gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 二值化以識別物體 / Binarize to identify objects # 應(yīng)用閾值,選擇較高的閾值來分離掉白色背景 / Apply threshold to separate white background _, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV) # 查找物體的輪廓 / Find contours of the object contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 創(chuàng)建一個新圖像用于繪制輪廓 / Create a new image for drawing contours contour_mask = np.zeros_like(gray) # 在輪廓區(qū)域上填充 / Fill in the contour area for contour in contours: cv2.drawContours(contour_mask, [contour], -1, (255, 255, 255, 255), thickness=cv2.FILLED) # 對輪廓進行高斯模糊處理 / Apply Gaussian blur to the contour smoothed_mask = cv2.GaussianBlur(contour_mask, (feather_size, feather_size), 0) resized_image = cv2.resize(smoothed_mask, (new_width, new_height), interpolation=cv2.INTER_AREA) cv2.imshow('feathered_mask', resized_image) cv2.waitKey(0) # 確保圖像具有透明通道 / Ensure the image has an alpha channel if image.shape[2] == 3: image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA) # 應(yīng)用羽化到透明通道 / Apply feathering to the alpha channel # 基于羽化輪廓掩模設(shè)置透明度 / Set alpha based on feathered contour mask smoothed_alpha = (smoothed_mask.astype(np.float32) / 255.0) alpha_channel = (255 * smoothed_alpha).astype(np.uint8) image[..., 3] = alpha_channel return image # 使用示例 feathered_image=feather_object_contour('Your_ForGround_Image_Path.png',feather_size=5) cv2.imshow('Blurred Img', feathered_image) cv2.waitKey(0) cv2.imwrite('You_Output_transparent_Img_Path.png', feathered_image)
下面是效果圖,可以和上面的圖進行比較。
2. 圖片疊加:前景圖和背景圖的疊加處理
既然有了上面PPT的效果,為什么還要用程序?qū)崿F(xiàn)?
不如多問一句:如果類似的圖處理有很多呢?
把這個取出的前景圖按照比例進行縮放,移動,旋轉(zhuǎn),得到新的前景圖,再通過前景圖中的像素的透明度來控制兩個圖的疊加效果。但是要增加投影的話,實際上需要第三個圖,也即投影圖,并且投影的圖中也需要一個柔和的過渡。
代碼中設(shè)置了前景圖和背景圖的比例關(guān)系,避免了合成之后前景圖尺寸的不一致。
合成圖片的代碼如下,效果圖后隨。
import cv2 import numpy as np """ 要在圖像合成中使用羽化(或模糊)處理前景圖的透明邊緣附近,以便在背景圖上生成一個朝指定方向的陰影效果,可以通過以下步驟實現(xiàn)。 這個過程通常涉及到對前景圖的透明度(alpha 通道)進行處理,然后將其偏移并疊加到背景圖上。 """ def create_shadow_effect(image, feather_size=10, shadow_offset=(10,10), shadow_color=(0, 0, 0, 128)): # 創(chuàng)建透明度通道的副本 alpha = image[:, :, 3] # 使用高斯模糊處理 alpha 通道,以實現(xiàn)邊緣羽化 feathered_alpha = cv2.GaussianBlur(alpha, (0, 0), sigmaX=feather_size, sigmaY=feather_size) #創(chuàng)建一個純色圖層用于陰影效果 shadow_layer = np.zeros_like(image) shadow_layer[:, :, :3] = shadow_color[:3] shadow_layer[:, :, 3] = feathered_alpha * (shadow_color[3] / 255.0) # 創(chuàng)建空白圖像作為陰影圖層 shadow = np.zeros_like(image) x_offset, y_offset = shadow_offset # 調(diào)整大小以適應(yīng)原圖像的邊界 y_bound = min(shadow_layer.shape[0], shadow.shape[0] - y_offset) x_bound = min(shadow_layer.shape[1], shadow.shape[1] - x_offset) # 復(fù)制陰影到陰影圖層中 shadow[y_offset:y_offset + y_bound, x_offset:x_offset + x_bound] = shadow_layer[:y_bound, :x_bound] return shadow # 融合三個圖 def blend_images(background, foreground, shadow): for y in range(shadow.shape[0]): for x in range(shadow.shape[1]): # Blend shadow into the background alpha_shadow = shadow[y, x, 3] / 255.0 background[y, x, :3] = (shadow[y, x, :3] * alpha_shadow + background[y, x, :3] * (1 - alpha_shadow)) for y in range(foreground.shape[0]): for x in range(foreground.shape[1]): # Blend foreground into the background alpha_fg = foreground[y, x, 3] / 255.0 background[y, x, :3] = (foreground[y, x, :3] * alpha_fg + background[y, x, :3] * (1 - alpha_fg)) # 旋轉(zhuǎn),縮放以及融合 def rotate_and_overlay(background_path, overlay_path, output_path, angle=30, feather_size=10): # 檢查前景圖有無透明層通道:創(chuàng)建一個帶有Alpha通道的 bg_image = cv2.imread(background_path, cv2.IMREAD_UNCHANGED) if bg_image.shape[2] == 3: bg_image = cv2.cvtColor(bg_image, cv2.COLOR_BGR2BGRA) # 檢查背景圖有無透明層通道:創(chuàng)建一個帶有Alpha通道的 fg_image = cv2.imread(overlay_path, cv2.IMREAD_UNCHANGED) if fg_image.shape[2] == 3: fg_image = cv2.cvtColor(fg_image, cv2.COLOR_BGR2BGRA) # 前景圖尺寸需要縮放 original_height, original_width = fg_image.shape[:2] scale = 650.0/870.0 # 計算新的尺寸 fg_new_width = int(original_width * scale) fg_new_height = int(original_height * scale) # 新尺寸的前景圖 fg_image = cv2.resize(fg_image, (fg_new_width, fg_new_height), interpolation=cv2.INTER_AREA) # 獲取前景圖像的中心 fg_center = (fg_image.shape[1] // 2, fg_image.shape[0] // 2) # 創(chuàng)建旋轉(zhuǎn)矩陣(順時針旋轉(zhuǎn)30度) rot_matrix = cv2.getRotationMatrix2D(fg_center, angle, 1.0) # 計算旋轉(zhuǎn)后的圖像維度 cos = np.abs(rot_matrix[0, 0]) sin = np.abs(rot_matrix[0, 1]) new_width = int((fg_image.shape[0] * sin) + (fg_image.shape[1] * cos)) new_height = int((fg_image.shape[0] * cos) + (fg_image.shape[1] * sin)) # 調(diào)整旋轉(zhuǎn)矩陣的移動量 rot_matrix[0, 2] += (new_width / 2) - fg_center[0] rot_matrix[1, 2] += (new_height / 2) - fg_center[1] # 旋轉(zhuǎn)前景圖像 rotated_fg = cv2.warpAffine(fg_image, rot_matrix, (new_width, new_height), borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0)) # 創(chuàng)建陰影效果圖像 shadow = create_shadow_effect(rotated_fg, feather_size=feather_size) # 將陰影和前景圖疊加到背景圖上 x_offset = (bg_image.shape[1] - shadow.shape[1]) // 2 y_offset = (bg_image.shape[0] - shadow.shape[0]) // 2 blend_images(bg_image[y_offset:y_offset + shadow.shape[0], x_offset:x_offset + shadow.shape[1]], rotated_fg, shadow) # 保存結(jié)果圖像 cv2.imwrite(output_path, bg_image) # 調(diào)用示例 backGround_ImgPath='Your_backGround_ImgPath.png' forGround_ImgPath = 'Your_forGround_ImgPath.png' output_ImgPath = 'Your_output_ImgPath.png' rotate_and_overlay(backGround_ImgPath, forGround_ImgPath, output_ImgPath, feather_size=5)
測試代碼的圖片疊加效果如下。
局部看起來還可以。甚至可以看到這個傳感器芯片的角度和前一個有一個轉(zhuǎn)角。是的,這里把前景圖整體轉(zhuǎn)動了一個指定的角度。
投影的效果看起來也比較自然。
上面就是小編要批量處理圖片的工具了,圖片量大的話,把代碼調(diào)整修改之后,就可以對文件夾中的所有圖片進行處理了,效率超過PhotoShop是沒有問題的。
-
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62605 -
OpenCV
+關(guān)注
關(guān)注
31文章
635瀏覽量
41347 -
python
+關(guān)注
關(guān)注
56文章
4797瀏覽量
84682
原文標題:用Python處理圖片:摳物體輪廓圖、透明化、圖片疊加以及圖中前景物體陰影的生成
文章出處:【微信號:安費諾傳感器學堂,微信公眾號:安費諾傳感器學堂】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論