Tạo ra một hình ảnh thì dễ, nhưng để có được phông nền trong suốt "sạch sẽ" cho các mục đích sử dụng thực tế như làm sticker, hình đè (overlays) hay in ấn theo yêu cầu (print-on-demand) thì lại khó hơn chúng ta tưởng.
Hướng dẫn này sẽ chỉ cho bạn cách tạo ra các sticker trong suốt sẵn sàng cho sản xuất bằng cách sử dụng Gemini Interactions API. Bí quyết nằm ở đây: tạo ảnh trên nền xanh chromakey, sau đó tách nền bằng nhận diện dải màu HSV.

Quy trình thực hiện (Workflow):

  1. Tạo ảnh với nền xanh chromakey (#00FF00) bằng Gemini Pro 3 Image Preview (Nano Banana Pro).
  2. Sử dụng nhận diện không gian màu HSV để loại bỏ chính xác tất cả các sắc độ màu xanh lá.
  3. Áp dụng làm sạch hình thái học (morphological cleanup) để loại bỏ các lỗi răng cưa ở viền.
  4. Lưu file dưới dạng định dạng PNG trong suốt chuẩn.

Điều kiện tiên quyết:

  • Cài đặt các thư viện cần thiết: pip install google-genai pillow scipy
  • Thiết lập biến môi trường GEMINI_API_KEY.
  • Đã có sẵn Notebook trên GitHub.

Tại sao lại dùng Chromakey thay vì xóa nền bằng AI (ML)?

  • Xóa nền bằng ML: Cần gọi thêm một model khác để tách nền. Chậm hơn và tốn kém hơn. Chất lượng đường viền đôi khi không ổn định.
  • Chromakey + HSV: Sử dụng nền xanh lá cây truyền thống. Nhanh hơn, rẻ hơn và dễ kiểm soát kết quả hơn. Mang lại Chất lượng đường viền xuất sắc với viền trắng bao quanh.
Khi bạn có quyền kiểm soát quá trình tạo ảnh, việc yêu cầu một màu nền cụ thể luôn hiệu quả hơn là phải chạy thêm một model khác để xử lý hậu kỳ.

Thiết lập (Setup)

Đầu tiên, hãy cài đặt các thư viện phụ thuộc và thiết lập Gemini client.
Cài đặt thư viện (Bỏ chú thích nếu cần)
# !pip install google-genai pillow scipy
import io
import base64
import colorsys
from google import genai
from PIL import Image, ImageFilter, ImageMorph
import numpy as np

# Khởi tạo Gemini client
client = genai.Client()

# Model sử dụng để tạo ảnh
MODEL_ID = "gemini-3-pro-image-preview"

Các hàm bổ trợ (Helper Functions)

Chúng ta sẽ tạo các hàm bổ trợ sử dụng không gian màu HSV để nhận diện phông xanh (green screen) mạnh mẽ hơn, giúp bắt được mọi sắc độ của màu xanh lá.
def decode_image(base64_data: str) -> Image.Image:
    """Giải mã dữ liệu ảnh base64 sang đối tượng PIL Image."""
    image_bytes = base64.b64decode(base64_data)
    return Image.open(io.BytesIO(image_bytes))


def rgb_to_hsv_array(rgb_array: np.ndarray) -> np.ndarray:
    """Chuyển đổi mảng RGB sang mảng HSV một cách hiệu quả."""
    # Chuẩn hóa RGB về khoảng 0-1
    rgb_normalized = rgb_array.astype(np.float32) / 255.0

    r, g, b = rgb_normalized[:, :, 0], rgb_normalized[:, :, 1], rgb_normalized[:, :, 2]

    max_c = np.maximum(np.maximum(r, g), b)
    min_c = np.minimum(np.minimum(r, g), b)
    delta = max_c - min_c

    # Tính toán Hue (Sắc độ)
    h = np.zeros_like(max_c)

    # Khi max == r
    mask_r = (max_c == r) & (delta != 0)
    h[mask_r] = (60 * ((g[mask_r] - b[mask_r]) / delta[mask_r]) + 360) % 360

    # Khi max == g
    mask_g = (max_c == g) & (delta != 0)
    h[mask_g] = (60 * ((b[mask_g] - r[mask_g]) / delta[mask_g]) + 120)

    # Khi max == b
    mask_b = (max_c == b) & (delta != 0)
    h[mask_b] = (60 * ((r[mask_b] - g[mask_b]) / delta[mask_b]) + 240)

    # Tính toán Saturation (Độ bão hòa)
    s = np.zeros_like(max_c)
    s[max_c != 0] = delta[max_c != 0] / max_c[max_c != 0]

    # Value (Giá trị độ sáng) chính là max_c
    v = max_c

    return np.stack([h, s * 100, v * 100], axis=-1)


def remove_green_screen_hsv(
    image: Image.Image,
    hue_center: float = 120,    # Tâm màu xanh lá trong HSV thường là 120 độ
    hue_range: float = 25,     # Khoảng lệch sắc độ
    min_saturation: float = 75, # Độ bão hòa tối thiểu
    min_value: float = 70,      # Độ sáng tối thiểu
    dilation_iterations: int = 2,
    erosion_iterations: int = 0
) -> Image.Image:
    """
    Xóa phông xanh sử dụng không gian màu HSV để nhận diện tốt hơn.

    HSV vượt trội trong việc nhận diện dải màu vì nó tách biệt
    sắc độ (hue) khỏi độ bão hòa (saturation) và giá trị độ sáng (value).
    """
    # Chuyển sang RGBA nếu chưa đúng định dạng
    if image.mode != 'RGBA':
        image = image.convert('RGBA')

    # Chuyển sang mảng numpy
    data = np.array(image)
    rgb = data[:, :, :3]

    # Chuyển sang HSV
    hsv = rgb_to_hsv_array(rgb)
    h, s, v = hsv[:, :, 0], hsv[:, :, 1], hsv[:, :, 2]

    # Tính toán khoảng cách sắc độ (tính đến tính chất vòng tròn của màu sắc)
    hue_diff = np.abs(h - hue_center)
    hue_diff = np.minimum(hue_diff, 360 - hue_diff)

    # Tạo mask (mặt nạ) cho các điểm ảnh màu xanh
    # Là màu xanh nếu: sắc độ nằm trong khoảng VÀ độ bão hòa đủ cao VÀ độ sáng đủ cao
    green_mask = (
        (hue_diff < hue_range) &
        (s > min_saturation) &
        (v > min_value)
    )

    # Áp dụng làm sạch hình thái học để loại bỏ lỗi răng cưa ở viền
    if dilation_iterations > 0 or erosion_iterations > 0:
        from scipy import ndimage

        # Giãn nở (Dilate) mask để bắt các điểm ảnh bị mờ ở viền (anti-aliased)
        if dilation_iterations > 0:
            green_mask = ndimage.binary_dilation(green_mask, iterations=dilation_iterations)

        # Tùy chọn xói mòn (Erode) lại (giúp loại bỏ nhiễu cô lập)
        if erosion_iterations > 0:
            green_mask = ndimage.binary_erosion(green_mask, iterations=erosion_iterations)

    # Gán độ trong suốt cho các điểm ảnh màu xanh
    alpha = data[:, :, 3].copy()
    alpha[green_mask] = 0
    data[:, :, 3] = alpha

    return Image.fromarray(data)


def remove_green_screen_aggressive(
    image: Image.Image,
    green_threshold: float = 1.2,
    edge_pixels: int = 0  # Đặt là 0 để tránh lấn vào viền trắng của sticker
) -> Image.Image:
    """
    Xóa xanh kiểu "mạnh tay": nhận diện bất kỳ điểm ảnh nào có màu xanh chiếm ưu thế.

    Cách này bắt được cả các màu xanh đậm/nhạt hơn, hoặc bóng đổ có ám xanh.
    """
    if image.mode != 'RGBA':
        image = image.convert('RGBA')

    data = np.array(image)
    r, g, b = data[:, :, 0].astype(float), data[:, :, 1].astype(float), data[:, :, 2].astype(float)

    # Một pixel được coi là "xanh" nếu kênh Green vượt trội đáng kể so với Red và Blue
    rb_max = np.maximum(r, b) + 1  # +1 để tránh lỗi chia cho 0
    green_ratio = g / rb_max

    # Kiểm tra thêm xem Green có phải là kênh màu chủ đạo không
    green_dominant = (g > r) & (g > b)

    # Kết hợp các điều kiện mặt nạ
    green_mask = (green_ratio > green_threshold) & green_dominant

    # Mở rộng mặt nạ để bắt các điểm ảnh ở rìa
    if edge_pixels > 0:
        from scipy import ndimage
        green_mask = ndimage.binary_dilation(green_mask, iterations=edge_pixels)

    # Áp dụng độ trong suốt
    alpha = data[:, :, 3].copy()
    alpha[green_mask] = 0
    data[:, :, 3] = alpha

    return Image.fromarray(data)


def cleanup_edges(image: Image.Image, threshold: int = 128) -> Image.Image:
    """
    Làm sạch các điểm ảnh bán trong suốt ở viền.

    Giúp loại bỏ hiệu ứng "quầng" (halo) từ các cạnh được khử răng cưa.
    """
    if image.mode != 'RGBA':
        return image

    data = np.array(image)
    alpha = data[:, :, 3]

    # Biến các điểm ảnh bán trong suốt thành trong suốt hoàn toàn hoặc đặc hoàn toàn
    alpha[alpha < threshold] = 0
    alpha[alpha >= threshold] = 255

    data[:, :, 3] = alpha
    return Image.fromarray(data)


def save_transparent_png(image: Image.Image, filename: str):
    """Lưu ảnh dưới dạng PNG và giữ nguyên độ trong suốt."""
    if image.mode != 'RGBA':
        image = image.convert('RGBA')
    image.save(filename, 'PNG')
    print(f"✅ Đã lưu: {filename}")

Tạo Sticker với phông xanh Chromakey

Chìa khóa ở đây là hướng dẫn Gemini tạo ảnh với nền xanh lá chuẩn (chromakey). Chúng ta sử dụng các câu lệnh (prompt) cụ thể để đảm bảo đường viền sạch sẽ và không bị hiện tượng "ám xanh" (green spill) vào chủ thể.
def load_image_as_content(image_path: str) -> dict:
    """
    Tải ảnh từ đường dẫn tệp và trả về dưới dạng khối nội dung (content block) cho API.
    """
    import os
    import mimetypes
    
    # Xác định loại mime từ phần mở rộng tệp
    mime_type, _ = mimetypes.guess_type(image_path)
    if mime_type is None:
        # Mặc định là JPEG nếu không xác định được
        mime_type = "image/jpeg"
    
    # Đọc và mã hóa ảnh sang base64
    with open(image_path, "rb") as f:
        image_data = base64.b64encode(f.read()).decode("utf-8")
    
    return {
        "type": "image",
        "data": image_data,
        "mime_type": mime_type
    }
 
 
def generate_sticker(
    prompt: str, 
    aspect_ratio: str = "1:1",
    image_size: str = "2K",
    input_images: list[str] | None = None
) -> Image.Image:
    """
    Tạo hình ảnh phong cách sticker với nền xanh chromakey.
    """
    # Prompt đã được tối ưu hóa để tách nền chromakey
    enhanced_prompt = f"""Hãy tạo một hình minh họa dạng sticker về: {prompt}
 
CÁC YÊU CẦU QUAN TRỌNG VỀ CHROMAKEY:
1. NỀN (BACKGROUND): Màu xanh chromakey đồng nhất, phẳng. Sử dụng CHÍNH XÁC mã màu hex #00FF00 (RGB 0, 255, 0). 
   Toàn bộ nền phải là một màu xanh thuần khiết duy nhất, KHÔNG biến đổi, KHÔNG gradient, KHÔNG đổ bóng, KHÔNG hiệu ứng ánh sáng.
 
2. VIỀN TRẮNG: Chủ thể PHẢI có một đường viền/mép màu trắng sạch sẽ (rộng 2-3 pixel) ngăn cách nó với nền xanh.
   Viền trắng này giúp ngăn hiện tượng lem màu giữa chủ thể và nền.
 
3. KHÔNG CÓ MÀU XANH TRÊN CHỦ THỂ: Bản thân chủ thể KHÔNG nên chứa bất kỳ màu xanh lá nào để tránh nhầm lẫn với phông nền.
   Nếu chủ thể bắt buộc phải có màu xanh (như lá cây), hãy sử dụng một sắc độ khác biệt rõ rệt như xanh rừng đậm hoặc xanh mòng két (teal).
 
4. CẠNH SẮC NÉT: Chủ thể cần có các cạnh sắc nét, rõ ràng - không có ranh giới mềm hoặc mờ.
 
5. CĂN GIỮA: Chủ thể nên được đặt ở chính giữa với khoảng đệm (padding) ở tất cả các phía.
 
6. PHONG CÁCH: Rực rỡ, sạch sẽ, phong cách sticker hoạt hình/minh họa với màu sắc đậm.
 
Đây là để trích xuất chromakey - nền xanh sẽ được loại bỏ bằng lập trình."""
 
    print(f"🎨 Đang tạo sticker: {prompt}")
    print(f"   Độ phân giải: {image_size}")
    
    # Xây dựng nội dung đầu vào
    # Khi có input_images, tạo danh sách các khối nội dung ảnh trước, sau đó đến văn bản
    if input_images:
        print(f"   Ảnh đầu vào: {len(input_images)} ảnh")
        input_content = []
        for img_path in input_images:
            print(f"   - Đang tải: {img_path}")
            input_content.append(load_image_as_content(img_path))
        # Thêm prompt văn bản vào khối nội dung cuối cùng
        input_content.append({"type": "text", "text": enhanced_prompt})
    else:
        # Nếu không có ảnh đầu vào, chỉ sử dụng trực tiếp prompt văn bản
        input_content = enhanced_prompt
 
    # Gọi Gemini Interactions API
    interaction = client.interactions.create(
        model=MODEL_ID,
        input=input_content,
        generation_config={
            "image_config": {
                "aspect_ratio": aspect_ratio,
                "image_size": image_size  # Sử dụng độ phân giải cao để có đường viền tốt hơn
            }
        }
    )
 
    # Trích xuất hình ảnh được tạo ra
    for output in interaction.outputs:
        if output.type == "image":
            print(f"✅ Đã tạo xong ảnh (mime_type: {output.mime_type})")
            return decode_image(output.data)

Tạo Sticker Quy trình Khép kín (End-to-End)

Hãy kết hợp tất cả lại với nhau: tạo ảnh, xóa phông xanh bằng nhận diện HSV, xử lý làm sạch chuyên sâu và lưu file.
def create_sticker(
    prompt: str, 
    output_filename: str,
    aspect_ratio: str = "1:1",
    image_size: str = "2K",
    save_raw: bool = False,
    input_images: list[str] | None = None
) -> Image.Image:
    """
    Quy trình hoàn chỉnh để tạo một sticker trong suốt.
 
    Sử dụng phương pháp tiếp cận đa bước:
    1. Tạo ảnh với prompt chromakey đã tối ưu.
    2. Xóa xanh dựa trên HSV (bắt đúng dải màu).
    3. Xóa xanh chuyên sâu (loại bỏ các sắc xanh còn sót lại).
    4. Làm sạch viền để loại bỏ hiệu ứng quầng (halo).
    """
    import os
 
    # Bước 1: Tạo hình ảnh với phông xanh
    raw_image = generate_sticker(prompt, aspect_ratio, image_size, input_images)
 
    # Tùy chọn lưu ảnh gốc để kiểm tra (debug)
    if save_raw:
        raw_filename = output_filename.replace('.png', '_raw.png')
        raw_image.save(raw_filename)
        print(f"📸 Đã lưu ảnh gốc: {raw_filename}")
 
    # Bước 2: Xóa phông xanh dựa trên HSV
    print("🔧 Bước 1: Đang xóa phông xanh bằng HSV...")
    transparent_image = remove_green_screen_hsv(
        raw_image,
        hue_center=120,       # Sắc độ xanh lá thuần túy
        hue_range=25,         # Khoảng hẹp quanh màu xanh thuần
        min_saturation=75,    # Chỉ chọn màu xanh có độ bão hòa cao (giữ lại các màu xanh khác trên logo)
        min_value=70,         # Chỉ chọn màu xanh sáng
        dilation_iterations=2,  # Bắt các điểm ảnh bị mờ (anti-aliased) ở viền
        erosion_iterations=0
    )
 
    # Bước 3: Bỏ qua xóa chuyên sâu (vô hiệu hóa - vì có thể gây đốm trên chủ thể)
    # transparent_image = remove_green_screen_aggressive(...)
 
    # Bước 4: Làm sạch các lỗi răng cưa bán trong suốt ở viền
    print("✨ Đang làm sạch các đường viền...")
    transparent_image = cleanup_edges(transparent_image, threshold=64)
 
    # Bước 5: Lưu dưới dạng PNG
    save_transparent_png(transparent_image, output_filename)
 
    return transparent_image

Tạo Sticker (Thực thi)

Hãy cùng tạo thử một vài sticker mẫu!
# Định nghĩa câu lệnh mô tả sticker
prompt = "một chú mèo con đáng yêu, hạnh phúc với đôi mắt to tròn"
 
# Gọi hàm create_sticker để thực hiện quy trình tự động
sticker1 = create_sticker(
    prompt=prompt,
    output_filename="../assets/cat.png",
    image_size="2K",     # Độ phân giải 2K giúp đường viền sắc nét hơn
    save_raw=True        # Lưu cả ảnh gốc nền xanh để đối chiếu
)
Ảnh gốc (Phông xanh):
cat_raw.png
Ảnh đã xử lý (Trong suốt):
cat.png

Tạo Sticker cá nhân hóa (Personalized Sticker)

Bây giờ, hãy thử tạo một sticker dựa trên khuôn mặt của chính bạn và logo của một thương hiệu cụ thể (ở đây là Google DeepMind). Chúng ta sẽ truyền ảnh chân dung và ảnh logo vào hàm create_sticker.
# Prompt mô tả sticker cá nhân hóa
prompt = "Một nhà phát triển mặc áo hoodie Google DeepMind, trông giống như tôi. Sử dụng các hình ảnh đính kèm của tôi và logo Google DeepMind mới."

# Danh sách ảnh đầu vào: ảnh chân dung và ảnh logo
input_images = ["../assets/headshot.png", "../assets/logo.png"]

# Gọi hàm create_sticker để thực hiện quy trình tự động
sticker2 = create_sticker(
    prompt=prompt,
    input_images=input_images,
    output_filename="../assets/developer.png",
    image_size="2K",     # Độ phân giải 2K giúp đường viền sắc nét hơn
    save_raw=True        # Lưu cả ảnh gốc nền xanh để đối chiếu
)
Ảnh gốc (Phông xanh):
developer_raw.png
Ảnh đã xử lý (Trong suốt):
developer.png

Mẹo Kỹ thuật Prompt (Prompt Engineering Tips)

  • Luôn chỉ định phong cách: Sử dụng các cụm từ như "sticker-style" (phong cách sticker) hoặc "illustration" (hình minh họa).
  • Yêu cầu đường viền rõ nét: Thêm cụm từ "clear defined edges" để việc tách nền (cutout) trở nên dễ dàng hơn.
  • Chỉ định rõ màu nền: Luôn yêu cầu màu nền cụ thể (như chromakey green #00FF00) trong câu lệnh.
  • Yêu cầu căn giữa: Đưa vào yêu cầu chủ thể phải được "centered with padding" (căn giữa và có khoảng đệm xung quanh).
  • Tránh màu xanh lá: Kỹ thuật này hoạt động hiệu quả nhất với các chủ thể không chứa màu xanh lá cây (để tránh bị xóa nhầm).

Cách sử dụng Sticker của bạn

  • Các tệp PNG được tạo ra có kênh alpha (độ trong suốt) chuẩn và có thể sử dụng trong:
  • Phần mềm thiết kế: Figma, Photoshop, Canva, v.v.
  • Công cụ thuyết trình: Google Slides, PowerPoint.
  • Ứng dụng trò chuyện: Telegram, WhatsApp, Zalo.
  • Dịch vụ in ấn theo yêu cầu (Print-on-demand): Làm áo thun, cốc sứ, sticker dán vật lý.
  • Ứng dụng di động: Chèn vào story Instagram hoặc làm tài nguyên đồ họa cho app.
Nguồn bài viết từ Tác giả Phil Schmid