fix(ai): warn that upscale and background removal are slow without a GPU (#605)

Add a one-line notice to the upscale and background-removal settings that
heavy AI runs much slower without a GPU and a large image can take minutes,
so a CPU-only self-hoster isn't caught off guard by a slow run. Translated
into all 21 locales.

Refs #591
This commit is contained in:
SnapOtter
2026-07-21 17:55:11 +08:00
committed by GitHub
parent 89d75853f4
commit d43208b85e
24 changed files with 95 additions and 0 deletions
@@ -217,6 +217,9 @@ export function RemoveBgControls({ settings, onChange }: RemoveBgControlsProps)
return (
<div className="space-y-3">
<p className="text-xs text-muted-foreground" data-testid="remove-bg-cpu-note">
{t.toolSettings["remove-background"].cpuNote}
</p>
{/* Subject type */}
<SectionLabel>{t.toolSettings["remove-background"].subject}</SectionLabel>
<div className="grid grid-cols-3 gap-1.5">
@@ -70,6 +70,9 @@ export function UpscaleControls({ settings: initialSettings, onChange }: Upscale
return (
<div className="space-y-4">
<p className="text-xs text-muted-foreground" data-testid="upscale-cpu-note">
{t.toolSettings.upscale.cpuNote}
</p>
{/* Scale factor */}
<div>
<div className="flex justify-between items-center">
+3
View File
@@ -1511,6 +1511,7 @@ export const ar: TranslationKeys = {
submit: "إزالة الخلفية",
submitBatch: "إزالة الخلفية ({count} ملف)",
progressLabel: "جاري إزالة الخلفية",
cpuNote: "يستخدم نموذج ذكاء اصطناعي. بدون معالج رسومات، قد تستغرق الصورة الكبيرة بعض الوقت.",
},
"remove-gif-background": {
quality: "الجودة",
@@ -1560,6 +1561,8 @@ export const ar: TranslationKeys = {
submitBatch: "تكبير {scale}x ({count} ملف)",
progressLabel: "جاري تكبير الصورة",
progressLabelBatch: "جاري تكبير {count} صورة",
cpuNote:
"التكبير بالذكاء الاصطناعي يستهلك موارد حسابية كبيرة. بدون معالج رسومات، قد تستغرق الصورة الكبيرة عدة دقائق.",
},
"erase-object": {
qualityFast: "سريع",
+3
View File
@@ -1528,6 +1528,7 @@ export const de: TranslationKeys = {
submit: "Hintergrund entfernen",
submitBatch: "Hintergrund entfernen ({count} Dateien)",
progressLabel: "Hintergrund wird entfernt",
cpuNote: "Nutzt ein KI-Modell. Ohne GPU kann ein großes Bild eine Weile dauern.",
},
"remove-gif-background": {
quality: "Qualität",
@@ -1578,6 +1579,8 @@ export const de: TranslationKeys = {
submitBatch: "Hochskalieren {scale}x ({count} Dateien)",
progressLabel: "Bild wird hochskaliert",
progressLabelBatch: "{count} Bilder werden hochskaliert",
cpuNote:
"KI-Hochskalierung ist rechenintensiv. Ohne GPU kann ein großes Bild mehrere Minuten dauern.",
},
"erase-object": {
qualityFast: "Schnell",
+3
View File
@@ -1475,6 +1475,7 @@ export const en = {
submit: "Remove Background",
submitBatch: "Remove Background ({count} files)",
progressLabel: "Removing background",
cpuNote: "Runs an AI model. Without a GPU, a large image can take a while.",
},
"remove-gif-background": {
quality: "Quality",
@@ -1524,6 +1525,8 @@ export const en = {
submitBatch: "Upscale {scale}x ({count} files)",
progressLabel: "Upscaling image",
progressLabelBatch: "Upscaling {count} images",
cpuNote:
"AI upscaling is compute-heavy. Without a GPU, a large image can take several minutes.",
},
"erase-object": {
qualityFast: "Fast",
+3
View File
@@ -1511,6 +1511,7 @@ export const es: TranslationKeys = {
submit: "Eliminar fondo",
submitBatch: "Eliminar fondo ({count} archivos)",
progressLabel: "Eliminando fondo",
cpuNote: "Usa un modelo de IA. Sin GPU, una imagen grande puede tardar un rato.",
},
"remove-gif-background": {
quality: "Calidad",
@@ -1561,6 +1562,8 @@ export const es: TranslationKeys = {
submitBatch: "Escalar {scale}x ({count} archivos)",
progressLabel: "Escalando imagen",
progressLabelBatch: "Escalando {count} imágenes",
cpuNote:
"El escalado con IA consume muchos recursos. Sin GPU, una imagen grande puede tardar varios minutos.",
},
"erase-object": {
qualityFast: "Rápido",
+3
View File
@@ -1533,6 +1533,7 @@ export const fr: TranslationKeys = {
submit: "Supprimer l'arrière-plan",
submitBatch: "Supprimer l'arrière-plan ({count} fichiers)",
progressLabel: "Suppression de l'arrière-plan",
cpuNote: "Utilise un modèle d'IA. Sans GPU, une grande image peut prendre un moment.",
},
"remove-gif-background": {
quality: "Qualité",
@@ -1584,6 +1585,8 @@ export const fr: TranslationKeys = {
submitBatch: "Agrandir {scale}x ({count} fichiers)",
progressLabel: "Agrandissement de l'image",
progressLabelBatch: "Agrandissement de {count} images",
cpuNote:
"L'agrandissement par IA demande beaucoup de calcul. Sans GPU, une grande image peut prendre plusieurs minutes.",
},
"erase-object": {
qualityFast: "Rapide",
+2
View File
@@ -1342,6 +1342,7 @@ export const hi: TranslationKeys = {
submit: "बैकग्राउंड हटाएं",
submitBatch: "बैकग्राउंड हटाएं ({count} फाइलें)",
progressLabel: "बैकग्राउंड हटाया जा रहा है",
cpuNote: "एक AI मॉडल चलाता है। GPU के बिना, बड़ी इमेज में कुछ समय लग सकता है।",
},
"remove-gif-background": {
quality: "क्वालिटी",
@@ -1390,6 +1391,7 @@ export const hi: TranslationKeys = {
submitBatch: "{scale}x अपस्केल ({count} फाइलें)",
progressLabel: "इमेज अपस्केल हो रही है",
progressLabelBatch: "{count} इमेज अपस्केल हो रही हैं",
cpuNote: "AI अपस्केलिंग कंप्यूट-हैवी है। GPU के बिना, बड़ी इमेज में कई मिनट लग सकते हैं।",
},
"erase-object": {
qualityFast: "तेज़",
+3
View File
@@ -1521,6 +1521,7 @@ export const id: TranslationKeys = {
submit: "Hapus Latar Belakang",
submitBatch: "Hapus Latar Belakang ({count} file)",
progressLabel: "Menghapus latar belakang",
cpuNote: "Menjalankan model AI. Tanpa GPU, gambar besar bisa memakan waktu.",
},
"remove-gif-background": {
quality: "Kualitas",
@@ -1570,6 +1571,8 @@ export const id: TranslationKeys = {
submitBatch: "Perbesar {scale}x ({count} file)",
progressLabel: "Memperbesar gambar",
progressLabelBatch: "Memperbesar {count} gambar",
cpuNote:
"Upscaling AI membutuhkan banyak komputasi. Tanpa GPU, gambar besar bisa memakan waktu beberapa menit.",
},
"erase-object": {
qualityFast: "Cepat",
+4
View File
@@ -1526,6 +1526,8 @@ export const it: TranslationKeys = {
submit: "Rimuovi sfondo",
submitBatch: "Rimuovi sfondo ({count} file)",
progressLabel: "Rimozione sfondo",
cpuNote:
"Usa un modello di IA. Senza GPU, un'immagine grande può richiedere un po' di tempo.",
},
"remove-gif-background": {
quality: "Qualità",
@@ -1576,6 +1578,8 @@ export const it: TranslationKeys = {
submitBatch: "Ingrandisci {scale}x ({count} file)",
progressLabel: "Ingrandimento immagine",
progressLabelBatch: "Ingrandimento di {count} immagini",
cpuNote:
"L'upscaling con IA richiede molte risorse. Senza GPU, un'immagine grande può richiedere diversi minuti.",
},
"erase-object": {
qualityFast: "Veloce",
+3
View File
@@ -1482,6 +1482,7 @@ export const ja: TranslationKeys = {
submit: "背景を除去",
submitBatch: "背景を除去({count}ファイル)",
progressLabel: "背景を除去中",
cpuNote: "AIモデルを使用します。GPUがない場合、大きな画像には時間がかかることがあります。",
},
"remove-gif-background": {
quality: "品質",
@@ -1532,6 +1533,8 @@ export const ja: TranslationKeys = {
submitBatch: "{scale}倍に拡大({count}ファイル)",
progressLabel: "画像をアップスケール中",
progressLabelBatch: "{count}枚の画像をアップスケール中",
cpuNote:
"AIアップスケーリングは処理負荷が高い処理です。GPUがない場合、大きな画像には数分かかることがあります。",
},
"erase-object": {
qualityFast: "高速",
+3
View File
@@ -1466,6 +1466,7 @@ export const ko: TranslationKeys = {
submit: "배경 제거",
submitBatch: "배경 제거 ({count}개 파일)",
progressLabel: "배경 제거 중",
cpuNote: "AI 모델을 사용합니다. GPU가 없으면 큰 이미지는 시간이 걸릴 수 있습니다.",
},
"remove-gif-background": {
quality: "품질",
@@ -1514,6 +1515,8 @@ export const ko: TranslationKeys = {
submitBatch: "{scale}배 확대 ({count}개 파일)",
progressLabel: "이미지 업스케일 중",
progressLabelBatch: "{count}개 이미지 업스케일 중",
cpuNote:
"AI 업스케일링은 연산 부담이 큽니다. GPU가 없으면 큰 이미지는 몇 분이 걸릴 수 있습니다.",
},
"erase-object": {
qualityFast: "빠름",
+3
View File
@@ -1526,6 +1526,7 @@ export const nl: TranslationKeys = {
submit: "Achtergrond verwijderen",
submitBatch: "Achtergrond verwijderen ({count} bestanden)",
progressLabel: "Achtergrond verwijderen",
cpuNote: "Gebruikt een AI-model. Zonder GPU kan een grote afbeelding even duren.",
},
"remove-gif-background": {
quality: "Kwaliteit",
@@ -1575,6 +1576,8 @@ export const nl: TranslationKeys = {
submitBatch: "Opschalen {scale}x ({count} bestanden)",
progressLabel: "Afbeelding opschalen",
progressLabelBatch: "{count} afbeeldingen opschalen",
cpuNote:
"AI-opschaling is rekenintensief. Zonder GPU kan een grote afbeelding enkele minuten duren.",
},
"erase-object": {
qualityFast: "Snel",
+2
View File
@@ -1525,6 +1525,7 @@ export const pl: TranslationKeys = {
submit: "Usuń tło",
submitBatch: "Usuń tło ({count} plików)",
progressLabel: "Usuwanie tła",
cpuNote: "Korzysta z modelu AI. Bez GPU duży obraz może chwilę potrwać.",
},
"remove-gif-background": {
quality: "Jakość",
@@ -1575,6 +1576,7 @@ export const pl: TranslationKeys = {
submitBatch: "Powiększ {scale}x ({count} plików)",
progressLabel: "Powiększanie obrazu",
progressLabelBatch: "Powiększanie {count} obrazów",
cpuNote: "Skalowanie AI mocno obciąża procesor. Bez GPU duży obraz może zająć kilka minut.",
},
"erase-object": {
qualityFast: "Szybko",
+3
View File
@@ -1525,6 +1525,7 @@ export const ptBR: TranslationKeys = {
submit: "Remover fundo",
submitBatch: "Remover fundo ({count} arquivos)",
progressLabel: "Removendo fundo",
cpuNote: "Usa um modelo de IA. Sem GPU, uma imagem grande pode demorar um pouco.",
},
"remove-gif-background": {
quality: "Qualidade",
@@ -1574,6 +1575,8 @@ export const ptBR: TranslationKeys = {
submitBatch: "Ampliar {scale}x ({count} arquivos)",
progressLabel: "Ampliando imagem",
progressLabelBatch: "Ampliando {count} imagens",
cpuNote:
"A ampliação com IA exige muito processamento. Sem GPU, uma imagem grande pode levar vários minutos.",
},
"erase-object": {
qualityFast: "Rápido",
+3
View File
@@ -1524,6 +1524,7 @@ export const ru: TranslationKeys = {
submit: "Удалить фон",
submitBatch: "Удалить фон ({count} файлов)",
progressLabel: "Удаление фона",
cpuNote: "Использует модель ИИ. Без GPU обработка большого изображения может занять время.",
},
"remove-gif-background": {
quality: "Качество",
@@ -1573,6 +1574,8 @@ export const ru: TranslationKeys = {
submitBatch: "Увеличить {scale}x ({count} файлов)",
progressLabel: "Увеличение изображения",
progressLabelBatch: "Увеличение {count} изображений",
cpuNote:
"Масштабирование ИИ требует много ресурсов. Без GPU обработка большого изображения может занять несколько минут.",
},
"erase-object": {
qualityFast: "Быстро",
+2
View File
@@ -1520,6 +1520,7 @@ export const sv: TranslationKeys = {
submit: "Ta bort bakgrund",
submitBatch: "Ta bort bakgrund ({count} filer)",
progressLabel: "Tar bort bakgrund",
cpuNote: "Använder en AI-modell. Utan GPU kan en stor bild ta en stund.",
},
"remove-gif-background": {
quality: "Kvalitet",
@@ -1570,6 +1571,7 @@ export const sv: TranslationKeys = {
submitBatch: "Uppskala {scale}x ({count} filer)",
progressLabel: "Uppskalar bild",
progressLabelBatch: "Uppskalar {count} bilder",
cpuNote: "AI-uppskalning är beräkningsintensiv. Utan GPU kan en stor bild ta flera minuter.",
},
"erase-object": {
qualityFast: "Snabb",
+2
View File
@@ -1503,6 +1503,7 @@ export const th: TranslationKeys = {
submit: "ลบพื้นหลัง",
submitBatch: "ลบพื้นหลัง ({count} ไฟล์)",
progressLabel: "กำลังลบพื้นหลัง",
cpuNote: "ใช้โมเดล AI หากไม่มี GPU รูปภาพขนาดใหญ่อาจใช้เวลาสักครู่",
},
"remove-gif-background": {
quality: "คุณภาพ",
@@ -1550,6 +1551,7 @@ export const th: TranslationKeys = {
submitBatch: "ขยาย {scale}x ({count} ไฟล์)",
progressLabel: "กำลังขยายภาพ",
progressLabelBatch: "กำลังขยาย {count} ภาพ",
cpuNote: "การขยายภาพด้วย AI ใช้พลังประมวลผลสูง หากไม่มี GPU รูปภาพขนาดใหญ่อาจใช้เวลาหลายนาที",
},
"erase-object": {
qualityFast: "เร็ว",
+4
View File
@@ -1524,6 +1524,8 @@ export const tr: TranslationKeys = {
submit: "Arka Planı Kaldır",
submitBatch: "Arka Planı Kaldır ({count} dosya)",
progressLabel: "Arka plan kaldırılıyor",
cpuNote:
"Bir yapay zeka modeli kullanır. GPU olmadan büyük bir görüntü biraz zaman alabilir.",
},
"remove-gif-background": {
quality: "Kalite",
@@ -1573,6 +1575,8 @@ export const tr: TranslationKeys = {
submitBatch: "{scale}x Büyüt ({count} dosya)",
progressLabel: "Görüntü büyütülüyor",
progressLabelBatch: "{count} görüntü büyütülüyor",
cpuNote:
"Yapay zeka ile büyütme yoğun işlem gerektirir. GPU olmadan büyük bir görüntü birkaç dakika sürebilir.",
},
"erase-object": {
qualityFast: "Hızlı",
+3
View File
@@ -1524,6 +1524,7 @@ export const uk: TranslationKeys = {
submit: "Видалити фон",
submitBatch: "Видалити фон ({count} файлів)",
progressLabel: "Видалення фону",
cpuNote: "Використовує модель ШІ. Без GPU обробка великого зображення може зайняти час.",
},
"remove-gif-background": {
quality: "Якість",
@@ -1573,6 +1574,8 @@ export const uk: TranslationKeys = {
submitBatch: "Збільшити {scale}x ({count} файлів)",
progressLabel: "Збільшення зображення",
progressLabelBatch: "Збільшення {count} зображень",
cpuNote:
"Масштабування ШІ потребує багато обчислень. Без GPU обробка великого зображення може зайняти кілька хвилин.",
},
"erase-object": {
qualityFast: "Швидко",
+3
View File
@@ -1524,6 +1524,7 @@ export const vi: TranslationKeys = {
submit: "Xóa nền",
submitBatch: "Xóa nền ({count} tệp)",
progressLabel: "Đang xóa nền",
cpuNote: "Sử dụng mô hình AI. Không có GPU, ảnh lớn có thể mất một lúc.",
},
"remove-gif-background": {
quality: "Chất lượng",
@@ -1573,6 +1574,8 @@ export const vi: TranslationKeys = {
submitBatch: "Phóng to {scale}x ({count} tệp)",
progressLabel: "Đang phóng to ảnh",
progressLabelBatch: "Đang phóng to {count} ảnh",
cpuNote:
"Nâng cấp bằng AI tiêu tốn nhiều tài nguyên tính toán. Không có GPU, ảnh lớn có thể mất vài phút.",
},
"erase-object": {
qualityFast: "Nhanh",
+2
View File
@@ -1291,6 +1291,7 @@ export const zhCN: TranslationKeys = {
submit: "移除背景",
submitBatch: "移除背景({count} 个文件)",
progressLabel: "正在移除背景",
cpuNote: "使用 AI 模型。若没有 GPU,处理大图可能需要一些时间。",
},
"remove-gif-background": {
quality: "质量",
@@ -1338,6 +1339,7 @@ export const zhCN: TranslationKeys = {
submitBatch: "放大 {scale}x{count} 个文件)",
progressLabel: "正在放大图片",
progressLabelBatch: "正在放大 {count} 张图片",
cpuNote: "AI 放大对算力要求很高。若没有 GPU,处理大图可能需要几分钟。",
},
"erase-object": {
qualityFast: "快速",
+2
View File
@@ -1291,6 +1291,7 @@ export const zhTW: TranslationKeys = {
submit: "移除背景",
submitBatch: "移除背景({count}個檔案)",
progressLabel: "正在移除背景",
cpuNote: "使用 AI 模型。若沒有 GPU,處理大圖可能需要一些時間。",
},
"remove-gif-background": {
quality: "品質",
@@ -1338,6 +1339,7 @@ export const zhTW: TranslationKeys = {
submitBatch: "放大{scale}倍({count}個檔案)",
progressLabel: "正在放大影像",
progressLabelBatch: "正在放大{count}張影像",
cpuNote: "AI 放大對算力需求很高。若沒有 GPU,處理大圖可能需要幾分鐘。",
},
"erase-object": {
qualityFast: "快速",
+30
View File
@@ -0,0 +1,30 @@
// @vitest-environment jsdom
import "@testing-library/jest-dom/vitest";
import { en } from "@snapotter/shared";
import { cleanup, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, it } from "vitest";
import { RemoveBgControls } from "@/components/tools/remove-bg-settings";
import { UpscaleControls } from "@/components/tools/upscale-settings";
afterEach(cleanup);
// A user on a CPU-only NUC was surprised AI upscale timed out ("unless my
// system is just underpowered"). These notices set that expectation up front.
describe("AI CPU expectation notice (#591)", () => {
it("upscale settings warn that it is slow without a GPU", () => {
render(<UpscaleControls />);
expect(screen.getByTestId("upscale-cpu-note")).toHaveTextContent(
en.toolSettings.upscale.cpuNote,
);
expect(en.toolSettings.upscale.cpuNote).toMatch(/GPU/);
});
it("remove-background settings warn that it is slow without a GPU", () => {
render(<RemoveBgControls settings={{}} onChange={() => {}} />);
expect(screen.getByTestId("remove-bg-cpu-note")).toHaveTextContent(
en.toolSettings["remove-background"].cpuNote,
);
expect(en.toolSettings["remove-background"].cpuNote).toMatch(/GPU/);
});
});