数据集增广 之 多个图片贴到一张图上,以及生成相应的json文件

数据集增广 之 多个图片贴到一张图上,以及生成相应的json文件,第1张

数据集增广 之 多个图片贴到一张图上,以及生成相应的json文件

多图合成一图:

import os
import tqdm
import glob
import json
import math
import random
import numpy as np
from pathlib import Path
from PIL import Image, ImageDraw


foresuffix = '_foreground.jpg'
pseudosuffix = '_pseudo.jpg'
jsonsuffix = '.jpg.json'


# 旋转图片,没有黑边
def rotateImage(img):
    image = np.asarray(img)
    rotated = np.rot90(image)   # 逆时针旋转!
    '''
    # 通道特殊处理
    # Image 通道是 “RGB”
    # numpy 通道是 “BGR”
    r, g, b = rotated[:, :, 0], rotated[:, :, 1], rotated[:, :, 2]
    h, w = r.shape
    new = np.zeros((h, w, 3))
    new[:, :, 0] = r
    new[:, :, 1] = g
    new[:, :, 2] = b
    '''
    newimage = Image.fromarray(np.uint8(rotated))
    return newimage


# 规划拼接区域
def splitArea(count, width, height):
    if count == 2:
        # 左右拼接
        if width >= height:
            #########
            #   A   #
            #########
            #   B   #
            #########
            w, h = width // 2, height
            x1, y = w // 2, h // 2
            x2 = x1 * 3
            boxlist = [(x1, y, w, h), (x2, y, w, h)]
        # 上下拼接
        else:
            #############
            #  A  #  B  #
            #############
            w, h = width, height//2
            x, y1 = w // 2, h // 2
            y2 = y1 * 3
            boxlist = [(x, y1, w, h), (x, y2, w, h)]

    elif count == 3:
        if width >= height:
            #############
            #     #  B  #
            #  A  #######
            #     #  C  #
            #############
            w, h1, h2 = width // 2, height, height // 2
            x1, y1, y2 = w // 2, h1 // 2, h2 // 2
            x2 = x1 * 3
            y3 = y2 * 3
            boxlist = [(x1, y1, w, h1), (x2, y2, w, h2), (x2, y3, w, h2)]
        # 上下拼接
        else:
            #############
            #     A     #
            #############
            #  B  #  C  #
            #############
            w1, w2, h = width, width // 2, height // 2
            x1, x2, y1 = w1 // 2, w2 // 2, h // 2
            y2 = y1 * 3
            x3 = x2 * 3
            boxlist = [(x1, y1, w1, h), (x2, y2, w2, h), (x3, y2, w2, h)]

    elif count == 4:
        #############
        #  A  #  C  #
        #############
        #  B  #  D  #
        #############
        w, h = width//2, height//2
        x1, y1 = w // 2, h // 2
        x2, y2 = x1 * 3, y1 * 3
        boxlist = [(x1, y1, w, h), (x1, y2, w, h), (x2, y1, w, h), (x2, y2, w, h)]

    else:
        pass

    return boxlist


# 旋转坐标点
def processPoints(pointList, boundingbox, lx, ly, fw, fh, w, h):
    pointlist = []
    offsetx, offsety = boundingbox[0], boundingbox[1]
    for point in pointList:
        # 把衣服框从原图中抠出来
        x, y = point[0]-offsetx, point[1]-offsety

        # 旋转
        """
        点point1绕点point2旋转angle后的点
        ======================================
        在平面坐标上,任意点P(x1,y1),绕一个坐标点Q(x2,y2)旋转θ角度后,新的坐标设为(x, y)的计算公式:
        x= (x1 - x2)*cos(θ) - (y1 - y2)*sin(θ) + y2 ;
        y= (x1 - x2)*sin(θ) + (y1 - y2)*cos(θ) + x2 ;
        ======================================
        将图像坐标(x,y)转换到平面坐标(x`,y`):
        x`=x
        y`=height-y
        :param point1:
        :param point2: base point (基点)
        :param angle: 旋转角度,正:表示逆时针,负:表示顺时
        """
        tfw, tfh = fw, fh
        if (w >= h and fw < fh) or (w < h and fw >= fh):
            x1, y1 = x, y
            rotx, roty = tfw // 2, tfh // 2
            # # 将图像坐标转换到平面坐标
            y1 = tfh - y1
            roty = tfh - roty
            # 逆时针
            x = (x1 - rotx) * math.cos(math.pi/2) - (y1 - roty) * math.sin(math.pi/2) + roty
            y = (x1 - rotx) * math.sin(math.pi/2) + (y1 - roty) * math.cos(math.pi/2) + rotx
            # 旋转之后,长宽对调,将平面坐标转换到图像坐标
            tfw, tfh = tfh, tfw
            y = tfh - y

        # 缩放
        ratiox, ratioy = w / tfw, h / tfh
        x *= ratiox
        y *= ratioy

        # # 拼接到新图的新区域
        x += lx
        y += ly
        pointlist.append([int(x), int(y)])

    return pointlist


# 拼接
def joint(baseimage, forelist, imagesavepath, savejsonpath=None, threshold=50, debug=False):
    # 获取基底图片尺寸
    base = Image.open(baseimage)
    if debug:
        print(f"baseimage mode is :{base.mode}")
        base.show()

    # 设置拼接坐标
    count = len(forelist)
    assert count >= 2, "ERROR: the count of foregrounds is not enough !"
    assert count <= 4, "ERROR: the count of foregrounds is too many !"
    width, height = base.size
    boxlist = splitArea(count, width, height)

    if savejsonpath is not None:
        # 拼接原图,从基底图片中截取一小部分进行放大,作为新的图片的背景
        box=(0, 0, threshold, threshold)
        background = base.crop(box)
        background = background.resize((width, height))
    else:
        # 拼接pseudo 和 foreground,直接生成纯黑背景
        background = Image.new("RGB", (width, height), "#000000")
    if debug:
        background.show()

    # 拼接前景图片
    jsonlist = []
    for idx, (fore, box) in enumerate(zip(forelist, boxlist)):
        fimg = Image.open(fore)
        boundingbox = fimg.getbbox()
        fimg = fimg.crop(boundingbox)
        if debug:
            print(fore)
            fimg.show()

        # 获取拼接区域
        cx, cy, w, h = box
        fw, fh = fimg.size
        if (w >= h and fw < fh) or (w < h and fw >= fh):
            fimg = rotateImage(fimg)
            if debug:
                print(fore, "旋转了")
                fimg.show()
        fimg = fimg.resize((w, h))
        if debug:
            fimg.show()

        # 获取mask
        mask, _, _ = fimg.split()
        mask = np.array(mask)
        mask[mask > 10] = 255
        mask[mask <= 10] = 0
        mask = Image.fromarray(mask)

        # 拼接
        lx, ly = cx-w//2, cy-h//2
        rx, ry = cx+w//2, cy+h//2
        background.paste(fimg, (lx, ly, rx, ry), mask)

        # 修改该json文件
        if savejsonpath is not None:
            readpath = fore.replace(foresuffix, jsonsuffix)
            with open(readpath, 'r') as reader:
                jsonfile = json.load(reader)

            # 如果只有一件衣服的图片中存在多个标注连通域,则警告
            if len(jsonfile) > 1 or len(jsonfile) == 0:
                with open('./runningLog.json', 'a') as writer:
                    info = f"WARNING: {fore}n"
                    writer.write(info)

            # 遍历每个标注区域
            for object in jsonfile:
                savejson = dict()
                savejson['name'] = object['name']
                savejson['color'] = object['color']
                savejson['labelIdx'] = idx + 1
                savejson['points'] = processPoints(object['points'], boundingbox, lx, ly, fw, fh, w, h)
                jsonlist.append(savejson)

    # 保存图片及json文件
    background.save(imagesavepath)
    if len(jsonlist) > 0:
        with open(savejsonpath, 'w') as writer:
            json.dump(jsonlist, writer)

    if debug:
        background.show()



if __name__ == '__main__':
    imageroot = './hx_clothes_1122/hx_clothes_imgs'
    maskroot = './hx_clothes_1122/hx_clothes_masks'

    idx = 0
    for _ in tqdm.tqdm(range(4)):
        imagelist = os.listdir(imageroot)
        while len(imagelist) > 1:
            idx += 1
            if len(imagelist) < 4:
                count = len(imagelist)          # 如果剩余图片不足4张,则都拼一块
            else:
                count = random.randint(2, 4)    # 随机产生【2,4】闭区间上的一个整数

            forelist = []
            psedolist = []
            jsonlist = []
            imglist = []
            for i in range(count):
                img = random.choice(imagelist)

                imgpath = os.path.join(imageroot, img)
                imglist.append(imgpath)

                forename = img.replace('.jpg', foresuffix)
                forepath = os.path.join(maskroot, forename)
                forelist.append(forepath)

                pseudoname = img.replace('.jpg', pseudosuffix)
                pseudopath = os.path.join(maskroot, pseudoname)
                psedolist.append(pseudopath)

                jsonname = img.replace('.jpg', jsonsuffix)
                jsonpath = os.path.join(maskroot, jsonname)
                jsonlist.append(jsonpath)

                imagelist.remove(img)

            saveimagename = f"hx_clothes_1122/joint_images/aug_{idx}.jpg"
            savejsonname = f"hx_clothes_1122/joint_masks/aug_{idx}.jpg.json"
            saveforename = f"hx_clothes_1122/joint_masks/aug_{idx}_foreground.jpg"
            savepseudoname = f"hx_clothes_1122/joint_masks/aug_{idx}_pseudo.jpg"
            joint(imglist[0], forelist, saveimagename, savejsonname)        # , debug=True
            joint(forelist[0], forelist, saveforename)
            joint(psedolist[0], psedolist, savepseudoname)

            with open('./runningLog.json', 'a') as writer:
                info = f"{json.dumps(imglist)}n===> {str(idx)}nn"
                writer.write(info)

            # break
            print(f"r剩余:{len(imagelist)}", end='', flush=True)
            # break

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5651125.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存