feat: curseur tolérance verre pour récupérer les zones semi-transparentes du détourage

Ajoute alphaThreshold (défaut 30) — les pixels dont l'alpha dépasse ce seuil
sont rendus opaques avant la composition sur fond blanc, évitant que le verre
ou les surfaces translucides soient supprimés par le modèle IA.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 11:28:42 +02:00
parent c1fad5d0ed
commit d45c9490c0
5 changed files with 53 additions and 14 deletions
+22 -11
View File
@@ -90,7 +90,7 @@ export function useImages() {
// Pipeline complet pour un item (avec ref)
const runPipeline = useCallback(
async (id: string, blob: Blob, ref: string, feather: number, rotation: number) => {
async (id: string, blob: Blob, ref: string, feather: number, rotation: number, alphaThreshold: number) => {
const { convertHeicToJpeg } = await import('@/lib/client/heicConverter')
const { removeBackgroundRaw, compositeOnWhite } = await import('@/lib/client/bgRemover')
const { rotateBlob } = await import('@/lib/client/imageRotator')
@@ -120,7 +120,7 @@ export function useImages() {
let finalBlob: Blob
try {
const composited = await compositeOnWhite(rawPng, feather)
const composited = await compositeOnWhite(rawPng, feather, alphaThreshold)
finalBlob = await rotateBlob(composited, rotation)
} catch (err) {
update(id, { status: 'error', error: `Post-traitement : ${(err as Error).message}` })
@@ -133,15 +133,15 @@ export function useImages() {
[update, searchProduct],
)
// Recomposite rapide (feather ou rotation changés) — ne relance pas l'IA
// Recomposite rapide (feather, alphaThreshold ou rotation changés) — ne relance pas l'IA
const recomposite = useCallback(
async (id: string, rawPngBlob: Blob, feather: number, rotation: number) => {
async (id: string, rawPngBlob: Blob, feather: number, rotation: number, alphaThreshold: number) => {
const { compositeOnWhite } = await import('@/lib/client/bgRemover')
const { rotateBlob } = await import('@/lib/client/imageRotator')
try {
const composited = await compositeOnWhite(rawPngBlob, feather)
const composited = await compositeOnWhite(rawPngBlob, feather, alphaThreshold)
const finalBlob = await rotateBlob(composited, rotation)
update(id, { processedBlob: finalBlob, feather, rotation })
update(id, { processedBlob: finalBlob, feather, rotation, alphaThreshold })
} catch (err) {
update(id, { status: 'error', error: `Retraitement : ${(err as Error).message}` })
}
@@ -167,6 +167,7 @@ export function useImages() {
processedBlob: null,
rotation: 0,
feather: 2,
alphaThreshold: 30,
shopifyProductId: null,
shopifyProductTitle: null,
uploadedUrl: null,
@@ -176,7 +177,7 @@ export function useImages() {
}))
dispatch({ type: 'ADD', items: newItems })
for (const item of newItems) {
runPipeline(item.id, item.originalBlob, item.ref, item.feather, item.rotation)
runPipeline(item.id, item.originalBlob, item.ref, item.feather, item.rotation, item.alphaThreshold)
}
} else {
const ref = refFromFilename(file.name)
@@ -189,6 +190,7 @@ export function useImages() {
processedBlob: null,
rotation: 0,
feather: 2,
alphaThreshold: 30,
shopifyProductId: null,
shopifyProductTitle: null,
uploadedUrl: null,
@@ -197,7 +199,7 @@ export function useImages() {
progress: null,
}
dispatch({ type: 'ADD', items: [newItem] })
runPipeline(newItem.id, newItem.originalBlob, newItem.ref, newItem.feather, newItem.rotation)
runPipeline(newItem.id, newItem.originalBlob, newItem.ref, newItem.feather, newItem.rotation, newItem.alphaThreshold)
}
}
},
@@ -208,7 +210,16 @@ export function useImages() {
(id: string, value: number) => {
const item = items.find((i) => i.id === id)
if (!item || !item.rawPngBlob) return
recomposite(id, item.rawPngBlob, value, item.rotation)
recomposite(id, item.rawPngBlob, value, item.rotation, item.alphaThreshold)
},
[items, recomposite],
)
const setAlphaThreshold = useCallback(
(id: string, value: number) => {
const item = items.find((i) => i.id === id)
if (!item || !item.rawPngBlob) return
recomposite(id, item.rawPngBlob, item.feather, item.rotation, value)
},
[items, recomposite],
)
@@ -219,7 +230,7 @@ export function useImages() {
if (!item || !item.rawPngBlob) return
const delta = direction === 'cw' ? 90 : -90
const newRotation = ((item.rotation + delta) % 360 + 360) % 360
recomposite(id, item.rawPngBlob, item.feather, newRotation)
recomposite(id, item.rawPngBlob, item.feather, newRotation, item.alphaThreshold)
},
[items, recomposite],
)
@@ -269,5 +280,5 @@ export function useImages() {
[update],
)
return { items, addFiles, setFeather, rotate, uploadOne, uploadAll, removeItem, assignProduct }
return { items, addFiles, setFeather, setAlphaThreshold, rotate, uploadOne, uploadAll, removeItem, assignProduct }
}