多图合成一图:
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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)