Update preprocess_onepair.py
Browse files- preprocess_onepair.py +36 -107
preprocess_onepair.py
CHANGED
|
@@ -1,136 +1,65 @@
|
|
| 1 |
# preprocess_onepair.py
|
| 2 |
-
# Build a one-pair VITON-like dataset from a user photo + garment.
|
| 3 |
-
# Creates: /<root>/test/{image,cloth,edge,image-parse,pose,warp_feat} + pairs.txt
|
| 4 |
-
#
|
| 5 |
-
# NOTES:
|
| 6 |
-
# - Cloth edge is made with rembg (background removal).
|
| 7 |
-
# - Human mask uses MediaPipe Selfie Segmentation (coarse but works).
|
| 8 |
-
# - pose, image-parse, warp_feat are stubbed for now (zeros/placeholders).
|
| 9 |
-
# You can replace them later with SCHP/OpenPose/PF-AFN for higher quality.
|
| 10 |
-
|
| 11 |
from __future__ import annotations
|
| 12 |
-
import os
|
| 13 |
-
import io
|
| 14 |
-
import shutil
|
| 15 |
from pathlib import Path
|
| 16 |
-
from typing import
|
| 17 |
-
|
| 18 |
import numpy as np
|
| 19 |
from PIL import Image
|
| 20 |
import cv2
|
| 21 |
-
|
| 22 |
-
# Background removal for cloth
|
| 23 |
from rembg import remove
|
| 24 |
-
|
| 25 |
-
# Coarse human mask (person vs background)
|
| 26 |
import mediapipe as mp
|
| 27 |
-
_mp_seg = mp.solutions.selfie_segmentation.SelfieSegmentation(model_selection=1)
|
| 28 |
|
|
|
|
| 29 |
|
| 30 |
def _to_pil(img) -> Image.Image:
|
| 31 |
-
if isinstance(img, Image.Image):
|
| 32 |
-
|
| 33 |
-
if isinstance(img,
|
| 34 |
-
return Image.open(img).convert("RGB")
|
| 35 |
-
if isinstance(img, bytes):
|
| 36 |
-
return Image.open(io.BytesIO(img)).convert("RGB")
|
| 37 |
raise TypeError("Unsupported image type")
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
im2 = im.resize((nw, nh), Image.BICUBIC)
|
| 47 |
-
canvas = Image.new("RGB", (tw, th), (255, 255, 255))
|
| 48 |
-
canvas.paste(im2, ((tw - nw)//2, (th - nh)//2))
|
| 49 |
return canvas
|
| 50 |
|
| 51 |
-
|
| 52 |
def _cloth_edge(garment_rgb: Image.Image) -> Image.Image:
|
| 53 |
-
"""Remove background → alpha → binary edge (white cloth on black)."""
|
| 54 |
arr = np.array(garment_rgb)
|
| 55 |
-
cut = remove(arr)
|
| 56 |
-
if cut.shape[2]
|
| 57 |
-
|
| 58 |
-
else:
|
| 59 |
-
alpha = np.ones(arr.shape[:2], dtype=np.uint8) * 255
|
| 60 |
-
|
| 61 |
-
# Edge is "white where cloth exists"
|
| 62 |
-
edge = np.zeros_like(alpha, dtype=np.uint8)
|
| 63 |
-
edge[alpha > 10] = 255
|
| 64 |
return Image.fromarray(edge)
|
| 65 |
|
| 66 |
-
|
| 67 |
def _human_mask(human_rgb: Image.Image) -> Image.Image:
|
| 68 |
-
"""Coarse person mask via MediaPipe (returns white=person, black=bg)."""
|
| 69 |
arr = np.array(human_rgb)
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
res = _mp_seg.process(rgb)
|
| 73 |
-
mask = (res.segmentation_mask > 0.5).astype(np.uint8) * 255
|
| 74 |
return Image.fromarray(mask)
|
| 75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
) -> str:
|
| 82 |
-
"""
|
| 83 |
-
Build one-pair dataset under <root>/test and return dataroot as string.
|
| 84 |
-
"""
|
| 85 |
-
root = Path(root)
|
| 86 |
-
test_root = root / "test"
|
| 87 |
-
|
| 88 |
-
# Clean slate
|
| 89 |
-
if test_root.exists():
|
| 90 |
-
shutil.rmtree(test_root)
|
| 91 |
-
(test_root / "image").mkdir(parents=True, exist_ok=True)
|
| 92 |
-
(test_root / "cloth").mkdir(parents=True, exist_ok=True)
|
| 93 |
-
(test_root / "edge").mkdir(parents=True, exist_ok=True)
|
| 94 |
-
(test_root / "image-parse").mkdir(parents=True, exist_ok=True)
|
| 95 |
-
(test_root / "pose").mkdir(parents=True, exist_ok=True)
|
| 96 |
-
(test_root / "warp_feat").mkdir(parents=True, exist_ok=True)
|
| 97 |
-
|
| 98 |
-
# Normalize to 512×512 canvases
|
| 99 |
-
person_pil = _resize_pad(_to_pil(person_img), (512, 512))
|
| 100 |
-
garment_pil = _resize_pad(_to_pil(garment_img), (512, 512))
|
| 101 |
-
|
| 102 |
-
# Save base images
|
| 103 |
-
person_name = "user_0001.jpg"
|
| 104 |
-
cloth_name = "cloth_0001.jpg"
|
| 105 |
-
(test_root / "image" / person_name).parent.mkdir(parents=True, exist_ok=True)
|
| 106 |
-
(test_root / "cloth" / cloth_name).parent.mkdir(parents=True, exist_ok=True)
|
| 107 |
-
|
| 108 |
-
person_pil.save(test_root / "image" / person_name, quality=95)
|
| 109 |
-
garment_pil.save(test_root / "cloth" / cloth_name, quality=95)
|
| 110 |
-
|
| 111 |
-
# Cloth edge (white cloth mask on black)
|
| 112 |
-
edge_pil = _cloth_edge(garment_pil)
|
| 113 |
-
edge_pil = edge_pil.convert("L").resize((512, 512), Image.NEAREST)
|
| 114 |
-
edge_pil.save(test_root / "edge" / cloth_name.replace(".jpg", ".png"))
|
| 115 |
-
|
| 116 |
-
# Human parse (stub = coarse person mask)
|
| 117 |
-
# Most CP datasets expect a PNG with label IDs; we give a soft body mask
|
| 118 |
-
# where torso is expected to be > 0; downstream code will use it as inpaint mask.
|
| 119 |
-
parse_pil = _human_mask(person_pil).convert("L")
|
| 120 |
-
parse_pil.save(test_root / "image-parse" / person_name.replace(".jpg", ".png"))
|
| 121 |
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
dummy_pose = np.zeros((512, 512, 3), dtype=np.uint8)
|
| 125 |
-
Image.fromarray(dummy_pose).save(test_root / "pose" / person_name.replace(".jpg", "_keypoints.png"))
|
| 126 |
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
wfeat = np.zeros((256, 256, 3), dtype=np.uint8)
|
| 130 |
-
Image.fromarray(wfeat).save(test_root / "warp_feat" / f"{person_name[:-4]}_{cloth_name[:-4]}.png")
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
|
|
|
| 135 |
|
|
|
|
| 136 |
return str(root)
|
|
|
|
| 1 |
# preprocess_onepair.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from __future__ import annotations
|
| 3 |
+
import io, os, shutil
|
|
|
|
|
|
|
| 4 |
from pathlib import Path
|
| 5 |
+
from typing import Tuple
|
|
|
|
| 6 |
import numpy as np
|
| 7 |
from PIL import Image
|
| 8 |
import cv2
|
|
|
|
|
|
|
| 9 |
from rembg import remove
|
|
|
|
|
|
|
| 10 |
import mediapipe as mp
|
|
|
|
| 11 |
|
| 12 |
+
_mp_seg = mp.solutions.selfie_segmentation.SelfieSegmentation(model_selection=1)
|
| 13 |
|
| 14 |
def _to_pil(img) -> Image.Image:
|
| 15 |
+
if isinstance(img, Image.Image): return img
|
| 16 |
+
if isinstance(img, (str, os.PathLike)): return Image.open(img).convert("RGB")
|
| 17 |
+
if isinstance(img, bytes): return Image.open(io.BytesIO(img)).convert("RGB")
|
|
|
|
|
|
|
|
|
|
| 18 |
raise TypeError("Unsupported image type")
|
| 19 |
|
| 20 |
+
def _resize_pad(im: Image.Image, size: Tuple[int,int]=(512,512)) -> Image.Image:
|
| 21 |
+
w,h = im.size; tw,th = size
|
| 22 |
+
scale = min(tw/w, th/h)
|
| 23 |
+
nw,nh = int(w*scale), int(h*scale)
|
| 24 |
+
im2 = im.resize((nw,nh), Image.BICUBIC)
|
| 25 |
+
canvas = Image.new("RGB",(tw,th),(255,255,255))
|
| 26 |
+
canvas.paste(im2,((tw-nw)//2,(th-nh)//2))
|
|
|
|
|
|
|
|
|
|
| 27 |
return canvas
|
| 28 |
|
|
|
|
| 29 |
def _cloth_edge(garment_rgb: Image.Image) -> Image.Image:
|
|
|
|
| 30 |
arr = np.array(garment_rgb)
|
| 31 |
+
cut = remove(arr)
|
| 32 |
+
alpha = cut[:,:,3] if cut.shape[2]==4 else np.ones(arr.shape[:2],dtype=np.uint8)*255
|
| 33 |
+
edge = np.zeros_like(alpha,dtype=np.uint8); edge[alpha>10]=255
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
return Image.fromarray(edge)
|
| 35 |
|
|
|
|
| 36 |
def _human_mask(human_rgb: Image.Image) -> Image.Image:
|
|
|
|
| 37 |
arr = np.array(human_rgb)
|
| 38 |
+
res = _mp_seg.process(cv2.cvtColor(arr, cv2.COLOR_RGB2BGR))
|
| 39 |
+
mask = (res.segmentation_mask>0.5).astype(np.uint8)*255
|
|
|
|
|
|
|
| 40 |
return Image.fromarray(mask)
|
| 41 |
|
| 42 |
+
def build_temp_dataset(person_img, garment_img, root: Path|str) -> str:
|
| 43 |
+
root = Path(root); test_root = root/"test"
|
| 44 |
+
if test_root.exists(): shutil.rmtree(test_root)
|
| 45 |
+
for sub in ["image","cloth","edge","image-parse","pose","warp_feat"]:
|
| 46 |
+
(test_root/sub).mkdir(parents=True,exist_ok=True)
|
| 47 |
|
| 48 |
+
person_pil=_resize_pad(_to_pil(person_img)); garment_pil=_resize_pad(_to_pil(garment_img))
|
| 49 |
+
person_name="user_0001.jpg"; cloth_name="cloth_0001.jpg"
|
| 50 |
+
person_pil.save(test_root/"image"/person_name,quality=95)
|
| 51 |
+
garment_pil.save(test_root/"cloth"/cloth_name,quality=95)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
edge_pil=_cloth_edge(garment_pil).convert("L").resize((512,512),Image.NEAREST)
|
| 54 |
+
edge_pil.save(test_root/"edge"/cloth_name.replace(".jpg",".png"))
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
parse_pil=_human_mask(person_pil).convert("L")
|
| 57 |
+
parse_pil.save(test_root/"image-parse"/person_name.replace(".jpg",".png"))
|
|
|
|
|
|
|
| 58 |
|
| 59 |
+
dummy=np.zeros((512,512,3),dtype=np.uint8)
|
| 60 |
+
Image.fromarray(dummy).save(test_root/"pose"/person_name.replace(".jpg","_keypoints.png"))
|
| 61 |
+
feat=np.zeros((256,256,3),dtype=np.uint8)
|
| 62 |
+
Image.fromarray(feat).save(test_root/"warp_feat"/f"{person_name[:-4]}_{cloth_name[:-4]}.png")
|
| 63 |
|
| 64 |
+
with open(test_root/"pairs.txt","w") as f: f.write(f"{person_name} {cloth_name}\n")
|
| 65 |
return str(root)
|