0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用Python+OpenCV处理图片

安费诺传感器学堂 来源:安费诺传感器学堂 2024-12-23 15:54 次阅读

如果给你一张图片作为背景,另外一张图片中的物体作为前景图,要把前景图中的物体叠加布置到背景图的中间位置,并且前景图中的物体需要在背景图中有旋转和投影,怎么处理?

如果是一两张图,PS应该是首选,但是如果是批量的呢?这就是小编要准备处理的一个小问题。挑战一下,用Python+OpenCV来实现这个小目标。

提出问题和解决思路

作为前景的图片的背景是白色不透明的,不过这就有条件实现抠图了

前景图的尺寸甚至比背景图大,要缩小并且要算出位置

如果图片没有透明层来控制,则需要转换并添加透明层

叠加的前景图需要可以控制旋转角度,但旋转同时也影响前景图的尺寸

抠图进行叠加效果不好怎么办?要进行羽化模糊化处理

为了让图片的叠加更加自然,还要考虑让前景物体在背景上投个影

代码验证

1. 抠图,取出前景物体的两种处理方式比较

为了看到中间过程,代码中添加了加轮廓线和掩码图两个中间图的输出。

1.1 通过cv2.bitwise_and()函数处理轮廓掩码

下面首先使用cv2.bitwise_and()函数处理。掩码图是有透明度控制层的,每个像素,在轮廓线外的的像素值是(B, G, R, A)=(0,0,0,0),轮廓线内的像素值是(B, G, R, A)=(255,255,255,255)。这种情况下,前景图中任何在轮廓线外的像素和掩码图对应位置的掩码像素进行按位“与”操作时,都会变成透明‘人’,而轮廓线内部的原图像素则会保留原值。

这里OpenCV中的一个函数是个关键。注意阈值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 图像,转换为 BGRA 格式以支持透明度
    if image.shape[2] == 3:  # Assuming BGR without alpha
        image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
    # 转换为灰度图像
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)


    # 应用阈值操作以找到物体
_,thresh=cv2.threshold(gray,240,255,cv2.THRESH_BINARY_INV)
    
    # 找到轮廓
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)


#创建一个全透明的背景
    result = np.zeros_like(image)


    # 在原图区域上填充在掩码中不透明的区域
    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)
    
    # 为输出中间图而调整图像大小
    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)
    
    # 仅保留原图在不透明区域(内部)的像素
    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)
    
    # 将结果图像保存为支持透明度的文件格式(如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中操作的,仅仅是为了显示这个产品图已经成功取出,在图的背后添加了蓝色。

我们看看细节。是不是无法忍受这些毛刺?

所以,通过“cv2.bitwise_and()”处理后得到的物体还是有点问题,我们需要对物体的周边进行平滑处理。于是有了下面优化后的代码,通过“cv2.GaussianBlur()”这个函数对得到的填充后的轮廓灰度图的边沿进行羽化处理,就可以对于轮廓的周边进行平滑处理。

1.2 通过cv2.GaussianBlur()函数处理轮廓后的改善

我们直接上代码。

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 图像,转换为 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)
    
    # 转换为灰度图像 / Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # 二值化以识别物体 / Binarize to identify objects
    # 应用阈值,选择较高的阈值来分离掉白色背景 / 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)


    # 创建一个新图像用于绘制轮廓 / Create a new image for drawing contours
    contour_mask = np.zeros_like(gray)


    # 在轮廓区域上填充 / 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)
    
    # 应用羽化到透明通道 / Apply feathering to the alpha channel
    # 基于羽化轮廓掩模设置透明度 / 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的效果,为什么还要用程序实现?

不如多问一句:如果类似的图处理有很多呢?

把这个取出的前景图按照比例进行缩放,移动,旋转,得到新的前景图,再通过前景图中的像素的透明度来控制两个图的叠加效果。但是要增加投影的话,实际上需要第三个图,也即投影图,并且投影的图中也需要一个柔和的过渡。

代码中设置了前景图和背景图的比例关系,避免了合成之后前景图尺寸的不一致。

合成图片的代码如下,效果图后随。

import cv2
import numpy as np


"""
要在图像合成中使用羽化(或模糊)处理前景图的透明边缘附近,以便在背景图上生成一个朝指定方向的阴影效果,可以通过以下步骤实现。
这个过程通常涉及到对前景图的透明度(alpha 通道)进行处理,然后将其偏移并叠加到背景图上。
"""
def create_shadow_effect(image, feather_size=10, shadow_offset=(10,10), shadow_color=(0, 0, 0, 128)):
    # 创建透明度通道的副本
    alpha = image[:, :, 3]


    # 使用高斯模糊处理 alpha 通道,以实现边缘羽化
    feathered_alpha = cv2.GaussianBlur(alpha, (0, 0), sigmaX=feather_size, sigmaY=feather_size)
    
#创建一个纯色图层用于阴影效果
    shadow_layer = np.zeros_like(image)
    shadow_layer[:, :, :3] = shadow_color[:3]
    shadow_layer[:, :, 3] = feathered_alpha * (shadow_color[3] / 255.0)


    # 创建空白图像作为阴影图层
    shadow = np.zeros_like(image)
    x_offset, y_offset = shadow_offset


    # 调整大小以适应原图像的边界
    y_bound = min(shadow_layer.shape[0], shadow.shape[0] - y_offset)
    x_bound = min(shadow_layer.shape[1], shadow.shape[1] - x_offset)


    # 复制阴影到阴影图层中
    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))
# 旋转,缩放以及融合            
def rotate_and_overlay(background_path, overlay_path, output_path, angle=30, feather_size=10):
    # 检查前景图有无透明层通道:创建一个带有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)
    # 检查背景图有无透明层通道:创建一个带有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)


    # 创建旋转矩阵(顺时针旋转30度)
    rot_matrix = cv2.getRotationMatrix2D(fg_center, angle, 1.0)


    # 计算旋转后的图像维度
    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))


    # 调整旋转矩阵的移动量
    rot_matrix[0, 2] += (new_width / 2) - fg_center[0]
    rot_matrix[1, 2] += (new_height / 2) - fg_center[1]


    # 旋转前景图像
    rotated_fg = cv2.warpAffine(fg_image, rot_matrix, (new_width, new_height), 
                                borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0))


    # 创建阴影效果图像
    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)


    # 保存结果图像
    cv2.imwrite(output_path, bg_image)


# 调用示例
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)

测试代码的图片叠加效果如下。

局部看起来还可以。甚至可以看到这个传感器芯片的角度和前一个有一个转角。是的,这里把前景图整体转动了一个指定的角度。

投影的效果看起来也比较自然。

上面就是小编要批量处理图片的工具了,图片量大的话,把代码调整修改之后,就可以对文件夹中的所有图片进行处理了,效率超过PhotoShop是没有问题的。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4329

    浏览量

    62576
  • OpenCV
    +关注

    关注

    31

    文章

    635

    浏览量

    41340
  • python
    +关注

    关注

    56

    文章

    4793

    浏览量

    84634

原文标题:用Python处理图片:抠物体轮廓图、透明化、图片叠加以及图中前景物体阴影的生成

文章出处:【微信号:安费诺传感器学堂,微信公众号:安费诺传感器学堂】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    【Raspberry Pi 3试用体验】+Opencv+python的人脸识别

    ;)看一下效果:光线不好还是能认出来,说明opencv自带的分类器算开源里面不错的了~参考:Tigerboard开发板试用体验 python+opencv的人脸识别 NanoPi2试用体验 简单人脸识别-结项
    发表于 05-13 21:38

    基于机器学习库opencv和平台Jupyter Notebook的车牌识别案例

    python+opencv实现车牌识别
    发表于 02-28 11:08

    【Toybrick RK3399Pro AI开发板试用体验】ACT Ⅵ:Linux下静态人脸识别 python+opencv

    程序,只要点击run即可,也不会报错。看了网上很多教程,python+opencv,在有深度训练源的情况下,7行代码就能实现,那我们现在就来试一试。首先,我们要找一个训练过的人脸的源,看了下大神的代码,我
    发表于 08-10 10:50

    LabVIEW+Python+openCV

    的,动态链接库也需要配置,还需要知道各个参数含义和类型,这里提供另外一种方法,那就是PythonopenCV工具包,绝对开源和免费,LabVIEW可以利用Python节点调用Python
    发表于 12-07 20:59

    OpenCV-Python-Toturial-中文版

    python编写opencv的入门资料,介绍了python的各个函数的应用
    发表于 03-23 14:55 0次下载

    Python下使用OpenCV的技巧教程与典型应用案例

    Python下使用OpenCV教程,本篇将介绍和深度学习数据处理阶段最相关的基础使用,并完成4个有趣实用的小例子: - 延时摄影小程序 - 视频中截屏采样的小程序 - 图片数据增加(
    发表于 11-15 18:43 1.2w次阅读

    python图像处理opencv步骤是怎么样的

    越来越觉得python是一强大的工具,处理样本确实不错。最近因项目需要涉及到图片处理,所以开始用python调用
    发表于 12-04 15:29 4410次阅读

    OpenCV中的Python实现

    类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。 OpenCV用C++语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言
    的头像 发表于 08-25 15:55 2071次阅读

    使用opencvpython进行智能火灾检测

    电子发烧友网站提供《使用opencvpython进行智能火灾检测.zip》资料免费下载
    发表于 11-02 15:08 0次下载
    使用<b class='flag-5'>opencv</b>和<b class='flag-5'>python</b>进行智能火灾检测

    opencv读入图片注意事项详解1

    深度学习数据预处理中常用opencv读入图片,一般在`__getitem__`函数中调用。本文主要介绍opencv读取图片的一些细节以及注意
    的头像 发表于 02-07 16:06 622次阅读

    opencv读入图片注意事项详解 2

    深度学习数据预处理中常用opencv读入图片,一般在`__getitem__`函数中调用。本文主要介绍opencv读取图片的一些细节以及注意
    的头像 发表于 02-07 16:06 663次阅读

    如何使用Python+opencv进行图像处理

    图像是 Web 应用中除文字外最普遍的媒体格式。流行的 Web 静态图片有 JPEG、PNG、ICO、BMP 等。动态图片主要是 GIF 格式。 为了节省图片传输流量,大型互联网公司还会定制特殊格式的
    的头像 发表于 02-08 10:57 919次阅读
    如何使用<b class='flag-5'>Python+opencv</b>进行图像<b class='flag-5'>处理</b>

    OpenCV 如何加载图片

    实现一遍。 OpenCV 的内容挺多的,而且不使用的话,容易忘记,所以通过制造工具的方式来给自己加深印象,平常要处理图片的时候,就可以用自己的工具直接处理,不需要每次都去重复的写代码。
    的头像 发表于 10-09 15:01 715次阅读

    opencv-pythonopencv一样吗

    不一样。OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,它提供了大量的图像和视频处理功能。OpenCV-Python
    的头像 发表于 07-16 10:38 1162次阅读

    一个月速成python+OpenCV图像处理

    OpenCV是一个广受欢迎且极为流行的计算机视觉库,它因其强大的功能、灵活性和开源特性而在开发者和研究者中备受青睐。学习OpenCV主要就是学习里面的计算机视觉算法。要学习这些算法的原理,知道它们
    的头像 发表于 11-29 18:27 128次阅读
    一个月速成<b class='flag-5'>python+OpenCV</b>图像<b class='flag-5'>处理</b>