Skip to content

Commit

Permalink
Merge branch 'master' into image_upload_progress_track
Browse files Browse the repository at this point in the history
  • Loading branch information
sumn2u authored Jun 7, 2024
2 parents e1efad9 + 0772640 commit d96d266
Show file tree
Hide file tree
Showing 33 changed files with 390 additions and 541 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Python application

on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Python 3.9
uses: actions/setup-python@v3
with:
python-version: 3.9

- name: Install dependencies
run: |
python -m venv venv
source venv/bin/activate
pip install -r server/requirements.txt
- name: Run tests
run: |
source venv/bin/activate
cd server
python3 -m unittest tests/test_app.py
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Annotate-Lab

Annotate-Lab is an open-source application designed for image annotation, comprising two main components: the client and the server. The client, a React application, is responsible for the user interface where users perform annotations. On the other hand, the server, a Flask application, manages persisting the annotated changes and generating masked and annotated images, along with configuration settings. More information can be found in our [documentation](./docs/annotate-lab.md).
Annotate-Lab is an open-source application designed for image annotation, comprising two main components: the client and the server. The client, a React application, is responsible for the user interface where users perform annotations. On the other hand, the server, a Flask application, manages persisting the annotated changes and generating masked and annotated images, along with configuration settings. More information can be found in our [documentation](https://annotate-docs.dwaste.live/).

![example](./example.png)
![example](./sample_images/example.png)

# Demo
[![Annotate Lab](https://img.youtube.com/vi/b78BJhbasVw/0.jpg)](https://www.youtube.com/watch?v=b78BJhbasVw)
Expand Down Expand Up @@ -34,6 +34,7 @@ annotation-lab/
│ └── ... (other React app files)
├── server/
│ ├── db/
│ ├── tests/
│ ├── venv/
│ ├── app.py
│ ├── requirements.txt
Expand All @@ -50,13 +51,14 @@ annotation-lab/
### Server
- **db/**: Database-related files and handlers.
- **venv/**: Python virtual environment (not included in version control).
- **tests/**: Contains test files.
- **app.py**: Main Flask application file.
- **requirements.txt**: Contains server dependencies.

## Settings
One can configure the tools, tags, upload images and do many more from the settings.

![configuration](./configuration.png)
![configuration](./sample_images/configuration.png)
## Dependencies

### Client
Expand Down Expand Up @@ -144,8 +146,8 @@ docker-compose up -d #running in detached mode
## Outputs
Sample of annotated image along with its mask and settings is show below.

![orange_annotation](./docs/orange_annotated-image.png)
![orange_annotation_mask](./docs/orange_masked-image.png)
![orange_annotation](./sample_images/orange_annotated-image.png)
![orange_annotation_mask](./sample_images/orange_masked-image.png)

```json
{
Expand Down
3 changes: 0 additions & 3 deletions client/.npmignore

This file was deleted.

46 changes: 23 additions & 23 deletions client/src/ConfigurationTask/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,31 @@ import React, { useMemo } from "react"
import Survey from "material-survey/components/Survey"
import { setIn } from "seamless-immutable"
import { CssBaseline, GlobalStyles } from "@mui/material";

const form = {
questions: [
{
name: "taskDescription",
title: "Task Information",
type: "text",
placeHolder: "Enter task details...",
isRequired: true
},
{
name: "taskChoice",
title: "Choice of Task",
type: "radiogroup",
isRequired: true,
choices: [
{ value: "image_classification", text: "Image Classification" },
{ value: "image_segmentation", text: "Image Segmentation" },
],
},

],
}
import {useTranslation} from "react-i18next"

export default ({ config, onChange }) => {
const { t } = useTranslation();
const form = {
questions: [
{
name: "taskDescription",
title: t("setup.tabs.taskinfo.task_info"),
type: "text",
isRequired: true
},
{
name: "taskChoice",
title: t("setup.tabs.taskinfo.task_choice"),
type: "radiogroup",
isRequired: true,
choices: [
{ value: "image_classification", text: t("setup.tabs.taskinfo.task_choice_classification")},
{ value: "image_segmentation", text: t("setup.tabs.taskinfo.task_choice_segmentation") },
],
},

],
}
const defaultAnswers = useMemo(
() => ({
taskDescription: config.taskDescription || "",
Expand Down
93 changes: 48 additions & 45 deletions client/src/ConfigureImageClassification/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,58 @@ import React, { useMemo } from "react"
import Survey from "material-survey/components/Survey"
import { setIn } from "seamless-immutable"
import { CssBaseline, GlobalStyles } from "@mui/material";
import { useTranslation } from "react-i18next"

const form = {
questions: [
{
name: "multipleRegions",
title: "Can multiple regions be created?",
type: "boolean",
},
{
name: "multipleRegionLabels",
title: "Multiple Region Labels Allowed?",
type: "boolean",
},
{
name: "regionTypesAllowed",
title: "Region Types Allowed",
description: "What types of regions can be drawn on the image.",
type: "checkbox",
choices: ["bounding-box", "polygon", "circle"],
export default ({ config, onChange }) => {
const { t } = useTranslation();

const form = {
questions: [
{
name: "multipleRegions",
title: t("configuration.multiple_regions"),
type: "boolean",
},
{
name: "labels",
title: "Labels",
description: "Classifications or tags to be labeled.",
type: "matrixdynamic",
columns: [
{ cellType: "text", name: "id", title: "id" , isRequired: true},
{
cellType: "text",
name: "description",
title: "Description (optional)",
{
name: "multipleRegionLabels",
title:t("configuration.multiple_region_labels"),
type: "boolean",
},
{
name: "regionTypesAllowed",
title: t("configuration.region_types_allowed"),
description: t("configuration.region_types_allowed.description"),
type: "checkbox",
choices: ["bounding-box", "polygon", "circle"],
},
],
},
{
name: "regions",
title: "Default Region Type",
description: "Choose default region type that can be drawn on the image.",
type: "dropdown",
choices: [
"Polygon",
"Bounding Box",
"Point",
],
}
],
}
{
name: "labels",
title: t("configuration.labels"),
description: t("configuration.labels.description"),
type: "matrixdynamic",
columns: [
{ cellType: "text", name: "id", title: t("configuration.labels.option.id") , isRequired: true},
{
cellType: "text",
name: "description",
title: t("configuration.labels.option.id"),
},
],
},
{
name: "regions",
title: t("configuration.regions"),
description: t("configuration.regions.description"),
type: "dropdown",
choices: [
"Polygon",
"Bounding Box",
"Point",
],
}
],
}

export default ({ config, onChange }) => {
const defaultAnswers = useMemo(
() => ({
multipleRegions: Boolean(config.multipleRegions ? config.multipleRegions : true),
Expand Down
24 changes: 14 additions & 10 deletions client/src/ConfigureImageSegmentation/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,49 @@ import React, { useMemo } from "react"
import Survey from "material-survey/components/Survey"
import { setIn, asMutable } from "seamless-immutable"
import { CssBaseline, GlobalStyles } from "@mui/material";
import { useTranslation } from "react-i18next"

const form = {

export default ({ config, onChange }) => {
const { t } = useTranslation();

const form = {
questions: [
{
name: "regionTypesAllowed",
title: "Region Types Allowed",
description: "What types of regions can be drawn on the image.",
title: t("configuration.region_types_allowed"),
description: t("configuration.region_types_allowed.description"),
type: "multiple-dropdown",
choices: ["bounding-box", "polygon", "circle"],
},
{
name: "multipleRegions",
title: "Can multiple regions be created?",
title: t("configuration.multiple_regions"),
type: "boolean",
},
{
name: "multipleRegionLabels",
title: "Multiple Region Labels Allowed?",
title: t("configuration.multiple_region_labels"),
type: "boolean",
},
{
name: "labels",
title: "Available Labels",
title: t("configuration.labels"),
description:
"If you're labeling regions on an image, these are the allowed classifications or tags.",
t("configuration.labels.description"),
type: "matrixdynamic",
columns: [
{ cellType: "text", name: "id", title: "id" },
{ cellType: "text", name: "id", title: t("configuration.labels.option.id") , isRequired: true},
{
cellType: "text",
name: "description",
title: "Description (optional)",
title: t("configuration.labels.option.description"),
},
],
},
],
}

export default ({ config, onChange }) => {
const defaultAnswers = useMemo(
() =>
asMutable(
Expand Down
4 changes: 3 additions & 1 deletion client/src/DemoSite/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ export default () => {
})[0]

let selectedImageIndex = imageNames.indexOf(selectedImage)
if(selectedImageIndex != -1){
changeSelectedImageIndex(selectedImageIndex)
}
}

const getEnabledTools = (selectedTools) => {
Expand Down Expand Up @@ -139,7 +141,7 @@ export default () => {
enabledTools={getEnabledTools(settings.configuration.regionTypesAllowed) || []}
regionClsList={settings.configuration.labels.map(label => label.id) || []}
selectedImage={selectedImageIndex}
enabledRegionProps= {["class", "comment"]}
enabledRegionProps= {["class", "name"]}
userReducer= {userReducer}
onExit={(output) => {
preprocessDataBeforeSend(output)
Expand Down
4 changes: 3 additions & 1 deletion client/src/FilesListMenu/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import capitalize from "lodash/capitalize"
import classnames from "classnames"
import Checkbox from "@mui/material/Checkbox"
import getActiveImage from "../Annotator/reducers/get-active-image"
import { useTranslation } from "react-i18next"

const theme = createTheme()
const LabelContainer = styled("div")(({ theme }) => ({
Expand Down Expand Up @@ -64,6 +65,7 @@ export const FilesListMenu = ({
onClick
}) => {
const [change, setChange] = useState('')
const { t } = useTranslation();
const handleClickLabel = (label) => {
onClick(getActiveImage(state))
saveActiveImage(getActiveImage(state).activeImage)
Expand All @@ -74,7 +76,7 @@ export const FilesListMenu = ({
return (
<ThemeProvider theme={theme}>
<SidebarBoxContainer
title={`Images [${allImages.length > 0 ? allImages.length : 0}]`}
title={`${t("menu.images")} [${allImages.length > 0 ? allImages.length : 0}]`}
subTitle=""
icon={<CollectionsIcon style={{ color: muiColors.grey[700] }} />}
noScroll={true}
Expand Down
Loading

0 comments on commit d96d266

Please sign in to comment.