@gradio/image 0.3.0-beta.5

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.
@@ -0,0 +1,204 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher, onMount } from "svelte";
3
+ import { Camera, Circle, Square } from "@gradio/icons";
4
+ import type { I18nFormatter } from "js/utils/src";
5
+
6
+ let video_source: HTMLVideoElement;
7
+ let canvas: HTMLCanvasElement;
8
+ export let streaming = false;
9
+ export let pending = false;
10
+
11
+ export let mode: "image" | "video" = "image";
12
+ export let mirror_webcam: boolean;
13
+ export let include_audio: boolean;
14
+ export let i18n: I18nFormatter;
15
+
16
+ const dispatch = createEventDispatcher<{
17
+ stream: undefined;
18
+ capture:
19
+ | {
20
+ data: FileReader["result"];
21
+ name: string;
22
+ is_example?: boolean;
23
+ is_file: boolean;
24
+ }
25
+ | string;
26
+ error: string;
27
+ start_recording: undefined;
28
+ stop_recording: undefined;
29
+ }>();
30
+
31
+ onMount(() => (canvas = document.createElement("canvas")));
32
+
33
+ async function access_webcam(): Promise<void> {
34
+ try {
35
+ stream = await navigator.mediaDevices.getUserMedia({
36
+ video: true,
37
+ audio: include_audio
38
+ });
39
+ video_source.srcObject = stream;
40
+ video_source.muted = true;
41
+ video_source.play();
42
+ } catch (err) {
43
+ if (err instanceof DOMException && err.name == "NotAllowedError") {
44
+ dispatch("error", i18n("image.allow_webcam_access"));
45
+ } else {
46
+ throw err;
47
+ }
48
+ }
49
+ }
50
+
51
+ function take_picture(): void {
52
+ var context = canvas.getContext("2d")!;
53
+
54
+ if (video_source.videoWidth && video_source.videoHeight) {
55
+ canvas.width = video_source.videoWidth;
56
+ canvas.height = video_source.videoHeight;
57
+ context.drawImage(
58
+ video_source,
59
+ 0,
60
+ 0,
61
+ video_source.videoWidth,
62
+ video_source.videoHeight
63
+ );
64
+
65
+ var data = canvas.toDataURL("image/png");
66
+ dispatch(streaming ? "stream" : "capture", data);
67
+ }
68
+ }
69
+
70
+ let recording = false;
71
+ let recorded_blobs: BlobPart[] = [];
72
+ let stream: MediaStream;
73
+ let mimeType: string;
74
+ let media_recorder: MediaRecorder;
75
+
76
+ function take_recording(): void {
77
+ if (recording) {
78
+ media_recorder.stop();
79
+ let video_blob = new Blob(recorded_blobs, { type: mimeType });
80
+ let ReaderObj = new FileReader();
81
+ ReaderObj.onload = function (e): void {
82
+ if (e.target) {
83
+ dispatch("capture", {
84
+ data: e.target.result,
85
+ name: "sample." + mimeType.substring(6),
86
+ is_example: false,
87
+ is_file: false
88
+ });
89
+ dispatch("stop_recording");
90
+ }
91
+ };
92
+ ReaderObj.readAsDataURL(video_blob);
93
+ } else {
94
+ dispatch("start_recording");
95
+ recorded_blobs = [];
96
+ let validMimeTypes = ["video/webm", "video/mp4"];
97
+ for (let validMimeType of validMimeTypes) {
98
+ if (MediaRecorder.isTypeSupported(validMimeType)) {
99
+ mimeType = validMimeType;
100
+ break;
101
+ }
102
+ }
103
+ if (mimeType === null) {
104
+ console.error("No supported MediaRecorder mimeType");
105
+ return;
106
+ }
107
+ media_recorder = new MediaRecorder(stream, {
108
+ mimeType: mimeType
109
+ });
110
+ media_recorder.addEventListener("dataavailable", function (e) {
111
+ recorded_blobs.push(e.data);
112
+ });
113
+ media_recorder.start(200);
114
+ }
115
+ recording = !recording;
116
+ }
117
+
118
+ access_webcam();
119
+
120
+ if (streaming && mode === "image") {
121
+ window.setInterval(() => {
122
+ if (video_source && !pending) {
123
+ take_picture();
124
+ }
125
+ }, 500);
126
+ }
127
+ </script>
128
+
129
+ <div class="wrap">
130
+ <!-- svelte-ignore a11y-media-has-caption -->
131
+ <video bind:this={video_source} class:flip={mirror_webcam} />
132
+ {#if !streaming}
133
+ <button on:click={mode === "image" ? take_picture : take_recording}>
134
+ {#if mode === "video"}
135
+ {#if recording}
136
+ <div class="icon">
137
+ <Square />
138
+ </div>
139
+ {:else}
140
+ <div class="icon">
141
+ <Circle />
142
+ </div>
143
+ {/if}
144
+ {:else}
145
+ <div class="icon">
146
+ <Camera />
147
+ </div>
148
+ {/if}
149
+ </button>
150
+ {/if}
151
+ </div>
152
+
153
+ <style>
154
+ .wrap {
155
+ position: relative;
156
+ width: var(--size-full);
157
+ height: var(--size-full);
158
+ min-height: var(--size-60);
159
+ }
160
+
161
+ video {
162
+ width: var(--size-full);
163
+ height: var(--size-full);
164
+ }
165
+
166
+ button {
167
+ display: flex;
168
+ position: absolute;
169
+ right: 0px;
170
+ bottom: var(--size-2);
171
+ left: 0px;
172
+ justify-content: center;
173
+ align-items: center;
174
+ margin: auto;
175
+ box-shadow: var(--shadow-drop-lg);
176
+ border-radius: var(--radius-xl);
177
+ background-color: rgba(0, 0, 0, 0.9);
178
+ width: var(--size-10);
179
+ height: var(--size-10);
180
+ }
181
+
182
+ @media (--screen-md) {
183
+ button {
184
+ bottom: var(--size-4);
185
+ }
186
+ }
187
+
188
+ @media (--screen-xl) {
189
+ button {
190
+ bottom: var(--size-8);
191
+ }
192
+ }
193
+
194
+ .icon {
195
+ opacity: 0.8;
196
+ width: 50%;
197
+ height: 50%;
198
+ color: white;
199
+ }
200
+
201
+ .flip {
202
+ transform: scaleX(-1);
203
+ }
204
+ </style>
@@ -0,0 +1,2 @@
1
+ export { default as Webcam } from "./Webcam.svelte";
2
+ export { default } from "./InteractiveImage.svelte";
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@gradio/image",
3
+ "version": "0.3.0-beta.5",
4
+ "description": "Gradio UI packages",
5
+ "type": "module",
6
+ "main": "./index.svelte",
7
+ "author": "",
8
+ "license": "ISC",
9
+ "private": false,
10
+ "dependencies": {
11
+ "cropperjs": "^1.5.12",
12
+ "lazy-brush": "^1.0.1",
13
+ "resize-observer-polyfill": "^1.5.1",
14
+ "@gradio/atoms": "^0.2.0-beta.3",
15
+ "@gradio/icons": "^0.2.0-beta.0",
16
+ "@gradio/statustracker": "^0.3.0-beta.5",
17
+ "@gradio/upload": "^0.3.0-beta.3",
18
+ "@gradio/utils": "^0.2.0-beta.3"
19
+ },
20
+ "main_changeset": true,
21
+ "exports": {
22
+ "./package.json": "./package.json",
23
+ "./interactive": "./interactive/index.ts",
24
+ "./static": "./static/index.ts",
25
+ "./example": "./example/index.ts"
26
+ }
27
+ }
@@ -0,0 +1,24 @@
1
+ export const get_coordinates_of_clicked_image = (
2
+ evt: MouseEvent
3
+ ): [number, number] | null => {
4
+ let image = evt.currentTarget as HTMLImageElement;
5
+
6
+ const imageRect = image.getBoundingClientRect();
7
+ const xScale = image.naturalWidth / imageRect.width;
8
+ const yScale = image.naturalHeight / imageRect.height;
9
+ if (xScale > yScale) {
10
+ const displayed_height = image.naturalHeight / xScale;
11
+ const y_offset = (imageRect.height - displayed_height) / 2;
12
+ var x = Math.round((evt.clientX - imageRect.left) * xScale);
13
+ var y = Math.round((evt.clientY - imageRect.top - y_offset) * xScale);
14
+ } else {
15
+ const displayed_width = image.naturalWidth / yScale;
16
+ const x_offset = (imageRect.width - displayed_width) / 2;
17
+ var x = Math.round((evt.clientX - imageRect.left - x_offset) * yScale);
18
+ var y = Math.round((evt.clientY - imageRect.top) * yScale);
19
+ }
20
+ if (x < 0 || x >= image.naturalWidth || y < 0 || y >= image.naturalHeight) {
21
+ return null;
22
+ }
23
+ return [x, y];
24
+ };
File without changes
@@ -0,0 +1,95 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from "svelte";
3
+ import type { SelectData } from "@gradio/utils";
4
+ import { uploadToHuggingFace } from "@gradio/utils";
5
+ import { BlockLabel, Empty, IconButton, ShareButton } from "@gradio/atoms";
6
+ import { Download } from "@gradio/icons";
7
+ import { get_coordinates_of_clicked_image } from "../shared/utils";
8
+
9
+ import { Image } from "@gradio/icons";
10
+ import { type FileData, normalise_file } from "@gradio/upload";
11
+ import type { I18nFormatter } from "@gradio/utils";
12
+
13
+ export let value: null | FileData;
14
+ let value_: null | FileData;
15
+ export let label: string | undefined = undefined;
16
+ export let show_label: boolean;
17
+ export let show_download_button = true;
18
+ export let selectable = false;
19
+ export let show_share_button = false;
20
+ export let root: string;
21
+ export let i18n: I18nFormatter;
22
+
23
+ const dispatch = createEventDispatcher<{
24
+ change: string;
25
+ select: SelectData;
26
+ }>();
27
+
28
+ $: value && dispatch("change", value.data);
29
+
30
+ $: if (value !== value_) {
31
+ value_ = value;
32
+ normalise_file(value_, root, null);
33
+ }
34
+
35
+ const handle_click = (evt: MouseEvent): void => {
36
+ let coordinates = get_coordinates_of_clicked_image(evt);
37
+ if (coordinates) {
38
+ dispatch("select", { index: coordinates, value: null });
39
+ }
40
+ };
41
+ </script>
42
+
43
+ <BlockLabel {show_label} Icon={Image} label={label || i18n("image.image")} />
44
+ {#if value_ === null}
45
+ <Empty unpadded_box={true} size="large"><Image /></Empty>
46
+ {:else}
47
+ <div class="icon-buttons">
48
+ {#if show_download_button}
49
+ <a
50
+ href={value_.data}
51
+ target={window.__is_colab__ ? "_blank" : null}
52
+ download={"image"}
53
+ >
54
+ <IconButton Icon={Download} label={i18n("common.download")} />
55
+ </a>
56
+ {/if}
57
+ {#if show_share_button}
58
+ <ShareButton
59
+ {i18n}
60
+ on:share
61
+ on:error
62
+ formatter={async (value) => {
63
+ if (!value) return "";
64
+ let url = await uploadToHuggingFace(value, "base64");
65
+ return `<img src="${url}" />`;
66
+ }}
67
+ {value}
68
+ />
69
+ {/if}
70
+ </div>
71
+ <!-- TODO: fix -->
72
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
73
+ <!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
74
+ <img src={value_.data} alt="" class:selectable on:click={handle_click} loading="lazy"/>
75
+ {/if}
76
+
77
+ <style>
78
+ img {
79
+ width: var(--size-full);
80
+ height: var(--size-full);
81
+ object-fit: contain;
82
+ }
83
+
84
+ .selectable {
85
+ cursor: crosshair;
86
+ }
87
+
88
+ .icon-buttons {
89
+ display: flex;
90
+ position: absolute;
91
+ top: 6px;
92
+ right: 6px;
93
+ gap: var(--size-1);
94
+ }
95
+ </style>
@@ -0,0 +1,76 @@
1
+ <svelte:options accessors={true} />
2
+
3
+ <script lang="ts">
4
+ import type { Gradio, SelectData } from "@gradio/utils";
5
+ import StaticImage from "./ImagePreview.svelte";
6
+
7
+ import { Block } from "@gradio/atoms";
8
+
9
+ import { StatusTracker } from "@gradio/statustracker";
10
+ import type { FileData } from "js/upload/src";
11
+ import type { LoadingStatus } from "@gradio/statustracker";
12
+
13
+ export let elem_id = "";
14
+ export let elem_classes: string[] = [];
15
+ export let visible = true;
16
+ export let value: null | FileData = null;
17
+ export let label: string;
18
+ export let show_label: boolean;
19
+ export let show_download_button: boolean;
20
+ export let root: string;
21
+
22
+ export let height: number | undefined;
23
+ export let width: number | undefined;
24
+
25
+ export let selectable = false;
26
+ export let container = true;
27
+ export let scale: number | null = null;
28
+ export let min_width: number | undefined = undefined;
29
+ export let loading_status: LoadingStatus;
30
+ export let show_share_button = false;
31
+ export let gradio: Gradio<{
32
+ change: never;
33
+ error: string;
34
+ select: SelectData;
35
+ share: ShareData;
36
+ }>;
37
+
38
+ $: value, gradio.dispatch("change");
39
+ let dragging: boolean;
40
+
41
+ $: value = !value ? null : value;
42
+ </script>
43
+
44
+ <Block
45
+ {visible}
46
+ variant={"solid"}
47
+ border_mode={dragging ? "focus" : "base"}
48
+ padding={false}
49
+ {elem_id}
50
+ {elem_classes}
51
+ height={height || undefined}
52
+ {width}
53
+ allow_overflow={false}
54
+ {container}
55
+ {scale}
56
+ {min_width}
57
+ >
58
+ <StatusTracker
59
+ autoscroll={gradio.autoscroll}
60
+ i18n={gradio.i18n}
61
+ {...loading_status}
62
+ />
63
+ <StaticImage
64
+ on:select={({ detail }) => gradio.dispatch("select", detail)}
65
+ on:share={({ detail }) => gradio.dispatch("share", detail)}
66
+ on:error={({ detail }) => gradio.dispatch("error", detail)}
67
+ {root}
68
+ {value}
69
+ {label}
70
+ {show_label}
71
+ {show_download_button}
72
+ {selectable}
73
+ {show_share_button}
74
+ i18n={gradio.i18n}
75
+ />
76
+ </Block>
@@ -0,0 +1 @@
1
+ export { default } from "./StaticImage.svelte";