Spaces:
Running
Running
File size: 10,681 Bytes
bfd1654 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
import gradio as gr
from app import demo as app
import os
_docs = {'PolygonAnnotator': {'description': 'Interactive polygon annotation component for visualizing and selecting polygon regions on images.\n\nThe PolygonAnnotator displays an image with customizable polygon overlays that users can interact with.\nFeatures include multi-selection with Ctrl/Cmd+click, hover effects, and customizable appearance including\nstroke width, opacity settings for both fill and stroke, with separate settings for selected states.\n\nPerfect for:\n- Document layout analysis and region selection\n- Image segmentation visualization\n- Interactive annotation review and editing\n- Object detection result visualization', 'members': {'__init__': {'value': {'type': 'dict | None', 'default': 'None', 'description': "Dictionary containing 'image' (FileData), 'polygons' (list with id, coordinates, color, opacities),"}, 'label': {'type': 'str | I18nData | None', 'default': 'None', 'description': 'Component label shown above the annotator.'}, 'every': {'type': 'Timer | float | None', 'default': 'None', 'description': 'Continuously calls `value` to recalculate it if `value` is a function.'}, 'inputs': {'type': 'Component | Sequence[Component] | set[Component] | None', 'default': 'None', 'description': "Components used as inputs to calculate `value` if it's a function."}, 'show_label': {'type': 'bool | None', 'default': 'None', 'description': 'Whether to display the label.'}, 'show_download_button': {'type': 'bool', 'default': 'True', 'description': 'Whether to show image download button.'}, 'height': {'type': 'int | str | None', 'default': 'None', 'description': 'Component height in pixels or CSS units.'}, 'width': {'type': 'int | str | None', 'default': 'None', 'description': 'Component width in pixels or CSS units.'}, 'container': {'type': 'bool', 'default': 'True', 'description': 'Whether to wrap component in a container with padding.'}, 'scale': {'type': 'int | None', 'default': 'None', 'description': 'Relative size compared to adjacent components.'}, 'min_width': {'type': 'int', 'default': '160', 'description': 'Minimum pixel width before wrapping.'}, 'interactive': {'type': 'bool | None', 'default': 'None', 'description': 'Whether users can interact with polygons (selection/deselection).'}, 'visible': {'type': 'bool | Literal["hidden"]', 'default': 'True', 'description': 'Whether component is visible ("hidden" keeps it in DOM but invisible).'}, 'elem_id': {'type': 'str | None', 'default': 'None', 'description': 'HTML DOM id for CSS targeting.'}, 'elem_classes': {'type': 'list[str] | str | None', 'default': 'None', 'description': 'HTML DOM classes for CSS targeting.'}, 'render': {'type': 'bool', 'default': 'True', 'description': 'Whether to render the component immediately.'}, 'key': {'type': 'int | str | tuple[int | str, ...] | None', 'default': 'None', 'description': 'Key for maintaining component identity across re-renders.'}, 'preserved_by_key': {'type': 'list[str] | str | None', 'default': '"value"', 'description': 'Parameters preserved across re-renders with same key.'}}, 'postprocess': {'value': {'type': 'dict | None', 'description': "Dictionary containing 'image' (file path or URL), 'polygons' (list of polygon dictionaries"}}, 'preprocess': {'return': {'type': 'dict | None', 'description': 'Dictionary with image path, polygon data including coordinates, colors, opacities,'}, 'value': None}}, 'events': {'clear': {'type': None, 'default': None, 'description': 'This listener is triggered when the user clears the PolygonAnnotator using the clear button for the component.'}, 'change': {'type': None, 'default': None, 'description': 'Triggered when the value of the PolygonAnnotator changes either because of user input (e.g. a user types in a textbox) OR because of a function update (e.g. an image receives a value from the output of an event trigger). See `.input()` for a listener that is only triggered by user input.'}, 'upload': {'type': None, 'default': None, 'description': 'This listener is triggered when the user uploads a file into the PolygonAnnotator.'}, 'select': {'type': None, 'default': None, 'description': 'Event listener for when the user selects or deselects the PolygonAnnotator. Uses event data gradio.SelectData to carry `value` referring to the label of the PolygonAnnotator, and `selected` to refer to state of the PolygonAnnotator. See EventData documentation on how to use this event data'}}}, '__meta__': {'additional_interfaces': {}, 'user_fn_refs': {'PolygonAnnotator': []}}}
abs_path = os.path.join(os.path.dirname(__file__), "css.css")
with gr.Blocks(
css=abs_path,
theme=gr.themes.Default(
font_mono=[
gr.themes.GoogleFont("Inconsolata"),
"monospace",
],
),
) as demo:
gr.Markdown(
"""
# `gradio_polygonannotator`
<div style="display: flex; gap: 7px;">
<img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.1.0%20-%20orange"> <a href="https://github.com/yourusername/gradio-polygonannotator/issues" target="_blank"><img alt="Static Badge" src="https://img.shields.io/badge/Issues-white?logo=github&logoColor=black"></a>
</div>
Interactive polygon annotation component for Gradio with multi-selection, hover effects, and customizable appearance
""", elem_classes=["md-custom"], header_links=True)
app.render()
gr.Markdown(
"""
## Installation
```bash
pip install gradio_polygonannotator
```
## Usage
```python
import gradio as gr
from gradio_polygonannotator import PolygonAnnotator
# Example with document regions
example_data = {
"image": "https://images.unsplash.com/photo-1544816155-12df9643f363?w=800&h=1200",
"polygons": [
{
"id": "title",
"coordinates": [[180, 150], [580, 150], [580, 200], [180, 200]],
"color": "#FF6B6B",
"mask_opacity": 0.15,
"stroke_width": 1.0,
"stroke_opacity": 0.8,
},
{
"id": "paragraph_1",
"coordinates": [[100, 400], [750, 400], [750, 600], [100, 600]],
"color": "#4ECDC4",
"mask_opacity": 0.15,
"stroke_width": 0.7,
"stroke_opacity": 0.6,
},
{
"id": "paragraph_2",
"coordinates": [[100, 650], [750, 650], [750, 950], [100, 950]],
"color": "#4ECDC4",
"mask_opacity": 0.15,
"stroke_width": 0.7,
"stroke_opacity": 0.6,
},
{
"id": "signature",
"coordinates": [[400, 1020], [650, 1020], [650, 1080], [400, 1080]],
"color": "#FFE66D",
"mask_opacity": 0.2,
"stroke_width": 1.5,
"stroke_opacity": 0.8,
}
]
}
def handle_selection(data, evt: gr.SelectData):
\"\"\"Handle polygon selection and display info\"\"\"
if evt.value and data:
selected_ids = evt.value if isinstance(evt.value, list) else [evt.value]
info = f"Selected {len(selected_ids)} polygon(s):\n"
for poly_id in selected_ids:
polygon = next((p for p in data["polygons"] if p["id"] == poly_id), None)
if polygon:
info += f"• {poly_id}\n"
return info
return "Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
with gr.Blocks() as demo:
gr.Markdown(\"\"\"
# PolygonAnnotator - Interactive Polygon Selection
Click on polygons to select them. Use **Ctrl/Cmd+Click** for multiple selections.
Click selected polygons to deselect.
\"\"\")
with gr.Row():
with gr.Column(scale=3):
annotator = PolygonAnnotator(
value=example_data,
label="Document with Region Annotations",
height=600,
)
with gr.Column(scale=1):
selected_info = gr.Textbox(
label="Selected Regions",
lines=6,
value="Click on polygons to select them. Use Ctrl/Cmd+Click for multi-selection."
)
# Handle selection events
annotator.select(
handle_selection,
inputs=[annotator],
outputs=[selected_info]
)
if __name__ == "__main__":
demo.launch()
```
""", elem_classes=["md-custom"], header_links=True)
gr.Markdown("""
## `PolygonAnnotator`
### Initialization
""", elem_classes=["md-custom"], header_links=True)
gr.ParamViewer(value=_docs["PolygonAnnotator"]["members"]["__init__"], linkify=[])
gr.Markdown("### Events")
gr.ParamViewer(value=_docs["PolygonAnnotator"]["events"], linkify=['Event'])
gr.Markdown("""
### User function
The impact on the users predict function varies depending on whether the component is used as an input or output for an event (or both).
- When used as an Input, the component only impacts the input signature of the user function.
- When used as an output, the component only impacts the return signature of the user function.
The code snippet below is accurate in cases where the component is used as both an input and an output.
- **As input:** Is passed, dictionary with image path, polygon data including coordinates, colors, opacities,.
- **As output:** Should return, dictionary containing 'image' (file path or URL), 'polygons' (list of polygon dictionaries.
```python
def predict(
value: dict | None
) -> dict | None:
return value
```
""", elem_classes=["md-custom", "PolygonAnnotator-user-fn"], header_links=True)
demo.load(None, js=r"""function() {
const refs = {};
const user_fn_refs = {
PolygonAnnotator: [], };
requestAnimationFrame(() => {
Object.entries(user_fn_refs).forEach(([key, refs]) => {
if (refs.length > 0) {
const el = document.querySelector(`.${key}-user-fn`);
if (!el) return;
refs.forEach(ref => {
el.innerHTML = el.innerHTML.replace(
new RegExp("\\b"+ref+"\\b", "g"),
`<a href="#h-${ref.toLowerCase()}">${ref}</a>`
);
})
}
})
Object.entries(refs).forEach(([key, refs]) => {
if (refs.length > 0) {
const el = document.querySelector(`.${key}`);
if (!el) return;
refs.forEach(ref => {
el.innerHTML = el.innerHTML.replace(
new RegExp("\\b"+ref+"\\b", "g"),
`<a href="#h-${ref.toLowerCase()}">${ref}</a>`
);
})
}
})
})
}
""")
demo.launch()
|