Allow setting sketch color default (#4979)

* changes

* add changeset

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

* changes

---------

Co-authored-by: pngwn <hello@pngwn.io>
Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
aliabid94 2023-07-25 11:39:51 -07:00 committed by GitHub
parent 3f8c210b01
commit 44ac8ad08d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 95 additions and 91 deletions

View File

@ -0,0 +1,7 @@
---
"@gradio/app": minor
"@gradio/image": minor
"gradio": minor
---
feat:Allow setting sketch color default

View File

@ -9,6 +9,7 @@
- Added autofocus argument to Textbox by [@aliabid94](https://github.com/aliabid94) in [PR 4978](https://github.com/gradio-app/gradio/pull/4978)
- The `gr.ChatInterface` UI now converts the "Submit" button to a "Stop" button in ChatInterface while streaming, which can be used to pause generation. By [@abidlabs](https://github.com/abidlabs) in [PR 4971](https://github.com/gradio-app/gradio/pull/4971).
- Add a `border_color_accent_subdued` theme variable to add a subdued border color to accented items. This is used by chatbot user messages. Set the value of this variable in `Default` theme to `*primary_200`. By [@freddyaboulton](https://github.com/freddyaboulton) in [PR 4989](https://github.com/gradio-app/gradio/pull/4989)
- Add default sketch color argument `brush_color`. Also, masks drawn on images are now slightly translucent (and mask color can also be set via brush_color). By [@aliabid94](https://github.com/aliabid94) in [PR 4979](https://github.com/gradio-app/gradio/pull/4979)
### Bug Fixes:

View File

@ -81,6 +81,8 @@ class Image(
elem_classes: list[str] | str | None = None,
mirror_webcam: bool = True,
brush_radius: float | None = None,
brush_color: str = "#000000",
mask_opacity: float = 0.7,
show_share_button: bool | None = None,
**kwargs,
):
@ -109,9 +111,13 @@ class Image(
elem_classes: An optional list of strings that are assigned as the classes of this component in the HTML DOM. Can be used for targeting CSS styles.
mirror_webcam: If True webcam will be mirrored. Default is True.
brush_radius: Size of the brush for Sketch. Default is None which chooses a sensible default
brush_color: Color of the brush for Sketch as hex string. Default is "#000000".
mask_opacity: Opacity of mask drawn on image, as a value between 0 and 1.
show_share_button: If True, will show a share icon in the corner of the component that allows user to share outputs to Hugging Face Spaces Discussions. If False, icon does not appear. If set to None (default behavior), then the icon appears if this Gradio app is launched on Spaces, but not otherwise.
"""
self.brush_radius = brush_radius
self.brush_color = brush_color
self.mask_opacity = mask_opacity
self.mirror_webcam = mirror_webcam
valid_types = ["numpy", "pil", "filepath"]
if type not in valid_types:
@ -178,6 +184,8 @@ class Image(
"streaming": self.streaming,
"mirror_webcam": self.mirror_webcam,
"brush_radius": self.brush_radius,
"brush_color": self.brush_color,
"mask_opacity": self.mask_opacity,
"selectable": self.selectable,
"show_share_button": self.show_share_button,
"show_download_button": self.show_download_button,
@ -198,6 +206,8 @@ class Image(
interactive: bool | None = None,
visible: bool | None = None,
brush_radius: float | None = None,
brush_color: str | None = None,
mask_opacity: float | None = None,
show_share_button: bool | None = None,
):
return {
@ -213,6 +223,8 @@ class Image(
"visible": visible,
"value": value,
"brush_radius": brush_radius,
"brush_color": brush_color,
"mask_opacity": mask_opacity,
"show_share_button": show_share_button,
"__type__": "update",
}
@ -276,6 +288,10 @@ class Image(
if self.tool == "sketch" and self.source in ["upload", "webcam"]:
mask_im = processing_utils.decode_base64_to_image(mask)
if mask_im.mode == "RGBA": # whiten any opaque pixels in the mask
alpha_data = mask_im.getchannel("A").convert("L")
mask_im = _Image.merge("RGB", [alpha_data, alpha_data, alpha_data])
return {
"image": self._format_image(im),
"mask": self._format_image(mask_im),

View File

@ -68,6 +68,7 @@ class Webcam(components.Image):
elem_id: str | None = None,
mirror_webcam: bool = True,
brush_radius: float | None = None,
brush_color: str = "#000000",
**kwargs,
):
super().__init__(
@ -86,6 +87,7 @@ class Webcam(components.Image):
elem_id=elem_id,
mirror_webcam=mirror_webcam,
brush_radius=brush_radius,
brush_color=brush_color,
**kwargs,
)
@ -115,6 +117,7 @@ class Sketchpad(components.Image):
elem_id: str | None = None,
mirror_webcam: bool = True,
brush_radius: float | None = None,
brush_color: str = "#000000",
**kwargs,
):
super().__init__(
@ -133,6 +136,7 @@ class Sketchpad(components.Image):
elem_id=elem_id,
mirror_webcam=mirror_webcam,
brush_radius=brush_radius,
brush_color=brush_color,
**kwargs,
)
@ -162,6 +166,7 @@ class Paint(components.Image):
elem_id: str | None = None,
mirror_webcam: bool = True,
brush_radius: float | None = None,
brush_color: str = "#000000",
**kwargs,
):
super().__init__(
@ -180,6 +185,7 @@ class Paint(components.Image):
elem_id=elem_id,
mirror_webcam=mirror_webcam,
brush_radius=brush_radius,
brush_color=brush_color,
**kwargs,
)
@ -209,6 +215,7 @@ class ImageMask(components.Image):
elem_id: str | None = None,
mirror_webcam: bool = True,
brush_radius: float | None = None,
brush_color: str = "#000000",
**kwargs,
):
super().__init__(
@ -227,6 +234,7 @@ class ImageMask(components.Image):
elem_id=elem_id,
mirror_webcam=mirror_webcam,
brush_radius=brush_radius,
brush_color=brush_color,
**kwargs,
)
@ -256,6 +264,7 @@ class ImagePaint(components.Image):
elem_id: str | None = None,
mirror_webcam: bool = True,
brush_radius: float | None = None,
brush_color: str = "#000000",
**kwargs,
):
super().__init__(
@ -274,6 +283,7 @@ class ImagePaint(components.Image):
elem_id=elem_id,
mirror_webcam=mirror_webcam,
brush_radius=brush_radius,
brush_color=brush_color,
**kwargs,
)
@ -303,6 +313,7 @@ class Pil(components.Image):
elem_id: str | None = None,
mirror_webcam: bool = True,
brush_radius: float | None = None,
brush_color: str = "#000000",
**kwargs,
):
super().__init__(
@ -321,6 +332,7 @@ class Pil(components.Image):
elem_id=elem_id,
mirror_webcam=mirror_webcam,
brush_radius=brush_radius,
brush_color=brush_color,
**kwargs,
)

View File

@ -54,6 +54,8 @@ XRAY_CONFIG = {
"type": "image",
"props": {
"image_mode": "RGB",
"brush_color": "#000000",
"mask_opacity": 0.7,
"source": "upload",
"tool": "editor",
"streaming": False,
@ -127,6 +129,8 @@ XRAY_CONFIG = {
"type": "image",
"props": {
"image_mode": "RGB",
"brush_color": "#000000",
"mask_opacity": 0.7,
"source": "upload",
"tool": "editor",
"streaming": False,
@ -375,6 +379,8 @@ XRAY_CONFIG_DIFF_IDS = {
"type": "image",
"props": {
"image_mode": "RGB",
"brush_color": "#000000",
"mask_opacity": 0.7,
"source": "upload",
"tool": "editor",
"streaming": False,
@ -448,6 +454,8 @@ XRAY_CONFIG_DIFF_IDS = {
"type": "image",
"props": {
"image_mode": "RGB",
"brush_color": "#000000",
"mask_opacity": 0.7,
"source": "upload",
"tool": "editor",
"streaming": False,
@ -699,6 +707,8 @@ XRAY_CONFIG_WITH_MISTAKE = {
"type": "image",
"props": {
"image_mode": "RGB",
"brush_color": "#000000",
"mask_opacity": 0.7,
"source": "upload",
"streaming": False,
"mirror_webcam": True,
@ -750,6 +760,8 @@ XRAY_CONFIG_WITH_MISTAKE = {
"type": "image",
"props": {
"image_mode": "RGB",
"brush_color": "#000000",
"mask_opacity": 0.7,
"source": "upload",
"tool": "editor",
"streaming": False,

View File

@ -25,6 +25,8 @@
export let mirror_webcam: boolean;
export let shape: [number, number];
export let brush_radius: number;
export let brush_color: string;
export let mask_opacity: number;
export let selectable = false;
export let container = true;
export let scale: number | null = null;
@ -78,11 +80,13 @@
{:else}
<Image
{brush_radius}
{brush_color}
{shape}
bind:value
{source}
{tool}
{selectable}
{mask_opacity}
on:edit
on:clear
on:stream

View File

@ -28,6 +28,8 @@
export let pending: boolean = false;
export let mirror_webcam: boolean;
export let brush_radius: number;
export let brush_color = "#000000";
export let mask_opacity;
export let selectable: boolean = false;
let sketch: Sketch;
@ -139,9 +141,6 @@
mode = "editor";
}
}
$: brush_color = mode == "mask" ? "#000000" : "#000";
let value_img;
let max_height;
let max_width;
@ -238,6 +237,7 @@
bind:this={sketch}
bind:brush_radius
bind:brush_color
{mask_opacity}
on:change={handle_save}
{mode}
width={img_width || max_width}

View File

@ -13,6 +13,7 @@
export let mode = "sketch";
export let brush_color = "#0b0f19";
export let brush_radius;
export let mask_opacity = 0.7;
export let source;
export let width = 400;
@ -66,31 +67,28 @@
function mid_point(p1, p2) {
return {
x: p1.x + (p2.x - p1.x) / 2,
y: p1.y + (p2.y - p1.y) / 2
y: p1.y + (p2.y - p1.y) / 2,
};
}
const canvas_types = [
{
name: "interface",
zIndex: 15
},
{
name: "drawing",
zIndex: 11
},
{
name: "temp",
zIndex: 12
zIndex: 15,
},
{
name: "mask",
zIndex: -1
zIndex: 13,
opacity: mask_opacity,
},
{
name: "temp_fake",
zIndex: -2
}
name: "drawing",
zIndex: 11,
},
{
name: "temp",
zIndex: 12,
},
];
let canvas = {};
@ -185,8 +183,8 @@
enabled: true,
initialPoint: {
x: width / 2,
y: height / 2
}
y: height / 2,
},
});
canvas_observer = new ResizeObserver((entries, observer, ...rest) => {
@ -243,9 +241,6 @@
lines = _lines;
ctx.drawing.drawImage(canvas.temp, 0, 0, width, height);
if (mode === "mask") {
ctx.mask.drawImage(canvas.temp_fake, 0, 0, width, height);
}
if (lines.length == 0) {
dispatch("clear");
@ -270,7 +265,7 @@
return JSON.stringify({
lines: lines,
width: canvas_width,
height: canvas_height
height: canvas_height,
});
};
@ -280,16 +275,9 @@
draw_points({
points: _points,
brush_color,
brush_radius
brush_radius,
mask: mode === "mask",
});
if (mode === "mask") {
draw_fake_points({
points: _points,
brush_color,
brush_radius
});
}
});
saveLine({ brush_color, brush_radius });
@ -351,15 +339,14 @@
const container_dimensions = {
height: container_height,
width: container_height * (dimensions.width / dimensions.height)
width: container_height * (dimensions.width / dimensions.height),
};
await Promise.all([
set_canvas_size(canvas.interface, dimensions, container_dimensions),
set_canvas_size(canvas.drawing, dimensions, container_dimensions),
set_canvas_size(canvas.temp, dimensions, container_dimensions),
set_canvas_size(canvas.temp_fake, dimensions, container_dimensions),
set_canvas_size(canvas.mask, dimensions, container_dimensions, false)
set_canvas_size(canvas.mask, dimensions, container_dimensions, false),
]);
if (!brush_radius) {
@ -418,7 +405,7 @@
return {
x: ((clientX - rect.left) / rect.width) * width,
y: ((clientY - rect.top) / rect.height) * height
y: ((clientY - rect.top) / rect.height) * height,
};
};
@ -434,69 +421,39 @@
draw_points({
points: points,
brush_color,
brush_radius
brush_radius,
mask: mode === "mask",
});
if (mode === "mask") {
draw_fake_points({
points: points,
brush_color,
brush_radius
});
}
}
mouse_has_moved = true;
};
let draw_points = ({ points, brush_color, brush_radius }) => {
let draw_points = ({ points, brush_color, brush_radius, mask }) => {
if (!points || points.length < 2) return;
ctx.temp.lineJoin = "round";
ctx.temp.lineCap = "round";
let target_ctx = mask ? ctx.mask : ctx.temp;
target_ctx.lineJoin = "round";
target_ctx.lineCap = "round";
ctx.temp.strokeStyle = brush_color;
ctx.temp.lineWidth = brush_radius;
if (!points || points.length < 2) return;
target_ctx.strokeStyle = brush_color;
target_ctx.lineWidth = brush_radius;
let p1 = points[0];
let p2 = points[1];
ctx.temp.moveTo(p2.x, p2.y);
ctx.temp.beginPath();
target_ctx.moveTo(p2.x, p2.y);
target_ctx.beginPath();
for (var i = 1, len = points.length; i < len; i++) {
var midPoint = mid_point(p1, p2);
ctx.temp.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
target_ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
p1 = points[i];
p2 = points[i + 1];
}
ctx.temp.lineTo(p1.x, p1.y);
ctx.temp.stroke();
};
let draw_fake_points = ({ points, brush_color, brush_radius }) => {
if (!points || points.length < 2) return;
ctx.temp_fake.lineJoin = "round";
ctx.temp_fake.lineCap = "round";
ctx.temp_fake.strokeStyle = "#fff";
ctx.temp_fake.lineWidth = brush_radius;
let p1 = points[0];
let p2 = points[1];
ctx.temp_fake.moveTo(p2.x, p2.y);
ctx.temp_fake.beginPath();
for (var i = 1, len = points.length; i < len; i++) {
var midPoint = mid_point(p1, p2);
ctx.temp_fake.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
p1 = points[i];
p2 = points[i + 1];
}
ctx.temp_fake.lineTo(p1.x, p1.y);
ctx.temp_fake.stroke();
target_ctx.lineTo(p1.x, p1.y);
target_ctx.stroke();
};
let save_mask_line = () => {
if (points.length < 1) return;
points.length = 0;
ctx.mask.drawImage(canvas.temp_fake, 0, 0, width, height);
trigger_on_change();
};
@ -507,7 +464,7 @@
lines.push({
points: points.slice(),
brush_color: brush_color,
brush_radius
brush_radius,
});
if (mode !== "mask") {
@ -540,15 +497,7 @@
ctx.temp.fillRect(0, 0, width, height);
if (mode === "mask") {
ctx.temp_fake.clearRect(
0,
0,
canvas.temp_fake.width,
canvas.temp_fake.height
);
ctx.mask.clearRect(0, 0, width, height);
ctx.mask.fillStyle = "#000";
ctx.mask.fillRect(0, 0, width, height);
ctx.mask.clearRect(0, 0, canvas.mask.width, canvas.mask.height);
}
}
@ -587,7 +536,7 @@
export function get_image_data() {
return mode === "mask"
? canvas.mask.toDataURL("image/jpg")
? canvas.mask.toDataURL("image/png")
: canvas.drawing.toDataURL("image/jpg");
}
</script>
@ -603,10 +552,11 @@
Start drawing
</div>
{/if}
{#each canvas_types as { name, zIndex }}
{#each canvas_types as { name, zIndex, opacity }}
<canvas
key={name}
style=" z-index:{zIndex};"
style:opacity
class:lr={add_lr_border}
class:tb={!add_lr_border}
bind:this={canvas[name]}

View File

@ -660,6 +660,8 @@ class TestImage:
)
assert image_input.get_config() == {
"brush_radius": None,
"brush_color": "#000000",
"mask_opacity": 0.7,
"image_mode": "RGB",
"shape": None,
"source": "upload",