如果給你一張圖片作為背景,另外一張圖片中的物體作為前景圖,要把前景圖中的物體疊加布置到背景圖的中間位置,并且前景圖中的物體需要在背景圖中有旋轉(zhuǎn)和投影,怎么處理?
如果是一兩張圖,PS應(yīng)該是首選,但是如果是批量的呢?這就是小編要準(zhǔn)備處理的一個小問題。挑戰(zhàn)一下,用Python+OpenCV來實(shí)現(xiàn)這個小目標(biāo)。
提出問題和解決思路
作為前景的圖片的背景是白色不透明的,不過這就有條件實(shí)現(xiàn)摳圖了
前景圖的尺寸甚至比背景圖大,要縮小并且要算出位置
如果圖片沒有透明層來控制,則需要轉(zhuǎn)換并添加透明層
疊加的前景圖需要可以控制旋轉(zhuǎn)角度,但旋轉(zhuǎn)同時也影響前景圖的尺寸
摳圖進(jìn)行疊加效果不好怎么辦?要進(jìn)行羽化模糊化處理
為了讓圖片的疊加更加自然,還要考慮讓前景物體在背景上投個影
代碼驗(yà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)位置的掩碼像素進(jìn)行按位“與”操作時,都會變成透明‘人’,而輪廓線內(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]
#為輸出中間圖計(jì)算縮放比例
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)
# 計(jì)算新的尺寸
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)成功取出,在圖的背后添加了藍(lán)色。
我們看看細(xì)節(jié)。是不是無法忍受這些毛刺?
所以,通過“cv2.bitwise_and()”處理后得到的物體還是有點(diǎn)問題,我們需要對物體的周邊進(jìn)行平滑處理。于是有了下面優(yōu)化后的代碼,通過“cv2.GaussianBlur()”這個函數(shù)對得到的填充后的輪廓灰度圖的邊沿進(jìn)行羽化處理,就可以對于輪廓的周邊進(jìn)行平滑處理。
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]
# 計(jì)算縮放比例 / 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)
# 計(jì)算新的尺寸 / 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)
# 對輪廓進(jìn)行高斯模糊處理 / 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)
下面是效果圖,可以和上面的圖進(jìn)行比較。
2. 圖片疊加:前景圖和背景圖的疊加處理
既然有了上面PPT的效果,為什么還要用程序?qū)崿F(xiàn)?
不如多問一句:如果類似的圖處理有很多呢?
把這個取出的前景圖按照比例進(jìn)行縮放,移動,旋轉(zhuǎn),得到新的前景圖,再通過前景圖中的像素的透明度來控制兩個圖的疊加效果。但是要增加投影的話,實(shí)際上需要第三個圖,也即投影圖,并且投影的圖中也需要一個柔和的過渡。
代碼中設(shè)置了前景圖和背景圖的比例關(guān)系,避免了合成之后前景圖尺寸的不一致。
合成圖片的代碼如下,效果圖后隨。
import cv2
import numpy as np
"""
要在圖像合成中使用羽化(或模糊)處理前景圖的透明邊緣附近,以便在背景圖上生成一個朝指定方向的陰影效果,可以通過以下步驟實(shí)現(xiàn)。
這個過程通常涉及到對前景圖的透明度(alpha 通道)進(jìn)行處理,然后將其偏移并疊加到背景圖上。
"""
def create_shadow_effect(image, feather_size=10, shadow_offset=(10,10), shadow_color=(0, 0, 0, 128)):
# 創(chuàng)建透明度通道的副本
alpha = image[:, :, 3]
# 使用高斯模糊處理 alpha 通道,以實(shí)現(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
# 計(jì)算新的尺寸
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)
# 計(jì)算旋轉(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)整修改之后,就可以對文件夾中的所有圖片進(jìn)行處理了,效率超過PhotoShop是沒有問題的。
-
函數(shù)
+關(guān)注
關(guān)注
3文章
4405瀏覽量
66795 -
OpenCV
+關(guān)注
關(guān)注
33文章
650瀏覽量
44381 -
python
+關(guān)注
關(guān)注
57文章
4856瀏覽量
89529
原文標(biāo)題:用Python處理圖片:摳物體輪廓圖、透明化、圖片疊加以及圖中前景物體陰影的生成
文章出處:【微信號:安費(fèi)諾傳感器學(xué)堂,微信公眾號:安費(fèi)諾傳感器學(xué)堂】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
【Raspberry Pi 3試用體驗(yàn)】+Opencv+python的人臉識別
基于機(jī)器學(xué)習(xí)庫opencv和平臺Jupyter Notebook的車牌識別案例
【Toybrick RK3399Pro AI開發(fā)板試用體驗(yàn)】ACT Ⅵ:Linux下靜態(tài)人臉識別 python+opencv
LabVIEW+Python+openCV
OpenCV-Python-Toturial-中文版
在Python下使用OpenCV的技巧教程與典型應(yīng)用案例
python圖像處理opencv步驟是怎么樣的
OpenCV中的Python實(shí)現(xiàn)
使用opencv和python進(jìn)行智能火災(zāi)檢測
opencv讀入圖片注意事項(xiàng)詳解1
opencv讀入圖片注意事項(xiàng)詳解 2
如何使用Python+opencv進(jìn)行圖像處理
OpenCV 如何加載圖片
opencv-python和opencv一樣嗎
一個月速成python+OpenCV圖像處理

使用Python+OpenCV處理圖片
評論