如何解决Chrome扩展传递图片至Python fastai后端的处理错误?
Chrome扩展图片拦截与FastAI后端格式传递问题
我正在开发一款Chrome扩展,用于拦截网站中包含特定内容的图片。目前在将图片以可被Python后端读取的格式传递时遇到问题,该后端需使用fastai模型分析图片并返回预测结果。尝试过base64编码、FormData传递、canvas处理等方式,但均未成功。
最新尝试的JavaScript函数
function DoElement() { if (showAll) return; let el = this; if (isImg(el)) { //attach load event - needed 1) as we need to catch it after it is switched for the blankImg, 2) in case the img gets changed to something else later DoLoadEventListener(el, true); //see if not yet loaded if (!el.complete) { //hide, to avoid flash until load event is handled MarkWizmaged(el, true); DoHidden(el, true); return; } let elWidth = el.width, elHeight = el.height; if (el.src == blankImg && !el.srcset) { //was successfully replaced DoHidden(el, false); } else if ((elWidth == 0 || elWidth > _settings.maxSafe) && (elHeight == 0 || elHeight > _settings.maxSafe)) { //needs to be hidden - we need to catch 0 too, as sometimes images start off as zero DoMouseEventListeners(el, true); if (!el.wzmHasTitleAndSizeSetup) { el.style.width = elWidth + 'px'; el.style.height = elHeight + 'px'; if (!el.title) if (el.alt) el.title = el.alt; else { el.src.match(/([-\w]+)(\.[\w]+)?$/i); el.title = RegExp.$1; } el.wzmHasTitleAndSizeSetup = true; } DoHidden(el, true); DoImgSrc(el, true); DoWizmageBG(el, true); el.src = blankImg; var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.width = el.width; canvas.height = el.height; context.drawImage(el, 0, 0); var imageData = context.getImageData(0, 0, el.width, el.height); // Create a Blob object from the image data var blob = new Blob([imageData.data.buffer], { type: 'image/png' }); // Create a FormData object and append the blob to it var formData = new FormData(); formData.append('image', blob, 'image.png'); // Send the request to the server fetch('http://localhost:8000/predict', { method: 'POST', body: formData }) .then((response) => response.json()) .then((data) => { if (data.prediction === 'nudity') { MarkWizmaged(el, true); DoHidden(el, true); } else { MarkWizmaged(el, false); DoHidden(el, false); } }) .catch((error) => { console.error('Error:', error); }); }
本地部署的Python后端代码
from fastai.vision.all import * from starlette.applications import Starlette from starlette.middleware.cors import CORSMiddleware from starlette.responses import JSONResponse import io import base64 app = Starlette() app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_headers=['*'], allow_methods=['*']) # Load the trained model learn = load_learner('/Users/.../Documents/Personal/WorkSafe/Backend/resnet18.pkl') # Define the prediction route @app.route("/predict", methods=["POST"]) async def predict(request): data = await request.form() image = data.get("image", None) if image is not None: # Retrieve the file data from the form image_data = await image.read() # Reset the stream position to the beginning image_data.seek(0) img = Image.open(io.BytesIO(image_data)) img = img.convert('RGB') # Make the prediction prediction, _, _ = learn.predict(img) return JSONResponse({'prediction': str(prediction)})
运行错误信息
第一次错误
response = await func(request) File "/Users/shaulsaitowitz/Documents/Personal/WorkSafe/Backend/worksafe_app.py", line 26, in predict img = Image.open(io.BytesIO(image_data)) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/PIL/Image.py", line 2967, in open raise UnidentifiedImageError( PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7fdff4b43ef0>
添加seek(0)后新错误
Traceback (most recent call last): File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 428, in run_asgi result = await app( # type: ignore[func-returns-value] File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__ return await self.app(scope, receive, send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/applications.py", line 122, in __call__ await self.middleware_stack(scope, receive, send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/errors.py", line 184, in __call__ raise exc File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/cors.py", line 91, in __call__ await self.simple_response(scope, receive, send, request_headers=headers) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/cors.py", line 146, in simple_response await self.app(scope, receive, send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/exceptions.py", line 62, in __call__ await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 57, in wrapped_app raise exc File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 46, in wrapped_app await app(scope, receive, sender) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 727, in __call__ await route.handle(scope, receive, send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 285, in handle await self.app(scope, receive, send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 74, in app await wrap_app_handling_exceptions(app, request)(scope, receive, send) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 57, in wrapped_app raise exc File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 46, in wrapped_app await app(scope, receive, sender) File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 69, in app response = await func(request) File "/Users/shaulsaitowitz/Documents/Personal/WorkSafe/Backend/worksafe_app.py", line 27, in predict image_data.seek(0)
问题根源与解决方案
核心问题
- JavaScript端:
- 绘制canvas前已将图片替换为空白图,导致传递的是空白数据
- 直接用
imageData.data.buffer创建Blob无法生成合法PNG文件(仅原始像素数据,无PNG文件头)
- Python端:
await image.read()返回bytes类型,无seek()方法,该调用完全多余
修正后的代码
修正后的JavaScript函数
function DoElement() { if (showAll) return; let el = this; if (isImg(el)) { DoLoadEventListener(el, true); if (!el.complete) { MarkWizmaged(el, true); DoHidden(el, true); return; } let elWidth = el.width, elHeight = el.height; if (el.src == blankImg && !el.srcset) { DoHidden(el, false); } else if ((elWidth == 0 || elWidth > _settings.maxSafe) && (elHeight == 0 || elHeight > _settings.maxSafe)) { DoMouseEventListeners(el, true); if (!el.wzmHasTitleAndSizeSetup) { el.style.width = elWidth + 'px'; el.style.height = elHeight + 'px'; if (!el.title) if (el.alt) el.title = el.alt; else { el.src.match(/([-\w]+)(\.[\w]+)?$/i); el.title = RegExp.$1; } el.wzmHasTitleAndSizeSetup = true; } // 保存原图片地址,避免绘制空白图 const originalSrc = el.src; // 先隐藏图片,后续根据结果处理 DoHidden(el, true); DoImgSrc(el, true); DoWizmageBG(el, true); var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.width = el.width; canvas.height = el.height; // 绘制原图片到canvas context.drawImage(el, 0, 0); // 使用canvas.toBlob生成合法PNG格式Blob canvas.toBlob(function(blob) { var formData = new FormData(); formData.append('image', blob, 'image.png'); fetch('http://localhost:8000/predict', { method: 'POST', body: formData }) .then((response) => response.json()) .then((data) => { if (data.prediction === 'nudity') { MarkWizmaged(el, true); DoHidden(el, true); el.src = blankImg; // 确认违规再替换为空白图 } else { MarkWizmaged(el, false); DoHidden(el, false); el.src = originalSrc; // 恢复原图片 } }) .catch((error) => { console.error('Error:', error); // 请求失败时恢复原图片 el.src = originalSrc; DoHidden(el, false); }); }, 'image/png'); } } }
修正后的Python后端代码
from fastai.vision.all import * from starlette.applications import Starlette from starlette.middleware.cors import CORSMiddleware from starlette.responses import JSONResponse import io app = Starlette() app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_headers=['*'], allow_methods=['*']) learn = load_learner('/Users/.../Documents/Personal/WorkSafe/Backend/resnet18.pkl') @app.route("/predict", methods=["POST"]) async def predict(request): data = await request.form() image = data.get("image", None) if image is not None: image_data = await image.read() # 直接用bytes创建BytesIO,无需seek img = Image.open(io.BytesIO(image_data)).convert('RGB') prediction, _, _ = learn.predict(img) return JSONResponse({'prediction': str(prediction)})
内容的提问来源于stack exchange,提问作者saujosai
相关产品推荐
相关产品推荐

