はじめに
デザインは画面上では良く見えても印刷できない=意味がありません。この記事では、POD の最も重要な課題、つまり、デザインが実際の T シャツの構造と一致するようにする、つまりプリント ゾーン、安全なマージン、衣服を意識した配置について説明します。
1. T シャツのプリント領域
印刷可能なエリアマップ
┌─────────────────────┐
│ NECK ZONE ❌ │
│ (no print) │
├─────────────────────┤
│ │
│ ┌───────────────┐ │
│ │ FRONT CHEST │ │ ← Primary print area
│ │ 30cm × 35cm │ │ (safe zone)
│ └───────────────┘ │
│ │
│ ┌───┐ ┌───┐ │
│ │ L │ │ R │ │ ← Sleeve print
│ │ │ │ │ │ 10cm × 25cm
│ └───┘ └───┘ │
│ │
└─────────────────────┘
│ │
│ ┌───────────────┐ │
│ │ BACK PRINT │ │ ← Back print area
│ │ 35cm × 45cm │ │
│ └───────────────┘ │
└─────────────────────┘
印刷ゾーンの定義
from dataclasses import dataclass
@dataclass
class PrintZone:
name: str
width_cm: float
height_cm: float
offset_from_top_cm: float
safe_margin_cm: float
max_dpi: int = 300
PRINT_ZONES = {
"front_chest": PrintZone(
name="Front Chest",
width_cm=30, height_cm=35,
offset_from_top_cm=8, # Dưới cổ 8cm
safe_margin_cm=2,
),
"full_front": PrintZone(
name="Full Front",
width_cm=38, height_cm=50,
offset_from_top_cm=5,
safe_margin_cm=3,
),
"back_print": PrintZone(
name="Back Print",
width_cm=35, height_cm=45,
offset_from_top_cm=10,
safe_margin_cm=3,
),
"left_chest": PrintZone(
name="Left Chest (Pocket)",
width_cm=10, height_cm=10,
offset_from_top_cm=10,
safe_margin_cm=1,
),
"sleeve_left": PrintZone(
name="Left Sleeve",
width_cm=10, height_cm=25,
offset_from_top_cm=3,
safe_margin_cm=1,
),
}
2. 安全マージン検証ツール
class SafeMarginValidator:
"""Kiểm tra design có nằm trong safe zone không"""
def validate(
self,
design: Image.Image,
zone: PrintZone,
shirt_size: str = "M",
) -> ValidationResult:
issues = []
# 1. Check design không quá gần edges
content_bbox = self._get_content_bounds(design)
margin_top = content_bbox[1]
margin_left = content_bbox[0]
margin_right = design.width - content_bbox[2]
margin_bottom = design.height - content_bbox[3]
min_margin_px = self._cm_to_px(
zone.safe_margin_cm, dpi=300
)
if margin_top < min_margin_px:
issues.append({
"type": "margin_top",
"severity": "error",
"message": f"Design quá gần cổ áo ({margin_top}px < {min_margin_px}px)",
"auto_fix": True,
})
if margin_left < min_margin_px or margin_right < min_margin_px:
issues.append({
"type": "margin_side",
"severity": "warning",
"message": "Design có thể bị cắt ở mép áo",
"auto_fix": True,
})
# 2. Check centering
center_offset = self._check_center_alignment(
design, content_bbox
)
if center_offset > 5: # > 5% offset
issues.append({
"type": "off_center",
"severity": "warning",
"message": f"Design lệch tâm {center_offset}%",
"auto_fix": True,
})
# 3. Check resolution
design_dpi = self._calculate_effective_dpi(
design, zone
)
if design_dpi < 150:
issues.append({
"type": "low_resolution",
"severity": "error",
"message": f"DPI quá thấp ({design_dpi}). Tối thiểu 150 DPI cho in",
"auto_fix": True, # Có thể upscale
})
elif design_dpi < 300:
issues.append({
"type": "medium_resolution",
"severity": "info",
"message": f"DPI {design_dpi} — đạt yêu cầu nhưng 300 DPI tối ưu hơn",
"auto_fix": True,
})
return ValidationResult(
is_valid=not any(i["severity"] == "error" for i in issues),
issues=issues,
effective_dpi=design_dpi,
)
def auto_fix(
self, design: Image.Image, issues: list[dict]
) -> Image.Image:
"""Tự động fix các vấn đề layout"""
fixed = design.copy()
for issue in issues:
if not issue.get("auto_fix"):
continue
if issue["type"] == "off_center":
fixed = self._center_design(fixed)
elif issue["type"] in ("margin_top", "margin_side"):
fixed = self._add_margins(fixed)
elif issue["type"] in ("low_resolution", "medium_resolution"):
fixed = self._upscale(fixed, target_dpi=300)
return fixed
3. AI を使用した衣服を意識した配置
class GarmentAwarePlacer:
"""Đặt design lên áo thun đúng vị trí"""
def __init__(self):
# Segmentation model cho áo thun
self.segmenter = self._load_garment_segmenter()
def place_design_on_garment(
self,
design: Image.Image,
garment_image: Image.Image,
zone: str = "front_chest",
) -> Image.Image:
"""
Composite design lên ảnh áo thun thật
1. Segment vùng áo (loại background)
2. Detect print zone trên áo
3. Warp design theo perspective áo
4. Blend tự nhiên
"""
# Step 1: Segment garment
garment_mask = self.segmenter.segment(garment_image)
# Step 2: Find print zone on garment
zone_bbox = self._detect_print_zone(
garment_image, garment_mask, zone
)
# Step 3: Perspective transform
warped_design = self._perspective_warp(
design, zone_bbox
)
# Step 4: Blend
result = self._alpha_blend(
garment_image, warped_design, zone_bbox
)
return result
def _perspective_warp(
self,
design: Image.Image,
target_quad: list[tuple],
) -> Image.Image:
"""Warp design theo perspective của áo"""
import cv2
import numpy as np
src_points = np.float32([
[0, 0],
[design.width, 0],
[design.width, design.height],
[0, design.height],
])
dst_points = np.float32(target_quad)
matrix = cv2.getPerspectiveTransform(src_points, dst_points)
warped = cv2.warpPerspective(
np.array(design),
matrix,
(design.width, design.height),
)
return Image.fromarray(warped)
4. 印刷用の品質ゲート
class PrintQualityGate:
"""Tất cả design PHẢI pass quality gate trước khi cho phép order"""
GATES = [
("resolution", "DPI >= 150"),
("color_space", "Không có màu ngoài gamut CMYK"),
("margins", "Safe margins đủ"),
("centering", "Design centered ± 5%"),
("file_size", "File < 50MB"),
("transparency", "Transparent background hoặc solid"),
]
def check_all(self, design: Image.Image) -> QualityReport:
results = {}
for gate_name, description in self.GATES:
checker = getattr(self, f"_check_{gate_name}")
passed, detail = checker(design)
results[gate_name] = {
"passed": passed,
"description": description,
"detail": detail,
}
all_passed = all(r["passed"] for r in results.values())
return QualityReport(
passed=all_passed,
gates=results,
)
概要
Print-Ready AI は次のことを保証します。
- プリント ゾーン — T シャツの正確なプリント ゾーンを理解する
- 安全マージン — マージンを超えた設計を自動検証および自動修正します
- 衣服を意識した配置 — 遠近法ワープ、自然なブレンド
- 品質ゲート — DPI、色域、センタリング — すべてを通過する必要があります
次の記事: 自動スケーリング デザイン — ユーザーがシャツのサイズや形状を変更したときに、スマートにサイズ変更します。