There is no problem in the development environment, but the build deployment reports an error no available backend found. ERR: [webgpu]
System Info
@huggingface/transformers is 3.0.0-alpha.7 and 3.0.0-alpha.6, Mac M1
Who can help?
@amyeroberts
Information
- [ ] The official example scripts
- [X] My own modified scripts
Tasks
- [ ] An officially supported task in the
examplesfolder (such as GLUE/SQuAD, ...) - [ ] My own task or dataset (give details below)
Reproduction
git: https://github.com/x007xyz/RMBG-webui
demo: https://rmbg-webui.videocovert.online/
core code:
import { AutoModel, AutoProcessor, env, PreTrainedModel, Processor, RawImage } from '@huggingface/transformers';
// Since we will download the model from the Hugging Face Hub, we can skip the local model check
env.allowLocalModels = false;
// Proxy the WASM backend to prevent the UI from freezing
env.backends.onnx.wasm.proxy = true;
export class Model {
static model: PreTrainedModel
static processor: Processor
static toDataURL(file: File): Promise<string> {
return new Promise((resolve) => {
const reader = new FileReader();
// Set up a callback when the file is loaded
reader.onload = e => resolve(e.target?.result as string);
reader.readAsDataURL(file);
})
}
static toImageURL (offscreen: OffscreenCanvas): Promise<string> {
return new Promise((resolve, reject) => {
// 将 OffscreenCanvas 转换为 Blob 对象
offscreen.convertToBlob().then(blob => {
resolve(URL.createObjectURL(blob))
}).catch(error => {
console.error('Error converting OffscreenCanvas to URL:', error);
reject(error)
});
})
}
static async loadModel() {
if (!this.model) {
this.model = await AutoModel.from_pretrained('briaai/RMBG-1.4', {
// Do not require config.json to be present in the repository
config: { model_type: 'custom' },
device: 'webgpu',
dtype: 'fp32',
});
}
if (!this.processor) {
this.processor = await AutoProcessor.from_pretrained('briaai/RMBG-1.4', {
// Do not require config.json to be present in the repository
config: {
do_normalize: true,
do_pad: false,
do_rescale: true,
do_resize: true,
image_mean: [0.5, 0.5, 0.5],
feature_extractor_type: "ImageFeatureExtractor",
image_std: [1, 1, 1],
resample: 2,
rescale_factor: 0.00392156862745098,
size: { width: 1024, height: 1024 },
}
});
}
}
static async processImage(file: string | File): Promise<string> {
if (file instanceof File) {
file = await this.toDataURL(file)
}
await this.loadModel();
// Read image
const image = await RawImage.fromURL(file);
const start = performance.now();
// Preprocess image
const { pixel_values } = await this.processor(image);
// Predict alpha matte
const { output } = await this.model({ input: pixel_values });
// Resize mask back to original size
const mask = await RawImage.fromTensor(output[0].mul(255).to('uint8')).resize(image.width, image.height);
console.log('消耗时间:', performance.now() - start);
// Create new canvas
const offscreenCanvas = new OffscreenCanvas(image.width, image.height)
const ctx = offscreenCanvas.getContext('2d');
if (!ctx) {
console.error('不支持OffscreenCanvas')
throw new Error("不支持OffscreenCanvas");
}
// Draw original image output to canvas
ctx.drawImage(image.toCanvas(), 0, 0);
console.log('消耗时间:', performance.now() - start);
// Update alpha channel
const pixelData = ctx.getImageData(0, 0, image.width, image.height);
for (let i = 0; i < mask.data.length; ++i) {
pixelData.data[4 * i + 3] = mask.data[i];
}
ctx.putImageData(pixelData, 0, 0);
return this.toImageURL(offscreenCanvas);
}
}
interface FileUploadOptions {
accept: string
multiple: boolean
max?: string
}
// 文件上传
export const selectFile = (options: FileUploadOptions): Promise<File[]> => {
return new Promise((resolve, reject) => {
// 创建input[file]元素
const input = document.createElement('input')
// 设置相应属性
input.setAttribute('type', 'file')
input.setAttribute('accept', options.accept)
options.multiple
? input.setAttribute('multiple', 'multiple')
: input.removeAttribute('multiple')
// 绑定事件
input.onchange = function (event) {
const input = event.target as HTMLInputElement;
// 获取文件列表
if (input?.files) {
const files = Array.from(input.files)
resolve(files)
} else {
reject(new Error('No files selected'))
}
}
input.oncancel = function () {
reject(new Error('No files selected'))
}
input.click()
})
}
view code
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { Button } from "@/components/ui/button"
import { Switch } from "@/components/ui/switch"
import { ImageDown, ImageUp } from "lucide-react"
import { selectFile, Model } from "@/utils"
import { useEffect, useMemo, useState } from "react"
import Loading from "./components/Loading"
const HomePage = () => {
const [loadingModel, setLoadingModel] = useState(true)
const [sourceImage, setSourceImage] = useState('')
const [processImage, setProcessImage] = useState('')
const [mode, setMode] = useState('source')
const canSwitch = useMemo(() => !!processImage, [processImage])
useEffect(() => {
Model.loadModel().then(() => {
// 模型加载完成
setLoadingModel(false)
})
}, [])
function onUpload () {
setSourceImage("")
setProcessImage("")
setMode('source')
selectFile({ accept: 'image/*', multiple: false }).then(files => {
Model.toDataURL(files[0]).then(url => {
setSourceImage(url)
})
return Model.processImage(files[0])
}).then(url => {
setProcessImage(url)
setMode('process')
})
}
function onCheckedChange(checked: boolean) {
setMode(checked ? 'process' : 'source')
}
function onDownLoad() {
const a = document.createElement('a');
a.href = processImage;
a.download = `${Date.now()}.png`; // 指定下载的文件名
a.click()
}
return (
<div className="mx-auto max-w-2xl p-6">
<AspectRatio ratio={4 / 3} className="bg-muted rounded-lg">
{mode === 'source' && sourceImage && <img className="object-contain w-full h-full absolute" src={sourceImage} alt="" />}
{processImage && mode === 'process' && <img className="object-contain w-full h-full absolute" src={processImage} alt="" />}
<div className="absolute bottom-4 right-4">
<Switch id="airplane-mode" disabled={!canSwitch} checked={mode === 'process'} onCheckedChange={onCheckedChange}/>
</div>
</AspectRatio>
<div className="mt-4 flex items-center">
<Button onClick={onUpload} disabled={loadingModel}>
{ loadingModel ? <Loading></Loading> : <ImageUp className="mr-4"/> }
上传图片
</Button>
<Button className="ml-6" variant={'outline'} disabled={!processImage} onClick={onDownLoad}>
<ImageDown className="mr-4"/>
下载图片
</Button>
</div>
</div>
)
}
export default HomePage
Expected behavior
Deployed to production environment normally
Hi @x007xyz, please make sure to follow the bug report template and provide all the necessary information so that we can help you.
At the moment there is no reproducible code snippet, explanation of what you're trying to do, what you've already done to try and fix it, to expected and observed behaviour or all relevant system information.
use @xenova/transformers is no problem,error form onnxruntime-web
Hi there @x007xyz 👋 - this has been fixed in the latest version (was an issue arising from proxying). In future, can you please open the issue in the Transformers.js repository (http://github.com/xenova/transformers.js)? Thanks!
If you still have any questions/issues, feel free to open a new issue in the above repo and I'd be happy to help!