Skip to content

Commit

Permalink
feat: Polygon support for image annotator #1597 (#1643)
Browse files Browse the repository at this point in the history
  • Loading branch information
mturoci authored Nov 3, 2022
1 parent 8d7142e commit 0282818
Show file tree
Hide file tree
Showing 14 changed files with 1,270 additions and 228 deletions.
33 changes: 16 additions & 17 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@
"url": "http://localhost:3000/demo",
"webRoot": "${workspaceFolder}/ui"
},
{
"name": "Debug UI Tests",
"type": "node",
"request": "launch",
"runtimeExecutable": "node",
"program": "${workspaceRoot}/ui/node_modules/jest/bin/jest.js",
"args": [
"--no-cache",
"--env=jsdom",
"${file}"
],
"cwd": "${workspaceRoot}/ui",
"protocol": "inspector",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "Debug PY Build",
"type": "python",
Expand Down Expand Up @@ -69,23 +85,6 @@
"python": "${workspaceFolder}/tools/showcase/venv/bin/python",
"console": "integratedTerminal",
},
{
"name": "Debug UI Tests",
"type": "node",
"request": "launch",
"runtimeExecutable": "node",
"program": "${workspaceRoot}/ui/node_modules/jest/bin/jest.js",
"args": [
"--watch",
"--runInBand",
"--no-cache",
"--env=jsdom"
],
"cwd": "${workspaceRoot}/ui",
"protocol": "inspector",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
Expand Down
10 changes: 8 additions & 2 deletions py/examples/image_annotator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,24 @@ async def serve(q: Q):
ui.button(name='back', label='Back', primary=True),
]
else:
q.page['example'] = ui.form_card(box='1 1 -1 -1', items=[
q.page['example'] = ui.form_card(box='1 1 5 8', items=[
ui.image_annotator(
name='annotator',
title='Drag to annotate',
image='https://images.pexels.com/photos/2696064/pexels-photo-2696064.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',
image_height='280px',
image_height='450px',
tags=[
ui.image_annotator_tag(name='p', label='Person', color='$cyan'),
ui.image_annotator_tag(name='f', label='Food', color='$blue'),
],
items=[
ui.image_annotator_item(shape=ui.image_annotator_rect(x1=649, y1=393, x2=383, y2=25), tag='p'),
ui.image_annotator_item(tag='p', shape=ui.image_annotator_polygon([
ui.image_annotator_point(x=828.2142857142857, y=135),
ui.image_annotator_point(x=731.7857142857142, y=212.14285714285714),
ui.image_annotator_point(x=890.3571428571429, y=354.6428571428571),
ui.image_annotator_point(x=950.3571428571429, y=247.5)
])),
],
),
ui.button(name='submit', label='Submit', primary=True)
Expand Down
78 changes: 78 additions & 0 deletions py/h2o_wave/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6441,32 +6441,110 @@ def load(__d: Dict) -> 'ImageAnnotatorRect':
)


class ImageAnnotatorPoint:
"""Create a polygon annotation point with x and y coordinates..
"""
def __init__(
self,
x: float,
y: float,
):
_guard_scalar('ImageAnnotatorPoint.x', x, (float, int,), False, False, False)
_guard_scalar('ImageAnnotatorPoint.y', y, (float, int,), False, False, False)
self.x = x
"""`x` coordinate of the point."""
self.y = y
"""`y` coordinate of the point."""

def dump(self) -> Dict:
"""Returns the contents of this object as a dict."""
_guard_scalar('ImageAnnotatorPoint.x', self.x, (float, int,), False, False, False)
_guard_scalar('ImageAnnotatorPoint.y', self.y, (float, int,), False, False, False)
return _dump(
x=self.x,
y=self.y,
)

@staticmethod
def load(__d: Dict) -> 'ImageAnnotatorPoint':
"""Creates an instance of this class using the contents of a dict."""
__d_x: Any = __d.get('x')
_guard_scalar('ImageAnnotatorPoint.x', __d_x, (float, int,), False, False, False)
__d_y: Any = __d.get('y')
_guard_scalar('ImageAnnotatorPoint.y', __d_y, (float, int,), False, False, False)
x: float = __d_x
y: float = __d_y
return ImageAnnotatorPoint(
x,
y,
)


class ImageAnnotatorPolygon:
"""Create a polygon annotation shape.
"""
def __init__(
self,
vertices: List[ImageAnnotatorPoint],
):
_guard_vector('ImageAnnotatorPolygon.vertices', vertices, (ImageAnnotatorPoint,), False, False, False)
self.vertices = vertices
"""List of points of the polygon."""

def dump(self) -> Dict:
"""Returns the contents of this object as a dict."""
_guard_vector('ImageAnnotatorPolygon.vertices', self.vertices, (ImageAnnotatorPoint,), False, False, False)
return _dump(
vertices=[__e.dump() for __e in self.vertices],
)

@staticmethod
def load(__d: Dict) -> 'ImageAnnotatorPolygon':
"""Creates an instance of this class using the contents of a dict."""
__d_vertices: Any = __d.get('vertices')
_guard_vector('ImageAnnotatorPolygon.vertices', __d_vertices, (dict,), False, False, False)
vertices: List[ImageAnnotatorPoint] = [ImageAnnotatorPoint.load(__e) for __e in __d_vertices]
return ImageAnnotatorPolygon(
vertices,
)


class ImageAnnotatorShape:
"""Create a shape to be rendered as an annotation on an image annotator.
"""
def __init__(
self,
rect: Optional[ImageAnnotatorRect] = None,
polygon: Optional[ImageAnnotatorPolygon] = None,
):
_guard_scalar('ImageAnnotatorShape.rect', rect, (ImageAnnotatorRect,), False, True, False)
_guard_scalar('ImageAnnotatorShape.polygon', polygon, (ImageAnnotatorPolygon,), False, True, False)
self.rect = rect
"""No documentation available."""
self.polygon = polygon
"""No documentation available."""

def dump(self) -> Dict:
"""Returns the contents of this object as a dict."""
_guard_scalar('ImageAnnotatorShape.rect', self.rect, (ImageAnnotatorRect,), False, True, False)
_guard_scalar('ImageAnnotatorShape.polygon', self.polygon, (ImageAnnotatorPolygon,), False, True, False)
return _dump(
rect=None if self.rect is None else self.rect.dump(),
polygon=None if self.polygon is None else self.polygon.dump(),
)

@staticmethod
def load(__d: Dict) -> 'ImageAnnotatorShape':
"""Creates an instance of this class using the contents of a dict."""
__d_rect: Any = __d.get('rect')
_guard_scalar('ImageAnnotatorShape.rect', __d_rect, (dict,), False, True, False)
__d_polygon: Any = __d.get('polygon')
_guard_scalar('ImageAnnotatorShape.polygon', __d_polygon, (dict,), False, True, False)
rect: Optional[ImageAnnotatorRect] = None if __d_rect is None else ImageAnnotatorRect.load(__d_rect)
polygon: Optional[ImageAnnotatorPolygon] = None if __d_polygon is None else ImageAnnotatorPolygon.load(__d_polygon)
return ImageAnnotatorShape(
rect,
polygon,
)


Expand Down
33 changes: 33 additions & 0 deletions py/h2o_wave/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -2398,6 +2398,39 @@ def image_annotator_rect(
))


def image_annotator_point(
x: float,
y: float,
) -> ImageAnnotatorPoint:
"""Create a polygon annotation point with x and y coordinates..
Args:
x: `x` coordinate of the point.
y: `y` coordinate of the point.
Returns:
A `h2o_wave.types.ImageAnnotatorPoint` instance.
"""
return ImageAnnotatorPoint(
x,
y,
)


def image_annotator_polygon(
vertices: List[ImageAnnotatorPoint],
) -> ImageAnnotatorShape:
"""Create a polygon annotation shape.
Args:
vertices: List of points of the polygon.
Returns:
A `h2o_wave.types.ImageAnnotatorPolygon` instance.
"""
return ImageAnnotatorShape(polygon=ImageAnnotatorPolygon(
vertices,
))


def image_annotator_item(
shape: ImageAnnotatorShape,
tag: str,
Expand Down
32 changes: 32 additions & 0 deletions r/R/ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -2797,6 +2797,38 @@ ui_image_annotator_rect <- function(
return(.o)
}

#' Create a polygon annotation point with x and y coordinates..
#'
#' @param x `x` coordinate of the point.
#' @param y `y` coordinate of the point.
#' @return A ImageAnnotatorPoint instance.
#' @export
ui_image_annotator_point <- function(
x,
y) {
.guard_scalar("x", "numeric", x)
.guard_scalar("y", "numeric", y)
.o <- list(
x=x,
y=y)
class(.o) <- append(class(.o), c(.wave_obj, "WaveImageAnnotatorPoint"))
return(.o)
}

#' Create a polygon annotation shape.
#'
#' @param vertices List of points of the polygon.
#' @return A ImageAnnotatorPolygon instance.
#' @export
ui_image_annotator_polygon <- function(
vertices) {
.guard_vector("vertices", "WaveImageAnnotatorPoint", vertices)
.o <- list(polygon=list(
vertices=vertices))
class(.o) <- append(class(.o), c(.wave_obj, "WaveImageAnnotatorShape"))
return(.o)
}

#' Create an annotator item with initial selected tags or no tag for plaintext.
#'
#' @param shape The annotation shape.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@
<option name="Python" value="true"/>
</context>
</template>
<template name="w_image_annotator_point" value="ui.image_annotator_point(x=$x$,y=$y$),$END$" description="Create a minimal Wave ImageAnnotatorPoint." toReformat="true" toShortenFQNames="true">
<variable name="x" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="y" expression="" defaultValue="" alwaysStopAt="true"/>
<context>
<option name="Python" value="true"/>
</context>
</template>
<template name="w_image_annotator_polygon" value="ui.image_annotator_polygon(vertices=[&#10; $vertices$ &#10;]),$END$" description="Create a minimal Wave ImageAnnotatorPolygon." toReformat="true" toShortenFQNames="true">
<variable name="vertices" expression="" defaultValue="" alwaysStopAt="true"/>
<context>
<option name="Python" value="true"/>
</context>
</template>
<template name="w_image_annotator_rect" value="ui.image_annotator_rect(x1=$x1$,y1=$y1$,x2=$x2$,y2=$y2$),$END$" description="Create a minimal Wave ImageAnnotatorRect." toReformat="true" toShortenFQNames="true">
<variable name="x1" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="y1" expression="" defaultValue="" alwaysStopAt="true"/>
Expand Down Expand Up @@ -1396,6 +1409,19 @@
<option name="Python" value="true"/>
</context>
</template>
<template name="w_full_image_annotator_point" value="ui.image_annotator_point(x=$x$,y=$y$),$END$" description="Create Wave ImageAnnotatorPoint with full attributes." toReformat="true" toShortenFQNames="true">
<variable name="x" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="y" expression="" defaultValue="" alwaysStopAt="true"/>
<context>
<option name="Python" value="true"/>
</context>
</template>
<template name="w_full_image_annotator_polygon" value="ui.image_annotator_polygon(vertices=[&#10; $vertices$ &#10;]),$END$" description="Create Wave ImageAnnotatorPolygon with full attributes." toReformat="true" toShortenFQNames="true">
<variable name="vertices" expression="" defaultValue="" alwaysStopAt="true"/>
<context>
<option name="Python" value="true"/>
</context>
</template>
<template name="w_full_image_annotator_rect" value="ui.image_annotator_rect(x1=$x1$,y1=$y1$,x2=$x2$,y2=$y2$),$END$" description="Create Wave ImageAnnotatorRect with full attributes." toReformat="true" toShortenFQNames="true">
<variable name="x1" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="y1" expression="" defaultValue="" alwaysStopAt="true"/>
Expand All @@ -1405,8 +1431,9 @@
<option name="Python" value="true"/>
</context>
</template>
<template name="w_full_image_annotator_shape" value="ui.image_annotator_shape(rect=$rect$),$END$" description="Create Wave ImageAnnotatorShape with full attributes." toReformat="true" toShortenFQNames="true">
<template name="w_full_image_annotator_shape" value="ui.image_annotator_shape(rect=$rect$,polygon=$polygon$),$END$" description="Create Wave ImageAnnotatorShape with full attributes." toReformat="true" toShortenFQNames="true">
<variable name="rect" expression="" defaultValue="&quot;None&quot;" alwaysStopAt="true"/>
<variable name="polygon" expression="" defaultValue="&quot;None&quot;" alwaysStopAt="true"/>
<context>
<option name="Python" value="true"/>
</context>
Expand Down
30 changes: 29 additions & 1 deletion tools/vscode-extension/component-snippets.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,20 @@
],
"description": "Create a minimal Wave Image."
},
"Wave ImageAnnotatorPoint": {
"prefix": "w_image_annotator_point",
"body": [
"ui.image_annotator_point(x=$1, y=$2),$0"
],
"description": "Create a minimal Wave ImageAnnotatorPoint."
},
"Wave ImageAnnotatorPolygon": {
"prefix": "w_image_annotator_polygon",
"body": [
"ui.image_annotator_polygon(vertices=[\n\t\t$1\t\t\n]),$0"
],
"description": "Create a minimal Wave ImageAnnotatorPolygon."
},
"Wave ImageAnnotatorRect": {
"prefix": "w_image_annotator_rect",
"body": [
Expand Down Expand Up @@ -1161,6 +1175,20 @@
],
"description": "Create a full Wave Image."
},
"Wave Full ImageAnnotatorPoint": {
"prefix": "w_full_image_annotator_point",
"body": [
"ui.image_annotator_point(x=$1, y=$2),$0"
],
"description": "Create a full Wave ImageAnnotatorPoint."
},
"Wave Full ImageAnnotatorPolygon": {
"prefix": "w_full_image_annotator_polygon",
"body": [
"ui.image_annotator_polygon(vertices=[\n\t\t$1\t\t\n]),$0"
],
"description": "Create a full Wave ImageAnnotatorPolygon."
},
"Wave Full ImageAnnotatorRect": {
"prefix": "w_full_image_annotator_rect",
"body": [
Expand All @@ -1171,7 +1199,7 @@
"Wave Full ImageAnnotatorShape": {
"prefix": "w_full_image_annotator_shape",
"body": [
"ui.image_annotator_shape(rect=${1:None}),$0"
"ui.image_annotator_shape(rect=${1:None}, polygon=${2:None}),$0"
],
"description": "Create a full Wave ImageAnnotatorShape."
},
Expand Down
Loading

0 comments on commit 0282818

Please sign in to comment.