Skip to content

fix(avatar-pack): bg removal, style regen consistency, and doc restructure#20

Merged
0xFANGO merged 21 commits intomainfrom
feat/avatar-pack-skill
Apr 8, 2026
Merged

fix(avatar-pack): bg removal, style regen consistency, and doc restructure#20
0xFANGO merged 21 commits intomainfrom
feat/avatar-pack-skill

Conversation

@latentflux42
Copy link
Copy Markdown
Contributor

@latentflux42 latentflux42 commented Apr 8, 2026

Summary

  • Background removal: add rembg fallback, lower checkerboard detection threshold, add connected-component island removal for enclosed remnants
  • Style regen consistency: remove stale base_image_original.png guard so profile card matches new style after scenario C option 2; use local processed path for profile card generation
  • Doc restructure: move output rules and file paths from SKILL.md to GENERATE.md; simplify SKILL.md description to English
  • Misc: add __pycache__/ to .gitignore, fix potential UnboundLocalError in rembg cleanup

Test plan

  • Generate a fresh avatar (scenario B) — verify all files created and consistent
  • Adjust style (scenario C option 2) — verify profile card matches new expressions
  • Regenerate single expression (scenario C option 3) — verify original preserved
  • Test with images that have checkerboard backgrounds — verify clean removal
  • Test rembg fallback when rembg is not installed

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new skill plus a large image-processing pipeline (background removal, GIF/sticker generation) that could fail on edge-case inputs or missing system deps, but is isolated to the new cola-avatar-pack/ tooling.

Overview
Introduces a new cola-avatar-pack skill that generates a pixel-art self-portrait, a profile card, 4 animated emoji GIFs, and 3 meme stickers, with a documented multi-phase flow for first-time generation and regeneration.

Adds cola-avatar-pack/scripts/process_avatar.py to handle background removal (including optional rembg CLI fallback and artifact cleanup), canvas fitting, animation frame generation, profile-card rendering (wuxing colors + rarity diamonds), and dual-size (128px + @2x) outputs for both GIFs and PNG stickers.

Updates docs/changelog to surface the new skill in README.md, README.zh.md, and CHANGELOG.md, and extends .gitignore for Python cache/artifacts.

Reviewed by Cursor Bugbot for commit a9d7f70. Bugbot is set up for automated code reviews on this repo. Configure here.

latentflux42 and others added 15 commits April 7, 2026 16:19
Cola-only skill that generates pixel-art avatars, profile cards,
4 emoji GIFs (happy/sad/angry/thinking) and 3 meme stickers
(confused/annoyed/cracked). Includes HARD-GATE environment check
so non-Cola agents get a clear message instead of silent failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GENERATE.md:
- Add grid/checkerboard keywords to negative_prompt to reduce background artifacts
- Improve Phase 5.5 quality check: add outfit color consistency check, smarter retry
  (use solid white bg + enforce reference matching on retry)
- Fix scene C single-expression regen: pass --line1/--line2 so profile card renders correctly

process_avatar.py:
- Better background removal: detect and clean grid line remnants on semi-transparent images
  using a 15x15 window scan (>80% transparent neighbors = remnant)
- Raise corner sampling threshold from 50% to 90% to catch checkerboard backgrounds
- Extract _is_fringe_color() helper for cleaner fringe detection (catches warm/cool gray,
  off-white, light beige — not just neutral gray)
- Use 8-connected neighbors instead of 4-connected for fringe removal
- Iterate fringe removal up to 3 rounds to handle multi-pixel-deep artifacts
- Widen lightning bolt crack in "cracked" meme for better visibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d cleanup

GENERATE.md:
- Add fallback for expired signed URLs in reference_images: re-upload from
  local base_image_original.png, or append text-based style matching prompt

process_avatar.py:
- Replace window-scan grid removal with flood-fill from transparent pixels
  into adjacent neutral/light opaque pixels — more accurate, avoids killing
  character content that happens to be surrounded by transparency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Profile tagline (Phase 2.4) must now align with AGENT.md personality —
prevents introducing negative traits absent from the character definition
(e.g., writing "holds grudges" when the character is "never genuinely mean").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… profile card mismatch

When user chose "adjust style" (scenario C option 2), save_base_image's
guard kept the old base_image_original.png while overwriting base_image.png.
Since generate_profile_card prefers original, the card showed old style
while expressions showed new style. Now we rm the old original first so
the guard lets the new 1K image through.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pdate tagline example

- Add two extra passes in remove_background() to clear semi-transparent
  neutral pixels and re-flood-fill from newly opened holes, fixing
  residual checkerboard artifacts from AI generators
- Update profile tagline example to better illustrate contradiction

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, color system refactor

- process_avatar.py: auto-detect low-transparency images and call rembg CLI as
  primary background removal before falling back to flood-fill. Fixes persistent
  opaque-background bug when listenhub outputs 0% transparent images.
- SKILL.md: rewrote description into Use when / Also use when / Do NOT use when
  structure with English keywords for better skill matching
- GENERATE.md: replaced hex codes and vague adjectives with aesthetic-narrative
  color guidance per wuxing element (e.g. fire → 窑变釉的红 → cinnabar red prompt);
  avatar.json now written in Phase 4 instead of Phase 7; added Phase 5.0 parameter
  validation with fallback recalculation if avatar.json missing; updated personality
  line rules to disallow negative traits absent from AGENT.md definition
…rembg prereq

- After rembg removes background, recompute transparency stats so the
  branch-1 cleanup (flood-fill from transparent pixels) still runs on
  any residual artifacts rembg left behind
- Remove redundant border-seeded flood-fill pass (rembg handles the
  cases it was designed for)
- Add rembg availability check to GENERATE.md prerequisites

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate trigger phrases into a single compact list and switch
the description to English for consistency with the skill metadata
format. No behavioral change — same trigger conditions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… to GENERATE.md

SKILL.md now only contains runtime behavior (wake display, emoji usage).
GENERATE.md is self-contained with output rules, file paths, and all
generation phases. Also drop "场景 A/B/C/D" labels in favor of plain
section names and add explicit "no caption" reminder to meme usage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…profile card

- Initialize tmp_in/tmp_out to None before try block in _try_rembg()
  to avoid UnboundLocalError in finally if tempfile creation fails
- Pass base_output (local processed path) instead of args.base (may be
  a remote URL) to generate_profile_card so it reads the correct file
- Clarify in GENERATE.md that avatar.json is maintained by the outer
  flow, not by process_avatar.py

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…se_image runs

Remove the exists-guard in save_base_image() — the caller (main's
is_expression_only check) already skips save_base_image entirely for
expression-only regeneration. When save_base_image does run, the input
is always a fresh 1K image, so the guard was only preventing style-regen
(scenario C option 2) from updating the original.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gray checkerboard

Reduce avg brightness threshold from 160 to 90 in _is_bg_remnant() so
the flood-fill cleanup pass can clear medium-gray checkerboard squares
that rembg sometimes leaves behind.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Lower _is_bg_remnant avg threshold from 90 to 55 to detect dark
  checkerboard squares (~70 brightness) while preserving character
  outlines (avg 20-40)
- Add Stage B: connected-component analysis that finds small (<=64px),
  low-saturation opaque islands disconnected from the main character
  and clears them — these are enclosed background remnants that
  flood-fill cannot reach

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Happy GIF always overwritten during single-expression regeneration
    • I made happy.gif generation conditional during targeted expression/meme regeneration and allowed meme-only runs to proceed without requiring any expression input.

Create PR

Or push these changes by commenting:

@cursor push efb373116a
Preview (efb373116a)
diff --git a/avatar-pack/scripts/process_avatar.py b/avatar-pack/scripts/process_avatar.py
--- a/avatar-pack/scripts/process_avatar.py
+++ b/avatar-pack/scripts/process_avatar.py
@@ -1203,12 +1203,17 @@
         return
 
     # Process expression images — skip any not provided (supports single-expression regen)
+    has_targeted_regen_input = any((
+        args.sad, args.angry, args.thinking,
+        args.meme_confused, args.meme_annoyed, args.meme_cracked,
+    ))
     images = {
-        'happy': args.base,
         'sad': args.sad,
         'angry': args.angry,
         'thinking': args.thinking,
     }
+    if not is_expression_only or not has_targeted_regen_input:
+        images['happy'] = args.base
 
     available = {}
     for emotion, path in images.items():
@@ -1217,7 +1222,8 @@
         elif path:
             print(f'Warning: {emotion} image not found: {path}, skipping', file=sys.stderr)
 
-    if not available:
+    has_meme_inputs = any((args.meme_confused, args.meme_annoyed, args.meme_cracked))
+    if not available and not has_meme_inputs:
         print('Error: no expression images found', file=sys.stderr)
         sys.exit(1)

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Comment thread cola-avatar-pack/scripts/process_avatar.py
…n regen

Add --regen-happy flag so happy.gif is only regenerated when explicitly
requested. During expression-only regen (scenario C option 3), the old
code unconditionally mapped happy to args.base (128px), overwriting the
1K-sourced happy.gif with a degraded version.

Now: full generation (Phase 6) passes --regen-happy, single-expression
regen does not. Also allow meme-only invocations without expression inputs.

Fixes: #20 (comment)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@latentflux42
Copy link
Copy Markdown
Contributor Author

Fixed in e1f528d. Added --regen-happy flag — full generation (Phase 6) passes it, single-expression regen does not. Also decoupled meme-only invocations from expression input requirements.

Resolves #20 (comment)

Comment thread avatar-pack/scripts/process_avatar.py Outdated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@latentflux42
Copy link
Copy Markdown
Contributor Author

Fixed in d4bdb4b — removed unused import math.

Resolves #20 (comment)

Comment thread cola-avatar-pack/scripts/process_avatar.py
…r pre-processed original

base_image_original.png is already background-removed by save_base_image.
Calling remove_background again enters the cleanup branch which can
cumulatively erode light/neutral character features (white fur, gray
accessories). Keep remove_background only for the else fallback where
the input may not be pre-processed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@latentflux42
Copy link
Copy Markdown
Contributor Author

Fixed in 356f2ccbase_image_original.png is already background-removed by save_base_image, so the second remove_background call in generate_profile_card was redundant and could cumulatively erode light/neutral character features. Removed it for the original branch; kept it in the else fallback for unprocessed inputs.

Resolves #20 (comment)

Rename skill directory and update all references across SKILL.md,
GENERATE.md, process_avatar.py, README.md, README.zh.md, and
CHANGELOG.md. No logic changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread cola-avatar-pack/scripts/process_avatar.py
…ransparency holes

Pillow's ImageDraw replaces pixel values rather than compositing, so
drawing outline diamonds at alpha=77 onto the opaque white card punched
semi-transparent holes visible on dark backgrounds. Premultiply the
outline color against white so the visual appearance is identical on
white but fully opaque everywhere.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@latentflux42
Copy link
Copy Markdown
Contributor Author

Fixed in ac21693 — premultiply diamond outline color against white background so it stays fully opaque (alpha=255) while looking visually identical. Eliminates transparency holes on dark backgrounds.

Resolves #20 (comment)

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ac21693. Configure here.

Comment thread cola-avatar-pack/scripts/process_avatar.py
@latentflux42
Copy link
Copy Markdown
Contributor Author

Won't fix — the 24px clipping occurs only at the apex frame (1 of 8, 100ms) at @2x resolution. At 128px display size it's 12px, imperceptible during animation playback. Reducing stretch to eliminate it would weaken the bounce expressiveness for no visible gain.

Resolves #20 (comment)

@0xFANGO 0xFANGO self-requested a review April 8, 2026 07:43
0xFANGO
0xFANGO previously approved these changes Apr 8, 2026
The lightning crack in the cracked meme used the full character bbox
center as its x-axis origin. For asymmetric distressed poses (slumped
shoulders, arms to one side), this placed the crack off-center from
the head. Now compute the centroid of opaque pixels in the upper 1/3
of the character (head region) for accurate centering. Text position
and brightness sampling also benefit from the corrected face_cx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@0xFANGO 0xFANGO merged commit beb811b into main Apr 8, 2026
2 checks passed
@latentflux42 latentflux42 deleted the feat/avatar-pack-skill branch April 16, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants