「图像 merge」无中生有制造数据

news2024/11/25 19:35:29

在进行一个新项目的时候,往往缺少一些真实数据,导致没办法进行模型训练,这时候就需要算法工程师自行制作一些数据了,比如这篇文章分享的 bag 目标检测,在检测区域没有真实的 bag数据

此时,就可以采用图像拼接的方式将凑集到的 bag图像粘贴到场景图像中【前提,目标图一般为“大头贴”】,当然场景图片并不是所有的位置都可以粘贴,一般有特定区域,比如 地面、墙壁、某设备等,因此还需要采用标注工具将这些目标区域标注出来,算法通过读取对应的目标区域,随机设定区域内的坐标点进行粘贴,将目标图粘贴到场景图当中

当然并不是任何形式的目标图都可以粘贴,为了更好的融合场景当中,还需要将目标从 大头贴当中给 扣出来,即除了目标区域,其他区域设置为透明格式,这样拼接的效果才“更”加真实一些【还是很假的!!!】

具体的操作过程如何所示,代码附最后,如有 bug 还望见谅!

1、制作底片模版

博主采用的 LabelImg标注工具,将底片待粘贴区域标注好

标注完成的 txt 内容
在这里插入图片描述

labelImg 的显示界面
在这里插入图片描述


2、将对应的大头贴目标头图处理

从 jpg 图像将目标 “扣” 出来,此处采用的像素抠图,将白色区域设置为透明,此处并不通用,不同目标的小伙伴自行修改代码,png的图像格式可以保存 alpha通道

在这里插入图片描述


#  !/usr/bin/env  python
#  -*- coding:utf-8 -*-
# @Time   :  2023.11
# @Author :  绿色羽毛
# @Email  :  lvseyumao@foxmail.com
# @Blog   :  https://blog.csdn.net/ViatorSun
# @Note   :  jpg2png.py



import os
import cv2
import numpy as np
import os.path as osp



file_path = "/media/yinzhe/DataYZ/DataSet/DataSet/bag_masknew"
save_path = file_path + "_out"

if not osp.exists(save_path):
    os.makedirs(save_path)

img_lst = []
for path, dirs, files in os.walk(file_path):
    for file in files:
        if os.path.splitext(file)[1] in ['.jpg', ".png", ".JPG", ".jpeg"]:  # 扫描指定格式文件
            img_dir = osp.join(path, file)
            img_save = osp.join(save_path, file)
            img = cv2.imread(img_dir)
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)                               # 转换为RGB格式
            png_img = osp.join(save_path, file.split(".")[0] + ".png")

            white_mask = cv2.inRange(img, (200, 200, 200), (255, 255, 255))  	# 提取白色部分
            img = cv2.cvtColor(img, cv2.COLOR_RGB2BGRA)

            img[:,:,3][white_mask == 255] = 0                       			# 将白色部分变成透明

            cv2.imwrite(png_img, img)


3、将底片与透明目标进行叠加融合

叠加融合的方法有很多种,此处只采用了最简单的叠加方式,也可以考虑 cv2.seamlessClone
最终成果如下图所示

在这里插入图片描述

除了生成合成图片,标注信息也同步生成

在这里插入图片描述

注意! 此处的标注信息是按照标注框尺寸保存的【此处隐藏bug后续优化】
提示:可以根据目标区域的非透明区域进行保存

在这里插入图片描述

#  !/usr/bin/env  python
#  -*- coding:utf-8 -*-
# @Time   :  2023.10
# @Author :  绿色羽毛
# @Email  :  lvseyumao@foxmail.com
# @Blog   :  https://blog.csdn.net/ViatorSun
# @Note   :  ps_merge_img.py



import os
import cv2
import random
from random import sample
import numpy as np
import argparse  




def read_label_txt(label_dir):
    labels = []
    with open(label_dir) as fp:
        for f in fp.readlines():
            labels.append(f.strip().split(' '))
    return labels

def rescale_yolo_labels(labels, img_shape):
    height, width, nchannel = img_shape
    rescale_boxes = []
    for box in list(labels):
        x_c = float(box[1]) * width
        y_c = float(box[2]) * height
        w = float(box[3]) * width
        h = float(box[4]) * height
        x_left = x_c - w * .5
        y_left = y_c - h * .5
        x_right = x_c + w * .5
        y_right = y_c + h * .5
        rescale_boxes.append([box[0], int(x_left), int(y_left), int(x_right), int(y_right)])
    return rescale_boxes

def xyxy2xywh(image, bboxes):
    height, width, _ = image.shape
    boxes = []
    for box in bboxes:
        if len(box) < 4:
            continue
        cls = int(box[0])
        x_min = box[1]
        y_min = box[2]
        x_max = box[3]
        y_max = box[4]
        w = x_max - x_min
        h = y_max - y_min
        x_c = (x_min + x_max) / 2.0
        y_c = (y_min + y_max) / 2.0
        x_c = x_c / width
        y_c = y_c / height
        w = float(w) / width
        h = float(h) / height
        boxes.append([cls, x_c, y_c, w, h])
    return boxes

def cast_color(img, value):
    img_t = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
    h,s,v = cv2.split(img_t)
    # 增加图像对比度
    v2 = np.clip(cv2.add(2*v,value),0,255)
    img2 = np.uint8(cv2.merge((h,s,v2)))
    img_cast = cv2.cvtColor(img2,cv2.COLOR_HSV2BGR)             # 改变图像对比度
    return img_cast

def brightness(img, value):
    img_t = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
    h,s,v = cv2.split(img_t)
    # 增加图像亮度
    v1 = np.clip(cv2.add(1*v,value),0,255)
    img1 = np.uint8(cv2.merge((h,s,v1)))
    img_brightness = cv2.cvtColor(img1,cv2.COLOR_HSV2BGR)       # 改变图像亮度亮度
    return img_brightness


def add_alpha_channel(img):
    """ 为jpg图像添加alpha通道 """

    b_channel, g_channel, r_channel = cv2.split(img)  				# 拆分jpg图像通道
    alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255  # 创建Alpha通道

    img_new = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))  # 融合通道
    return img_new


def merge_img(jpg_img, png_img, y1, y2, x1, x2):
    """ 将png透明图像与jpg图像叠加
        y1,y2,x1,x2为叠加位置坐标值
    """

    # 判断jpg图像是否已经为4通道
    if jpg_img.shape[2] == 3:
        jpg_img = add_alpha_channel(jpg_img)

    '''
    当叠加图像时,可能因为叠加位置设置不当,导致png图像的边界超过背景jpg图像,而程序报错
    这里设定一系列叠加位置的限制,可以满足png图像超出jpg图像范围时,依然可以正常叠加
    '''
    yy1 = 0
    yy2 = png_img.shape[0]
    xx1 = 0
    xx2 = png_img.shape[1]

    if x1 < 0:
        xx1 = -x1
        x1 = 0
    if y1 < 0:
        yy1 = - y1
        y1 = 0
    if x2 > jpg_img.shape[1]:
        xx2 = png_img.shape[1] - (x2 - jpg_img.shape[1])
        x2 = jpg_img.shape[1]
    if y2 > jpg_img.shape[0]:
        yy2 = png_img.shape[0] - (y2 - jpg_img.shape[0])
        y2 = jpg_img.shape[0]

    # 获取要覆盖图像的alpha值,将像素值除以255,使值保持在0-1之间
    alpha_png = png_img[yy1:yy2, xx1:xx2, 3] / 255.0
    alpha_jpg = 1 - alpha_png

    # 开始叠加
    for c in range(0, 3):
        jpg_img[y1:y2, x1:x2, c] = ((alpha_jpg * jpg_img[y1:y2, x1:x2, c]) + (alpha_png * png_img[yy1:yy2, xx1:xx2, c]))

    return jpg_img


def random_add_patches_on_objects(image, mask_lst, rescale_boxes, paste_number):

    img = image.copy()
    new_bboxes = []
    cl = 0

    random.shuffle(rescale_boxes)

    for i, rescale_bbox in enumerate(rescale_boxes[:int(len(mask_lst))]):     # 待ps图像 目标框中

        p_img = mask_lst[i]
        bbox_h, bbox_w, bbox_c = p_img.shape

        obj_xmin, obj_ymin, obj_xmax, obj_ymax = rescale_bbox[1:]
        obj_w = obj_xmax - obj_xmin + 1         # 目标框尺寸
        obj_h = obj_ymax - obj_ymin + 1

        while not (bbox_w < obj_w and bbox_h < obj_h):                  # 如果目标框小于 mask尺寸,对mask进行缩放以确保可以放进 bbox中
            new_bbox_w = int(bbox_w * random.uniform(0.5, 0.8))
            new_bbox_h = int(bbox_h * random.uniform(0.5, 0.8))
            bbox_w, bbox_h = new_bbox_w, new_bbox_h

        success_num = 0
        while success_num < paste_number:
            center_search_space = [obj_xmin, obj_ymin, obj_xmax - new_bbox_w - 1, obj_ymax - new_bbox_h - 1] # 选取生成随机点区域
            if center_search_space[0] >= center_search_space[2] or center_search_space[1] >= center_search_space[3]:
                print('============== center_search_space error!!!! ================')
                success_num += 1
                continue

            new_bbox_x_min = random.randint(center_search_space[0], center_search_space[2])  # 随机生成点坐标
            new_bbox_y_min = random.randint(center_search_space[1], center_search_space[3])
            new_bbox_x_left, new_bbox_y_top, new_bbox_x_right, new_bbox_y_bottom = new_bbox_x_min, new_bbox_y_min, new_bbox_x_min + new_bbox_w - 1, new_bbox_y_min + new_bbox_h - 1
            new_bbox = [cl, int(new_bbox_x_left), int(new_bbox_y_top), int(new_bbox_x_right), int(new_bbox_y_bottom)]
            success_num += 1
            new_bboxes.append(new_bbox)

            p_img = cv2.resize(p_img, (new_bbox_w, new_bbox_h))

            img = merge_img(img, p_img, new_bbox_y_top, new_bbox_y_bottom+1, new_bbox_x_left, new_bbox_x_right+1)


    return img, new_bboxes




if __name__ == "__main__":
    # 用来装载参数的容器
    parser = argparse.ArgumentParser(description='PS')
    # 给这个解析对象添加命令行参数
    parser.add_argument('-i', '--images', default= '/media/yinzhe/DataYZ/DataSet/DataSet/bag_model',type=str, help='path of images')
    parser.add_argument('-t', '--mask', default= '/media/yinzhe/DataYZ/DataSet/DataSet/bag_mask',type=str, help='path of masks')
    parser.add_argument('-s', '--saveImage',default= '/media/yinzhe/DataYZ/DataSet/DataSet/bag_save', type=str, help='path of ')
    parser.add_argument('-c', '--scale', default= 0.2, type=float, help='number of img')
    parser.add_argument('-n', '--num', default= 5, type=int, help='number of img')

    args = parser.parse_args()  # 获取所有参数

    mask_filedirs = args.mask
    images_path = args.images
    save_path = args.saveImage
    scale, num = args.scale, args.num
    mask_paths = []

    if not os.path.exists(save_path):
        os.makedirs(save_path)


    # 读取所有的 mask 模版
    mask_lst = []
    for t_path in os.listdir(mask_filedirs):
        mask = cv2.imread(os.path.join(mask_filedirs, t_path), cv2.IMREAD_UNCHANGED)
        if (mask.shape[2] != 4):  # RGB alpha
            break
        mask_lst.append(mask)

    # template_paths = random.shuffle(template_paths) #打乱顺序
    for image_path in os.listdir(images_path) :
        if "txt" in image_path:
            continue

        image = cv2.imread(os.path.join(images_path, image_path))
        pre_name = image_path.split('.')[0]
        bbox_lst = read_label_txt(os.path.join(images_path, pre_name + ".txt"))

        if image is None or len(bbox_lst) == 0:
            print("empty image !!! or empty label !!!")
            continue

        # yolo txt转化为x1y1x2y2
        rescale_bboxes = rescale_yolo_labels(bbox_lst, image.shape)  # 转换坐标表示
        # maskes_path = sample(mask_paths, int(len(bbox_lst) * scale))

        #
        for i in range(num):
            maskes = sample(mask_lst, int(len(bbox_lst) * scale))
            img, bboxes = random_add_patches_on_objects(image, maskes, rescale_bboxes, 1)
            boxes = xyxy2xywh(img, bboxes)
            img_name = pre_name + '_' + str(i) + '.jpg'

            print('handle img:', img_name)
            cv2.imwrite(os.path.join(save_path, img_name), img)

            with open(os.path.join(save_path, img_name[:-4] + ".txt"), 'a') as f:
                for box in boxes:

                    mess = str(3) + " " + str(box[1]) + " " + str(box[2]) + " " + str(box[3] * 0.6) + " " + str(box[4]* 0.6) + "\n"
                    f.write(mess)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1161275.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CHS零壹视频恢复程序换机加载扫描结果的方法

有些特殊情况下我们需要把当前机器上扫描的结果在另外一台机器上加载&#xff0c;这样可以避免多次扫描浪费时间。目前CHS零壹视频恢复程序监控版、专业版、高级版已经支持换机加载&#xff0c;下面这个教程演示了如何换机加载。 STEP1&#xff1a;选择扫描对象点击扫描->根…

4.3 抽象类和接口

思维导图: 4.3 抽象类和接口的笔记 4.3.1 抽象类 定义&#xff1a;当一个类需要定义成员方法描述其行为特征&#xff0c;但这些方法的实现方式无法确定时&#xff0c;Java提供了抽象类来满足这种需求。 特点&#xff1a; 抽象方法是使用abstract关键字修饰的方法&#xff0c;无…

Spring-创建非懒加载的单例Bean源码

补充&#xff1a;关于扫描的逻辑 /*** Scan the class path for candidate components.* param basePackage the package to check for annotated classes* return a corresponding Set of autodetected bean definitions*/ public Set<BeanDefinition> findCandidateCo…

【C++】无重复数字全排列(三种方法)和有重复数字全排列

文章目录 一、无重复数字排列1.1 题目描述1.2 用dfs方法1.2.1 思路分析1.2.2 代码编写 1.3 用交换法1.4 STL秒解1.4.1 所用函数1.4.2 代码编写 二、有重复数字排列2.1 思路分析2.2 代码编写 一、无重复数字排列 1.1 题目描述 把 1 ∼ n 1∼n 1∼n 这 n n n 个整数排成一行后…

Xray+awvs联动扫描

首先xray开启监听 xray_windows_amd64.exe webscan --listen 127.0.0.1:7777 --html-output xray-xxx.html --plugins sqldet,xxe,upload,brute-force,cmd-injection,struts,thinkphp 然后准备目标csv文件,每行一个url或ip然后加个逗号 接着awvs导入csv 对导进来的每个目…

【python海洋专题三十四】调用自己的colormore

点击蓝字&#xff0c;关注我们 ​ 【python海洋专题三十四】调用自己的colormore 有时候不想使用他们给好的&#xff0c;调用自己的colormore 使用素材 ncl的colormore。 格式文本格式&#xff01; 这样自己的colormore存入txt也可以使用了。 ​ 结果呈现 单个颜色调…

网络层协议【IP协议】

全文目录 基本概念IP协议IPv4 协议头格式&#xff1a;分片发送方进行分片&#xff1a;识别IP分片&#xff1a;组装IP分片&#xff1a; 网段划分DHCP技术IP分类 私有IP和共有IP1. 私有IP地址&#xff08;Private IP Address&#xff09;&#xff1a;2. 公网IP地址&#xff08;Pu…

接入文心一言实战(一):API申请与测试

大家好&#xff0c;我是豆小匠。 这期来介绍申请百度文心一言API的步骤。 第一步 注册百度智能云账号 网址&#xff1a;https://login.bce.baidu.com/new-reg?tplbceplat&fromportal 第二步&#xff1a;申请预置模型 网址&#xff1a;https://console.bce.baidu.com/qi…

VCS与XRUN对语法支持的不同点(持续更新...)

静态方法声明位置不同&#xff1a;VCS支持声明在class内/外&#xff08;extern&#xff09;两种方式&#xff0c;XRUN只支持static function声明于类内&#xff0c;不支持类外声明&#xff08;带extern关键字&#xff09;。 字符串转二进制、8进制、十进制、16进制方法&#xf…

【算法挑战】字符串解码(含解析、源码)

394.字符串解码 https://leetcode-cn.com/problems/decode-string/ 394.字符串解码 题目描述方法 1: 递归 思路复杂度分析代码 方法 2: 循环 栈 图解复杂度分析代码 题目描述 给定一个经过编码的字符串&#xff0c;返回它解码后的字符串。编码规则为: k[encoded_string]…

VSCode 设置平滑光标

1.点击左下角的设置按钮&#xff0c;再点击设置 2.点击文本编辑器&#xff0c;点击光标&#xff0c;勾选控制是否启用平滑插入动画。 3.随便打开一个文件&#xff0c;上下左右移动光标时&#xff0c;会发现非常的流畅。 原创作者&#xff1a;吴小糖 创作时间&#xff1a;2023…

【AUTOSAR】【以太网】EthSyn

AUTOSAR专栏——总目录_嵌入式知行合一的博客-CSDN博客文章浏览阅读215次。本文主要汇总该专栏文章,以方便各位读者阅读。https://xianfan.blog.csdn.net/article/details/132072415 目录 一、概述 二、功能描述 2.1 初始化

深入了解进口跨境商城源码的电商开发的关键

随着全球电子商务的快速发展&#xff0c;进口跨境商城源码的电商开发逐渐成为一种趋势。本文将深入探讨进口跨境商城源码的电商开发的关键&#xff0c;包括需求分析、技术实现、运营推广、风险控制等方面。 一、需求分析 在进口跨境商城源码的电商开发中&#xff0c;需求分析是…

【C/C++】 常量指针、指针常量、指向常量的常指针

const修饰指针的三种情况 int main() {int a 10;int b 10;//常量指针//const修饰的是int&#xff0c;指针指向可以改&#xff0c;指针指向的值不可以更改const int * p1 &a; p1 &b; //正确//*p1 100; 报错//指针常量//const修饰的是指针&#xff0c;指针的值&am…

Appium 移动端自动化测试 —— 触摸(TouchAction) 与多点触控(MultiAction)

一、触摸 TouchAction 在所有的 Appium 客户端库里&#xff0c;TouchAction 触摸对象被创建并被赋予一连串的事件。 规范里可用的事件有&#xff1a; * 短按(press) * 释放(release) * 移动到(moveTo) * 点击(tap) * 等待(wait) * 长按(longPress) * 取消(cancel) * 执行(per…

Unity屏幕中涂鸦

LineRenderer LineRenderer是Unity中的一个组件&#xff0c;用于在场景中绘制简单的线段。 LineRenderer组件允许你通过设置一系列顶点来定义线段的形状和外观。它会根据这些顶点自动在场景中绘制出线段。 下面是LineRenderer的一些重要属性和方法&#xff1a; positionCou…

串口通信(6)应用定时器中断+串口中断实现接收一串数据

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

LICEcap使用教程

打开LICEcap&#xff0c;显示如下 点击Record&#xff0c;开始录制 点击Stop&#xff0c;停止录制 点击Record&#xff0c;进入该页面 display in animation&#xff08;在动画中显示&#xff09; title frame&#xff08;标题框&#xff09; - - - sec&#xff08;秒&…

网络安全应急响应工具(系统痕迹采集)-FireKylin

文章目录 网络安全应急响应工具(系统痕迹采集)-FireKylin1.FireKylin介绍【v1.4.0】 2021-12-20【v1.0.1】 2021-08-09 2.客户端界面Agent支持的操作系统FireKylinAgent界面使用方式比较传统方式与FireKylin比较无法可达目标的场景应用对比 3.使用教程设置语言Agent配置&#x…

Vue路由导航(replace、push、forward、back、go)

Vue路由导航&#xff08;replace、push、forward、back、go&#xff09; 先了解栈结构&#xff0c;再学习以下内容 栈的数据结构&#xff1a;先进后出&#xff0c;后进先出。原理&#xff1a;push将元素压入栈内&#xff0c;pop将元素弹出&#xff0c;栈有分别有栈底指针和栈顶…