Accept portrait images for square crop

This commit is contained in:
2026-08-06 00:48:33 +02:00
parent 82d0a07e1f
commit 057726b543
3 changed files with 15 additions and 7 deletions
+1 -1
View File
@@ -188,7 +188,7 @@ There are three selectable data sources:
- Source discovery: use manufacturer mapping from Local scrape DB and show exactly which source produced each candidate. - Source discovery: use manufacturer mapping from Local scrape DB and show exactly which source produced each candidate.
- Browser scraping: use the configured browser engine per source and keep the UI responsive while scraping runs. - Browser scraping: use the configured browser engine per source and keep the UI responsive while scraping runs.
- Image extraction: collect original image URLs, dimensions, mime type, file size and source page URL. - Image extraction: collect original image URLs, dimensions, mime type, file size and source page URL.
- Image quality: show a clear warning for any picture candidate smaller than `800 x 800`; small thumbnails must remain visible for review but must be marked as not production-ready. - Image quality: product pictures will later be normalized to a `1:1` square, so a portrait/landscape image may be production-ready even when one side is below `800px` (for example `739 x 1435`). Mark images as too small only when the longest side is below `800px`; otherwise show the real size and note when it is OK for `1:1` crop.
- Duplicate detection: calculate `sha256` for exact duplicates and perceptual hash for visually similar images. - Duplicate detection: calculate `sha256` for exact duplicates and perceptual hash for visually similar images.
- Target assignment: propose whether pictures belong to `color`, `size`, `combination` or `manual` target, then learn from user corrections. - Target assignment: propose whether pictures belong to `color`, `size`, `combination` or `manual` target, then learn from user corrections.
- Persistence: save picture blobs, candidates, hashes, source metadata and learned rules only into Local scrape DB. - Persistence: save picture blobs, candidates, hashes, source metadata and learned rules only into Local scrape DB.
+6 -2
View File
@@ -912,10 +912,14 @@ function renderPictureCandidates(items) {
row.classList.remove("is-too-small"); row.classList.remove("is-too-small");
return; return;
} }
const isTooSmall = image.naturalWidth < MIN_PRODUCT_PICTURE_SIZE || image.naturalHeight < MIN_PRODUCT_PICTURE_SIZE; const shortestSide = Math.min(image.naturalWidth, image.naturalHeight);
const longestSide = Math.max(image.naturalWidth, image.naturalHeight);
const isTooSmall = longestSide < MIN_PRODUCT_PICTURE_SIZE;
row.classList.toggle("is-too-small", isTooSmall); row.classList.toggle("is-too-small", isTooSmall);
dimensions.textContent = isTooSmall dimensions.textContent = isTooSmall
? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - too small, min ${MIN_PRODUCT_PICTURE_SIZE} x ${MIN_PRODUCT_PICTURE_SIZE}` ? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - too small, min long side ${MIN_PRODUCT_PICTURE_SIZE}px`
: shortestSide < MIN_PRODUCT_PICTURE_SIZE
? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - OK for 1:1 crop`
: `Size: ${image.naturalWidth} x ${image.naturalHeight}px`; : `Size: ${image.naturalWidth} x ${image.naturalHeight}px`;
}); });
image.addEventListener("error", () => { image.addEventListener("error", () => {
+6 -2
View File
@@ -992,10 +992,14 @@ function renderPictureCandidates(items) {
return; return;
} }
const isTooSmall = image.naturalWidth < MIN_PRODUCT_PICTURE_SIZE || image.naturalHeight < MIN_PRODUCT_PICTURE_SIZE; const shortestSide = Math.min(image.naturalWidth, image.naturalHeight);
const longestSide = Math.max(image.naturalWidth, image.naturalHeight);
const isTooSmall = longestSide < MIN_PRODUCT_PICTURE_SIZE;
row.classList.toggle("is-too-small", isTooSmall); row.classList.toggle("is-too-small", isTooSmall);
dimensions.textContent = isTooSmall dimensions.textContent = isTooSmall
? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - too small, min ${MIN_PRODUCT_PICTURE_SIZE} x ${MIN_PRODUCT_PICTURE_SIZE}` ? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - too small, min long side ${MIN_PRODUCT_PICTURE_SIZE}px`
: shortestSide < MIN_PRODUCT_PICTURE_SIZE
? `Size: ${image.naturalWidth} x ${image.naturalHeight}px - OK for 1:1 crop`
: `Size: ${image.naturalWidth} x ${image.naturalHeight}px`; : `Size: ${image.naturalWidth} x ${image.naturalHeight}px`;
}); });
image.addEventListener("error", () => { image.addEventListener("error", () => {