Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mockup speed enhancement #123

Merged
merged 17 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions mockup_package/mockup/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from .image import mockup as startMockup # noqa: F401
from .image import previewMockup # noqa: F401
from .mockup_generator import MockupGenerator # noqa: F401
1 change: 1 addition & 0 deletions mockup_package/mockup/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def get_parse_model(m):
get_output_name(m["device_id"], o): {
"image": "{}-{}.png".format(m["device_id"], o["name"]),
"screen_coord": o["coords"],
"name": o["name"],
}
for o in m["orientations"]
},
Expand Down
107 changes: 0 additions & 107 deletions mockup_package/mockup/image.py

This file was deleted.

112 changes: 112 additions & 0 deletions mockup_package/mockup/mockup_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import base64
import sys
from pathlib import Path
from typing import Any, Self
from pyodide.http import pyfetch
from mockup.image_generator import ImageGenerator as IG
import os


class MockupGenerator:
location: str
device_id: str
original_img_path: str
device_info: dict[str, Any]
orientation_name: str
device_path_prefix: str
device_mask_path_prefix: str
device_path: str
device_mask_path: str

def __init__(
self,
location,
device_id: str,
original_img_path: str,
device_info: dict[str, Any],
orientation_name: str,
):
self.location = location
self.device_id = device_id
self.original_img_path = original_img_path
self.device_info = device_info
self.orientation_name = orientation_name

self.device_path_prefix: str = (
f"{location.split('/')[0]}//{location.split('/')[2]}"
)
self.device_mask_path_prefix: str = (
self.device_path_prefix + "/images/mockup_mask_templates/"
)
self.device_path_prefix += "/images/mockup_templates/"
self.device_path: str = "./device.png"
self.device_mask_path: str = "./device_mask.png"

async def generate(
self: Self,
spec: dict[str, Any],
ig: IG,
) -> tuple[str, str, str]:
try:
await self.process_response(
self.device_path_prefix + str(spec["image"]),
self.device_path,
)
await self.process_response(
self.device_mask_path_prefix + str(spec["image"]),
self.device_mask_path,
)
except Exception as e:
print(e, file=sys.stderr)
# js.errorBox(e)
raise
ig.create_fit_coord_image(spec)
deviceView = str(spec["image"]).split("-")[-1].split(".")[0]
path = (
f"{os.path.splitext(os.path.basename(self.original_img_path))[0]}"
+ f"-{deviceView}.png"
)
ig.create_mockup_image(self.device_path, self.device_mask_path, path)
return (path, self.original_img_path, deviceView)

async def mockup(
self: Self,
):
ig = IG(self.original_img_path, self.device_id, self.device_info)
ig.create_fit_resolution_image()
mockups = list(ig.phone_models.get(self.device_id).get("mockups").values())

for spec in mockups:
if spec.get("name") == self.orientation_name:
output_img_path = await self.generate(spec, ig)

return output_img_path

raise Exception("Cannot find orientation", self.orientation_name)

async def download(self: Self, url: str):
filename = Path(url).name
response = await pyfetch(url)
if response.status == 200:
status = response.status
with open(filename, mode="wb") as file:
file.write(await response.bytes())
return filename, status
else:
status = response.status
filename = None
return filename, status

async def process_response(self: Self, url: str, path: str):
response_content = await self.download(url)
if response_content[1] == 200:
data = base64.b64encode(open(response_content[0], "rb").read())
data = data.decode("utf-8")
imgdata = base64.b64decode(data)
filename = path # I assume you have a way of picking unique filenames
with open(filename, "wb+") as f:
f.write(imgdata)
return filename
else:
src = None
return src
42 changes: 1 addition & 41 deletions public/image_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,10 @@
import io
import os

from js import Uint8Array, imageUploadList, previewJobQueue
from js import Uint8Array, previewJobQueue
from PIL import Image


async def upload_single_image_and_save_to_list(
origin_image, file_name, original_img_path_list
):
array_buf = Uint8Array.new(await origin_image.arrayBuffer())
bytes_list = bytearray(array_buf)
origin_bytes = io.BytesIO(bytes_list)
my_image = Image.open(origin_bytes)
filePath = f"./{file_name}.png"
original_img_path_list.append(filePath)
my_image.save(filePath)


async def upload_single_image(origin_image, file_name):
array_buf = Uint8Array.new(await origin_image.arrayBuffer())
bytes_list = bytearray(array_buf)
Expand All @@ -41,18 +29,6 @@ async def upload_file():
return original_img_path


async def upload_files():
original_img_path_list = []
for fileItem in imageUploadList:
basename, ext = os.path.splitext(fileItem.name)
if ext.lower() not in [".psd", ".jpg", ".jpeg", ".png"]:
return
await upload_single_image_and_save_to_list(
fileItem, basename, original_img_path_list
)
return original_img_path_list


def save_image(image):
print("image", image)
path = image[0]
Expand All @@ -65,19 +41,3 @@ def save_image(image):
dataurl = f"data:image/{ext};base64,{base64_utf8_str}"
print(basename)
return [f"img{basename}", dataurl]


def save_images(imageList):
returnList = []
for image in imageList:
path = image[0]
my_image = Image.open(path)
my_stream = io.BytesIO()
my_image.save(my_stream, format="PNG")
binary_fc = open(path, "rb").read()
base64_utf8_str = base64.b64encode(binary_fc).decode("utf-8")
basename, ext = os.path.splitext(path)
dataurl = f"data:image/{ext};base64,{base64_utf8_str}"
print(basename)
returnList.append([f"img{basename}", dataurl])
return returnList
Binary file modified public/mockup.zip
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,24 @@ async function initiatePyodide() {
return pyodide;
}

// Now only the first orientation model is generated for preview
async function runPreviewMockup(pyodide) {
async function runMockup(pyodide) {
let pythonNamespace = pyodide.globals.get("dict")();
await pyodide.runPythonAsync(
`
import mockup
from mockup import MockupGenerator
import image_process
from js import locationKey, deviceInfo, deviceId
origin_image_path = await image_process.upload_file()
print("start preview", origin_image_path)
output_img = await mockup.previewMockup(locationKey, deviceId, origin_image_path, deviceInfo)
from js import locationKey, deviceInfo, deviceId, orientationsQueue

async def runMockup():
origin_image_path = await image_process.upload_file()
orientation = orientationsQueue.shift()
print("start mockup", origin_image_path)
print("orientation", orientation)

mockup_generator = MockupGenerator(locationKey, deviceId, origin_image_path, deviceInfo, orientation)
return await mockup_generator.mockup()

output_img = await runMockup()
`,
{ globals: pythonNamespace },
);
Expand All @@ -47,19 +54,23 @@ async function runPreviewMockup(pyodide) {
async function main() {
let pyodideObject = initiatePyodide();
self["previewJobQueue"] = [];
self["orientationsQueue"] = [];
self.onmessage = async (event) => {
pyodideObject = await pyodideObject;

self["imageUploadList"] = undefined;
self["previewJobQueue"].push(event.data.imageUpload);
self["locationKey"] = event.data.location;
self["deviceId"] = event.data.deviceId;
self["deviceInfo"] = event.data.deviceInfo;
self["orientationsQueue"].push(event.data.orientation);

try {
let results = await runPreviewMockup(pyodideObject);
console.log("preview results", results);
self.postMessage({ ulid: event.data.ulid, results: results });
let results = await runMockup(pyodideObject);
console.log("mockup results", results);
self.postMessage({
ulid: event.data.ulid,
results: results,
});
} catch (error) {
self.postMessage({ ulid: event.data.ulid, error: error.message });
}
Expand Down
1 change: 1 addition & 0 deletions public/scripts/models/image-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ImageUpload {
message = null;
ulid = null;
previewUrl = null;
generatedMockups = [];

loadDimensionPromise = null;

Expand Down
Loading
Loading