mirror of
https://github.com/gradio-app/gradio.git
synced 2025-02-23 11:39:17 +08:00
allow the canvas size to be set on the ImageEditor
(#8127)
* add canvas size kwarg to imageeditor * add changeset * fix tests * fix cropsize * changes * notebooks * update docstrings * fix type * fix undefined dimensions * Update image_editor.py Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * fix type * format --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com> Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
parent
f5b710c919
commit
24b2286a22
6
.changeset/real-chefs-admire.md
Normal file
6
.changeset/real-chefs-admire.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"@gradio/imageeditor": minor
|
||||
"gradio": minor
|
||||
---
|
||||
|
||||
feat:allow the canvas size to be set on the `ImageEditor`
|
@ -133,7 +133,7 @@ class ImageEditor(Component):
|
||||
image_mode: Literal[
|
||||
"1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "LAB", "HSV", "I", "F"
|
||||
] = "RGBA",
|
||||
sources: Iterable[Literal["upload", "webcam", "clipboard"]] = (
|
||||
sources: Iterable[Literal["upload", "webcam", "clipboard"]] | None = (
|
||||
"upload",
|
||||
"webcam",
|
||||
"clipboard",
|
||||
@ -160,12 +160,13 @@ class ImageEditor(Component):
|
||||
brush: Brush | None | Literal[False] = None,
|
||||
format: str = "webp",
|
||||
layers: bool = True,
|
||||
canvas_size: tuple[int, int] | None = None,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
value: Optional initial image(s) to populate the image editor. Should be a dictionary with keys: `background`, `layers`, and `composite`. The values corresponding to `background` and `composite` should be images or None, while `layers` should be a list of images. Images can be of type PIL.Image, np.array, or str filepath/URL. Or, the value can be a callable, in which case the function will be called whenever the app loads to set the initial value of the component.
|
||||
height: The height of the displayed images, specified in pixels if a number is passed, or in CSS units if a string is passed.
|
||||
width: The width of the displayed images, specified in pixels if a number is passed, or in CSS units if a string is passed.
|
||||
height: The height of the component container, specified in pixels if a number is passed, or in CSS units if a string is passed.
|
||||
width: The width of the component container, specified in pixels if a number is passed, or in CSS units if a string is passed.
|
||||
image_mode: "RGB" if color, or "L" if black and white. See https://pillow.readthedocs.io/en/stable/handbook/concepts.html for other supported image modes and their meaning.
|
||||
sources: List of sources that can be used to set the background image. "upload" creates a box where user can drop an image file, "webcam" allows user to take snapshot from their webcam, "clipboard" allows users to paste an image from the clipboard.
|
||||
type: The format the images are converted to before being passed into the prediction function. "numpy" converts the images to numpy arrays with shape (height, width, 3) and values from 0 to 255, "pil" converts the images to PIL image objects, "filepath" passes images as str filepaths to temporary copies of the images.
|
||||
@ -189,7 +190,7 @@ class ImageEditor(Component):
|
||||
brush: The options for the brush tool in the image editor. Should be an instance of the `gr.Brush` class, or None to use the default settings. Can also be False to hide the brush tool, which will also hide the eraser tool.
|
||||
format: Format to save image if it does not already have a valid format (e.g. if the image is being returned to the frontend as a numpy array or PIL Image). The format should be supported by the PIL library. This parameter has no effect on SVG files.
|
||||
layers: If True, will allow users to add layers to the image. If False, the layers option will be hidden.
|
||||
|
||||
canvas_size: The size of the default canvas in pixels. If a tuple, the first value is the width and the second value is the height. If None, the canvas size will be the same as the background image or 800 x 600 if no background image is provided.
|
||||
"""
|
||||
self._selectable = _selectable
|
||||
self.mirror_webcam = mirror_webcam
|
||||
@ -205,12 +206,15 @@ class ImageEditor(Component):
|
||||
valid_sources = ["upload", "webcam", "clipboard"]
|
||||
if isinstance(sources, str):
|
||||
sources = [sources] # type: ignore
|
||||
for source in sources:
|
||||
if source not in valid_sources:
|
||||
raise ValueError(
|
||||
f"`sources` must be a list consisting of elements in {valid_sources}"
|
||||
)
|
||||
self.sources = sources
|
||||
if sources is not None:
|
||||
for source in sources:
|
||||
if source not in valid_sources:
|
||||
raise ValueError(
|
||||
f"`sources` must be a list consisting of elements in {valid_sources}"
|
||||
)
|
||||
self.sources = sources
|
||||
else:
|
||||
self.sources = []
|
||||
|
||||
self.show_download_button = show_download_button
|
||||
|
||||
@ -227,6 +231,7 @@ class ImageEditor(Component):
|
||||
self.blob_storage: dict[str, EditorDataBlobs] = {}
|
||||
self.format = format
|
||||
self.layers = layers
|
||||
self.canvas_size = canvas_size
|
||||
|
||||
super().__init__(
|
||||
label=label,
|
||||
|
@ -109,6 +109,7 @@ class Sketchpad(components.ImageEditor):
|
||||
brush: Brush | None = None,
|
||||
format: str = "webp",
|
||||
layers: bool = True,
|
||||
canvas_size: tuple[int, int] | None = None,
|
||||
):
|
||||
if not brush:
|
||||
brush = Brush(colors=["#000000"], color_mode="fixed")
|
||||
@ -140,6 +141,7 @@ class Sketchpad(components.ImageEditor):
|
||||
brush=brush,
|
||||
format=format,
|
||||
layers=layers,
|
||||
canvas_size=canvas_size,
|
||||
)
|
||||
|
||||
|
||||
@ -182,6 +184,7 @@ class Paint(components.ImageEditor):
|
||||
brush: Brush | None = None,
|
||||
format: str = "webp",
|
||||
layers: bool = True,
|
||||
canvas_size: tuple[int, int] | None = None,
|
||||
):
|
||||
super().__init__(
|
||||
value=value,
|
||||
@ -211,6 +214,7 @@ class Paint(components.ImageEditor):
|
||||
brush=brush,
|
||||
format=format,
|
||||
layers=layers,
|
||||
canvas_size=canvas_size,
|
||||
)
|
||||
|
||||
|
||||
@ -257,6 +261,7 @@ class ImageMask(components.ImageEditor):
|
||||
brush: Brush | None = None,
|
||||
format: str = "webp",
|
||||
layers: bool = True,
|
||||
canvas_size: tuple[int, int] | None = None,
|
||||
):
|
||||
if not brush:
|
||||
brush = Brush(colors=["#000000"], color_mode="fixed")
|
||||
@ -288,6 +293,7 @@ class ImageMask(components.ImageEditor):
|
||||
brush=brush,
|
||||
format=format,
|
||||
layers=layers,
|
||||
canvas_size=canvas_size,
|
||||
)
|
||||
|
||||
|
||||
|
@ -54,6 +54,7 @@
|
||||
export let server: {
|
||||
accept_blobs: (a: any) => void;
|
||||
};
|
||||
export let canvas_size: [number, number] | undefined;
|
||||
|
||||
export let gradio: Gradio<{
|
||||
change: never;
|
||||
@ -175,6 +176,7 @@
|
||||
/>
|
||||
|
||||
<InteractiveImageEditor
|
||||
{canvas_size}
|
||||
on:change={() => handle_history_change()}
|
||||
bind:image_id
|
||||
{crop_size}
|
||||
|
@ -62,7 +62,7 @@
|
||||
import { create_pixi_app, type ImageBlobs } from "./utils/pixi";
|
||||
import Controls from "./Controls.svelte";
|
||||
export let antialias = true;
|
||||
export let crop_size: [number, number] = [800, 600];
|
||||
export let crop_size: [number, number] | undefined;
|
||||
export let changeable = false;
|
||||
export let history: boolean;
|
||||
export let bg = false;
|
||||
@ -73,8 +73,13 @@
|
||||
change: void;
|
||||
}>();
|
||||
export let crop_constraint = false;
|
||||
export let canvas_size: [number, number] | undefined;
|
||||
|
||||
let dimensions = writable(crop_size);
|
||||
$: orig_canvas_size = canvas_size;
|
||||
|
||||
const BASE_DIMENSIONS: [number, number] = canvas_size || [800, 600];
|
||||
|
||||
let dimensions = writable(BASE_DIMENSIONS);
|
||||
export let height = 0;
|
||||
|
||||
let editor_box: EditorContext["editor_box"] = writable({
|
||||
@ -260,7 +265,10 @@
|
||||
$: $position_spring && get_dimensions(canvas_wrap, pixi_target);
|
||||
|
||||
export function handle_remove(): void {
|
||||
editor_context.reset(true, $dimensions);
|
||||
editor_context.reset(
|
||||
true,
|
||||
orig_canvas_size ? orig_canvas_size : $dimensions
|
||||
);
|
||||
if (!sources.length) {
|
||||
set_tool("draw");
|
||||
} else {
|
||||
@ -270,7 +278,12 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const app = create_pixi_app(pixi_target, ...crop_size, antialias);
|
||||
const _size = (canvas_size ? canvas_size : crop_size) || [800, 600];
|
||||
const app = create_pixi_app({
|
||||
target: pixi_target,
|
||||
dimensions: _size,
|
||||
antialias
|
||||
});
|
||||
|
||||
function resize(width: number, height: number): void {
|
||||
app.resize(width, height);
|
||||
|
@ -44,6 +44,7 @@
|
||||
export let layers: boolean;
|
||||
export let accept_blobs: (a: any) => void;
|
||||
export let status: "pending" | "complete" | "error" = "complete";
|
||||
export let canvas_size: [number, number] | undefined;
|
||||
export let realtime: boolean;
|
||||
export let upload: Client["upload"];
|
||||
export let stream_handler: Client["eventSource_factory"];
|
||||
@ -203,6 +204,8 @@
|
||||
label={label || i18n("image.image")}
|
||||
/>
|
||||
<ImageEditor
|
||||
{canvas_size}
|
||||
crop_size={Array.isArray(crop_size) ? crop_size : undefined}
|
||||
bind:this={editor}
|
||||
bind:height={editor_height}
|
||||
{changeable}
|
||||
|
@ -73,12 +73,15 @@ export interface PixiApp {
|
||||
* @param antialias Whether to use antialiasing
|
||||
* @returns object with pixi container and renderer
|
||||
*/
|
||||
export function create_pixi_app(
|
||||
target: HTMLElement,
|
||||
width: number,
|
||||
height: number,
|
||||
antialias: boolean
|
||||
): PixiApp {
|
||||
export function create_pixi_app({
|
||||
target,
|
||||
dimensions: [width, height],
|
||||
antialias
|
||||
}: {
|
||||
target: HTMLElement;
|
||||
dimensions: [number, number];
|
||||
antialias: boolean;
|
||||
}): PixiApp {
|
||||
const ratio = window.devicePixelRatio || 1;
|
||||
const app = new Application({
|
||||
width,
|
||||
|
@ -641,6 +641,7 @@ class TestImageEditor:
|
||||
"server_fns": ["accept_blobs"],
|
||||
"format": "webp",
|
||||
"layers": True,
|
||||
"canvas_size": None,
|
||||
}
|
||||
|
||||
def test_process_example(self):
|
||||
|
Loading…
Reference in New Issue
Block a user