@gradio/image 0.3.5 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -1
- package/Index.svelte +8 -2
- package/package.json +5 -5
- package/shared/ImagePreview.svelte +2 -1
- package/shared/ImageUploader.svelte +13 -1
- package/shared/Webcam.svelte +7 -10
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,61 @@
|
|
1
1
|
# @gradio/image
|
2
2
|
|
3
|
+
## 0.4.0
|
4
|
+
|
5
|
+
### Highlights
|
6
|
+
|
7
|
+
#### New `ImageEditor` component ([#6169](https://github.com/gradio-app/gradio/pull/6169) [`9caddc17b`](https://github.com/gradio-app/gradio/commit/9caddc17b1dea8da1af8ba724c6a5eab04ce0ed8))
|
8
|
+
|
9
|
+
A brand new component, completely separate from `Image` that provides simple editing capabilities.
|
10
|
+
|
11
|
+
- Set background images from file uploads, webcam, or just paste!
|
12
|
+
- Crop images with an improved cropping UI. App authors can event set specific crop size, or crop ratios (`1:1`, etc)
|
13
|
+
- Paint on top of any image (or no image) and erase any mistakes!
|
14
|
+
- The ImageEditor supports layers, confining draw and erase actions to that layer.
|
15
|
+
- More flexible access to data. The image component returns a composite image representing the final state of the canvas as well as providing the background and all layers as individual images.
|
16
|
+
- Fully customisable. All features can be enabled and disabled. Even the brush color swatches can be customised.
|
17
|
+
|
18
|
+
<video src="https://user-images.githubusercontent.com/12937446/284027169-31188926-fd16-4a1c-8718-998e7aae4695.mp4" autoplay muted></video>
|
19
|
+
|
20
|
+
```py
|
21
|
+
|
22
|
+
def fn(im):
|
23
|
+
im["composite"] # the full canvas
|
24
|
+
im["background"] # the background image
|
25
|
+
im["layers"] # a list of individual layers
|
26
|
+
|
27
|
+
|
28
|
+
im = gr.ImageEditor(
|
29
|
+
# decide which sources you'd like to accept
|
30
|
+
sources=["upload", "webcam", "clipboard"],
|
31
|
+
# set a cropsize constraint, can either be a ratio or a concrete [width, height]
|
32
|
+
crop_size="1:1",
|
33
|
+
# enable crop (or disable it)
|
34
|
+
transforms=["crop"],
|
35
|
+
# customise the brush
|
36
|
+
brush=Brush(
|
37
|
+
default_size="25", # or leave it as 'auto'
|
38
|
+
color_mode="fixed", # 'fixed' hides the user swatches and colorpicker, 'defaults' shows it
|
39
|
+
default_color="hotpink", # html names are supported
|
40
|
+
colors=[
|
41
|
+
"rgba(0, 150, 150, 1)", # rgb(a)
|
42
|
+
"#fff", # hex rgb
|
43
|
+
"hsl(360, 120, 120)" # in fact any valid colorstring
|
44
|
+
]
|
45
|
+
),
|
46
|
+
brush=Eraser(default_size="25")
|
47
|
+
)
|
48
|
+
|
49
|
+
```
|
50
|
+
|
51
|
+
Thanks [@pngwn](https://github.com/pngwn)!
|
52
|
+
|
53
|
+
## 0.3.6
|
54
|
+
|
55
|
+
### Fixes
|
56
|
+
|
57
|
+
- [#6441](https://github.com/gradio-app/gradio/pull/6441) [`2f805a7dd`](https://github.com/gradio-app/gradio/commit/2f805a7dd3d2b64b098f659dadd5d01258290521) - Small but important bugfixes for gr.Image: The upload event was not triggering at all. The paste-from-clipboard was not triggering an upload event. The clear button was not triggering a change event. The change event was triggering infinitely. Uploaded images were not preserving their original names. Uploading a new image should clear out the previous image. Thanks [@freddyaboulton](https://github.com/freddyaboulton)!
|
58
|
+
|
3
59
|
## 0.3.5
|
4
60
|
|
5
61
|
### Patch Changes
|
@@ -196,4 +252,4 @@ Thanks [@pngwn](https://github.com/pngwn)!
|
|
196
252
|
|
197
253
|
### Features
|
198
254
|
|
199
|
-
- [#4979](https://github.com/gradio-app/gradio/pull/4979) [`44ac8ad0`](https://github.com/gradio-app/gradio/commit/44ac8ad08d82ea12c503dde5c78f999eb0452de2) - Allow setting sketch color default. Thanks [@aliabid94](https://github.com/aliabid94)!
|
255
|
+
- [#4979](https://github.com/gradio-app/gradio/pull/4979) [`44ac8ad0`](https://github.com/gradio-app/gradio/commit/44ac8ad08d82ea12c503dde5c78f999eb0452de2) - Allow setting sketch color default. Thanks [@aliabid94](https://github.com/aliabid94)!
|
package/Index.svelte
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
export { default as BaseImageUploader } from "./shared/ImageUploader.svelte";
|
6
6
|
export { default as BaseStaticImage } from "./shared/ImagePreview.svelte";
|
7
7
|
export { default as BaseExample } from "./Example.svelte";
|
8
|
+
export { default as BaseImage } from "./shared/Image.svelte";
|
8
9
|
</script>
|
9
10
|
|
10
11
|
<script lang="ts">
|
@@ -61,7 +62,9 @@
|
|
61
62
|
share: ShareData;
|
62
63
|
}>;
|
63
64
|
|
64
|
-
$:
|
65
|
+
$: url = _value?.url;
|
66
|
+
$: url && gradio.dispatch("change");
|
67
|
+
|
65
68
|
let dragging: boolean;
|
66
69
|
let active_tool: null | "webcam" = null;
|
67
70
|
</script>
|
@@ -127,7 +130,10 @@
|
|
127
130
|
{root}
|
128
131
|
{sources}
|
129
132
|
on:edit={() => gradio.dispatch("edit")}
|
130
|
-
on:clear={() =>
|
133
|
+
on:clear={() => {
|
134
|
+
gradio.dispatch("clear");
|
135
|
+
gradio.dispatch("change");
|
136
|
+
}}
|
131
137
|
on:stream={() => gradio.dispatch("stream")}
|
132
138
|
on:drag={({ detail }) => (dragging = detail)}
|
133
139
|
on:upload={() => gradio.dispatch("upload")}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@gradio/image",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.4.0",
|
4
4
|
"description": "Gradio UI packages",
|
5
5
|
"type": "module",
|
6
6
|
"author": "",
|
@@ -10,12 +10,12 @@
|
|
10
10
|
"cropperjs": "^1.5.12",
|
11
11
|
"lazy-brush": "^1.0.1",
|
12
12
|
"resize-observer-polyfill": "^1.5.1",
|
13
|
-
"@gradio/atoms": "^0.
|
13
|
+
"@gradio/atoms": "^0.3.0",
|
14
|
+
"@gradio/icons": "^0.3.0",
|
14
15
|
"@gradio/client": "^0.8.1",
|
15
|
-
"@gradio/statustracker": "^0.
|
16
|
-
"@gradio/
|
16
|
+
"@gradio/statustracker": "^0.4.0",
|
17
|
+
"@gradio/upload": "^0.5.0",
|
17
18
|
"@gradio/utils": "^0.2.0",
|
18
|
-
"@gradio/upload": "^0.4.1",
|
19
19
|
"@gradio/wasm": "^0.3.0"
|
20
20
|
},
|
21
21
|
"main_changeset": true,
|
@@ -40,7 +40,7 @@
|
|
40
40
|
<a
|
41
41
|
href={value.url}
|
42
42
|
target={window.__is_colab__ ? "_blank" : null}
|
43
|
-
download={"image"}
|
43
|
+
download={value.orig_name || "image"}
|
44
44
|
>
|
45
45
|
<IconButton Icon={Download} label={i18n("common.download")} />
|
46
46
|
</a>
|
@@ -71,6 +71,7 @@
|
|
71
71
|
height: var(--size-full);
|
72
72
|
object-fit: contain;
|
73
73
|
display: block;
|
74
|
+
border-radius: var(--radius-lg);
|
74
75
|
}
|
75
76
|
|
76
77
|
.selectable {
|
@@ -33,10 +33,12 @@
|
|
33
33
|
export let i18n: I18nFormatter;
|
34
34
|
|
35
35
|
let upload: Upload;
|
36
|
+
let uploading = false;
|
36
37
|
export let active_tool: "webcam" | null = null;
|
37
38
|
|
38
39
|
function handle_upload({ detail }: CustomEvent<FileData>): void {
|
39
40
|
value = normalise_file(detail, root, null);
|
41
|
+
dispatch("upload");
|
40
42
|
}
|
41
43
|
|
42
44
|
async function handle_save(img_blob: Blob | any): Promise<void> {
|
@@ -52,6 +54,8 @@
|
|
52
54
|
pending = false;
|
53
55
|
}
|
54
56
|
|
57
|
+
$: if (uploading) value = null;
|
58
|
+
|
55
59
|
$: value && !value.url && (value = normalise_file(value, root, null));
|
56
60
|
|
57
61
|
const dispatch = createEventDispatcher<{
|
@@ -111,6 +115,7 @@
|
|
111
115
|
for (let i = 0; i < items.length; i++) {
|
112
116
|
const type = items[i].types.find((t) => t.startsWith("image/"));
|
113
117
|
if (type) {
|
118
|
+
value = null;
|
114
119
|
items[i].getType(type).then(async (blob) => {
|
115
120
|
const f = await upload.load_files([
|
116
121
|
new File([blob], `clipboard.${type.replace("image/", "")}`)
|
@@ -139,12 +144,18 @@
|
|
139
144
|
|
140
145
|
<div data-testid="image" class="image-container">
|
141
146
|
{#if value?.url}
|
142
|
-
<ClearImage
|
147
|
+
<ClearImage
|
148
|
+
on:remove_image={() => {
|
149
|
+
value = null;
|
150
|
+
dispatch("clear");
|
151
|
+
}}
|
152
|
+
/>
|
143
153
|
{/if}
|
144
154
|
<div class="upload-container">
|
145
155
|
<Upload
|
146
156
|
hidden={value !== null || active_tool === "webcam"}
|
147
157
|
bind:this={upload}
|
158
|
+
bind:uploading
|
148
159
|
bind:dragging
|
149
160
|
filetype="image/*"
|
150
161
|
on:load={handle_upload}
|
@@ -187,6 +198,7 @@
|
|
187
198
|
on:click={() => handle_toolbar(source)}
|
188
199
|
Icon={sources_meta[source].icon}
|
189
200
|
size="large"
|
201
|
+
label="{source}-image-toolbar-btn"
|
190
202
|
padded={false}
|
191
203
|
/>
|
192
204
|
{/each}
|
package/shared/Webcam.svelte
CHANGED
@@ -15,21 +15,17 @@
|
|
15
15
|
|
16
16
|
const dispatch = createEventDispatcher<{
|
17
17
|
stream: undefined;
|
18
|
-
capture:
|
19
|
-
| {
|
20
|
-
data: FileReader["result"];
|
21
|
-
name: string;
|
22
|
-
is_example?: boolean;
|
23
|
-
is_file: boolean;
|
24
|
-
}
|
25
|
-
| Blob;
|
18
|
+
capture: Blob;
|
26
19
|
error: string;
|
27
20
|
start_recording: undefined;
|
28
21
|
stop_recording: undefined;
|
29
22
|
}>();
|
30
23
|
|
31
24
|
onMount(() => (canvas = document.createElement("canvas")));
|
32
|
-
|
25
|
+
const size = {
|
26
|
+
width: { ideal: 1920 },
|
27
|
+
height: { ideal: 1440 }
|
28
|
+
};
|
33
29
|
async function access_webcam(device_id?: string): Promise<void> {
|
34
30
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
35
31
|
dispatch("error", i18n("image.no_webcam_support"));
|
@@ -37,7 +33,7 @@
|
|
37
33
|
}
|
38
34
|
try {
|
39
35
|
stream = await navigator.mediaDevices.getUserMedia({
|
40
|
-
video: device_id ? { deviceId: { exact: device_id } } :
|
36
|
+
video: device_id ? { deviceId: { exact: device_id }, ...size } : size,
|
41
37
|
audio: include_audio
|
42
38
|
});
|
43
39
|
video_source.srcObject = stream;
|
@@ -95,6 +91,7 @@
|
|
95
91
|
ReaderObj.onload = function (e): void {
|
96
92
|
if (e.target) {
|
97
93
|
dispatch("capture", {
|
94
|
+
//@ts-ignore
|
98
95
|
data: e.target.result,
|
99
96
|
name: "sample." + mimeType.substring(6),
|
100
97
|
is_example: false,
|