חיתוך דמות או מוצר לנכס שקוף

תמונה של דמות, לוגו או מוצר צריכה להגיע לאתר בלי רקע, עם קצה רך ובכמה גדלים. הדרך המהירה, סף בהירות אחד, נכשלת ברגע שיש בנושא אזור בהיר כמו בגד לבן או אריזה. ה-Skill הזה נותן את שלושת המנגנונים שעובדים יחד ואת הסדר ביניהם, ואת השלב שרוב האנשים מדלגים עליו: לדגום את הפיקסלים של המקור לפני שקובעים סף, כי לפעמים ההפרדה בין הרקע לנושא היא לא בהירות בכלל אלא טמפרטורת הצבע. זהו Skill ל-Claude: תיקייה עם קובץ SKILL.md, שנטענת אוטומטית כשהמשימה מתאימה לתיאור שבראש הקובץ. בניגוד לפרומפט שמדביקים בכל פעם מחדש, Skill מותקן פעם אחת וממשיך לעבוד לבד.

בניית אתרים · תמונות · שקיפות · sharp · נכסים · מסקוט

מתי הוא נכנס לפעולה

---
name: cutout-assets
description: "Turn a rendered character, mascot, logo or product image into a clean transparent web asset: background removal by colour, flood fill from the edges, connected component cleanup, edge feathering and multiple output sizes. Use when an image has a background that must go, when a cut out looks jagged or has leftover artifacts, or when preparing a mascot or product shot for a hero section."
---

# חיתוך נכס לאתר

## שלושת המנגנונים, ומתי כל אחד

**סף צבע גלובלי** עובד רק כשצבע הרקע אינו מופיע בשום מקום בנושא. מסך
ירוק הוא המקרה האידיאלי.

**מילוי הצפה מהשוליים** מתחיל מהמסגרת פנימה. זה מה שמאפשר לשמור על
אזורים בהירים **בתוך** הנושא: הם מוקפים בו, ולכן ההצפה לא מגיעה אליהם.

**ספירת רכיבים קשירים** מסירה איים: אביזרים מרחפים, השתקפויות, ולכלוך.

בפועל צריך את שלושתם, בסדר הזה.

## הטעות שעולה הכי הרבה זמן

דמות עם רתמה לבנה על רקע בהיר. סף בהירות אוכל את הרתמה, כי היא בהירה
בדיוק כמו הרקע.

**לדגום את המקור לפני שקובעים סף.** לא לנחש:

```js
const px = (x, y) => {
  const i = (y * W + x) * C
  return [data[i], data[i + 1], data[i + 2]]
}
for (const [name, [x, y]] of Object.entries(points)) console.log(name, px(x, y))
```

במקרה אמיתי הדגימה גילתה שההפרדה אינה בהירות אלא **טמפרטורה**: הרקע היה
חם, והאזור שרצו לשמור היה קריר. סף על ההפרש הזה הפך את החיתוך לוודאי.

```js
const isGreen = (i) => data[i + 1] - Math.max(data[i], data[i + 2]) > 40
const isSubject = (i) => data[i + 2] > data[i] + 18 && data[i + 2] > 70
```

## מילוי הצפה עם דמיון מקומי

רקע הוא כמעט תמיד גרדיאנט ולא צבע אחד, ולכן השוואה לדגימה בודדת עוצרת
באמצע. השוואה **לשכן שממנו הגענו** ממשיכה לאורך המעבר:

```js
const CLOSE = 26
const near = (a, b) =>
  Math.abs(data[a] - data[b]) < CLOSE &&
  Math.abs(data[a + 1] - data[b + 1]) < CLOSE &&
  Math.abs(data[a + 2] - data[b + 2]) < CLOSE
```

הזרעים הם כל פיקסלי המסגרת. תמיד להגן על הנושא במפורש, אחרת ההצפה נוגסת
בשוליים השקופים למחצה.

## רכיבים קשירים: סף, לא "הגדול ביותר"

"לשמור רק את הגדול ביותר" נשמע נכון ונכשל: אם משהו חוצה את הגוף, הנושא
מפוצל לכמה גושים ותקבלו רק את הראש.

לבדוק את ההתפלגות ואז לבחור סף. כשהגושים הם עשרים ושישה אחוז ואחר כך
אחוז ומטה, סף של חמישה אחוזים מפריד בבירור.

**מה שנוגע בנושא לא ניתן להפרדה בספירה.** השתקפות שנוגעת בקצה היא חלק
מאותו רכיב, ושם צריך חתך לפי מיקום.

## הפלט

```js
const cut = sharp(data, { raw: { width: W, height: H, channels: C } })
  .png()
  .blur(0.4) // ריכוך הקצה. בלעדיו החיתוך נראה כמו גזירה במספריים

const trimmed = await sharp(await cut.toBuffer())
  .trim({ threshold: 1 })
  .toBuffer({ resolveWithObject: true })

for (const size of [512, 256, 128]) {
  const base = sharp(trimmed.data).resize(size, size, {
    fit: 'contain',
    background: { r: 0, g: 0, b: 0, alpha: 0 },
  })
  await base.clone().png({ compressionLevel: 9 }).toFile(`out/x-${size}.png`)
  await base.clone().webp({ quality: 90 }).toFile(`out/x-${size}.webp`)
}
```

**החיתוך לפני ההקטנה.** הקטנה תחילה מורחת את הקצה ואז אין מה לחתוך.

## אימות

לבדוק את הפינות ואת מרכז האזור שרציתם לשמור, ולוודא שהאלפא שם הוא מה
שציפיתם. חיתוך שנראה תקין בתצוגה מוקטנת יכול להיות אכול לגמרי בגודל
מלא.

איך מתקינים

  1. צרו תיקייה בשם cutout-assets בתוך ‎.claude/skills/‎ בפרויקט, או בתיקיית הבית לשימוש בכל הפרויקטים.
  2. שמרו בתוכה את הקובץ בשם SKILL.md.
  3. זהו. אין צורך להפעיל כלום, והוא ייטען לבד כשהמשימה תתאים לתיאור.