Spaces:
Runtime error
Runtime error
create usage guide
Browse files- Dockerfile +0 -3
- MANIFEST.in +4 -2
- larvaecount/assets/wiki_style.css +3 -0
- larvaecount/docs/user_guide.md +96 -0
- larvaecount/gradient.py +3 -0
- larvaecount/pages/guide.py +26 -0
- larvaecount/pages/home.py +10 -5
- larvaecount/ui/ui_utils.py +9 -8
- setup.py +8 -1
Dockerfile
CHANGED
|
@@ -1,6 +1,3 @@
|
|
| 1 |
-
# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
| 2 |
-
# you will also find guides on how best to write your Dockerfile
|
| 3 |
-
|
| 4 |
FROM python:3.10
|
| 5 |
|
| 6 |
WORKDIR /code
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
FROM python:3.10
|
| 2 |
|
| 3 |
WORKDIR /code
|
MANIFEST.in
CHANGED
|
@@ -1,2 +1,4 @@
|
|
| 1 |
-
include
|
| 2 |
-
include
|
|
|
|
|
|
|
|
|
| 1 |
+
include larvaecount/assets/*.jpg
|
| 2 |
+
include larvaecount/assets/*.png
|
| 3 |
+
include larvaecount/assets/*.css
|
| 4 |
+
include larvaecount/docs/*.md
|
larvaecount/assets/wiki_style.css
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6, .wiki p, .wiki li {
|
| 2 |
+
text-align: left;
|
| 3 |
+
}
|
larvaecount/docs/user_guide.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Larvae Counter User Guide
|
| 2 |
+
|
| 3 |
+
Welcome! This is a web-app that is equipped with a variety of tools to automate the counting
|
| 4 |
+
of mosquito larvae and pupae (although mostly pupae at this stage) from images. Currently, this
|
| 5 |
+
larvae counting tool has three automatic pupae counting tools based on "old-school" computer
|
| 6 |
+
vision techniques. However, a deep-learning method based off of the `YoloV8` architecture
|
| 7 |
+
is currently in development. Hypothetically, this deep learning method should be much less
|
| 8 |
+
susceptible to noise and allow the simultaneous counting of larvae and pupae, and maybe even
|
| 9 |
+
other stages of the mosquito life cycle. See below for a quick getting started guide.
|
| 10 |
+
|
| 11 |
+
## Workflow
|
| 12 |
+
|
| 13 |
+
Below is the general workflow used to count mosquito larvae present in an image.
|
| 14 |
+
|
| 15 |
+
### Step 1: Upload an Image
|
| 16 |
+
|
| 17 |
+
The first step is to upload an image containing pupae to be counted. This is done by either
|
| 18 |
+
clicking on or dragging an image file to the camera icon on the page. If this app is being
|
| 19 |
+
used on a smartphone an image can also be taken directly from the smartphone's camera by
|
| 20 |
+
tapping on the camera icon. After uploading an image, you can begin tunning parameters.
|
| 21 |
+
|
| 22 |
+
### Step 2: Parameter Tuning
|
| 23 |
+
|
| 24 |
+
The next step is select your counting method and begin tuning the counting parameters. The
|
| 25 |
+
counting method can be selected in the drop-down list below the image upload box. The
|
| 26 |
+
`Gradient CC w/ filter` is recommended as the default counting algorithm. The parameters
|
| 27 |
+
for the selected counting method can be found in the box below the counting method drop-down
|
| 28 |
+
list.
|
| 29 |
+
|
| 30 |
+
It is recommend to click the `count` button with the default parameters to get a general
|
| 31 |
+
idea of how the parameters need to be changed in order to get better results. After changing
|
| 32 |
+
the parameters, click the `count` button again to re-run the selected algorithm with a new
|
| 33 |
+
set of parameters.
|
| 34 |
+
|
| 35 |
+
In general, parameters should be tuned from top down - meaning that the first parameter from the
|
| 36 |
+
top should be tuned to get satisfactory results, then the second parameter, and so on. The
|
| 37 |
+
images provided in the `Visualization` section of the results box (below the parameters box)
|
| 38 |
+
should be used to tune the parameters. More details will be given on each counting method and
|
| 39 |
+
its parameters below.
|
| 40 |
+
|
| 41 |
+
### Step 3: Check Results
|
| 42 |
+
|
| 43 |
+
After the parameters have been tuned, the final results are available in the results box - which
|
| 44 |
+
is situated just below the counting parameters box.
|
| 45 |
+
|
| 46 |
+
## Counting Algorithms
|
| 47 |
+
|
| 48 |
+
The larvae-count tool currently has three counting algorithms available, `Gradient CC w/ Filter`,
|
| 49 |
+
`Gradient CC`, and `Countour`.
|
| 50 |
+
|
| 51 |
+
### Gradient CC w/ filter
|
| 52 |
+
|
| 53 |
+
`Gradient CC w/ Filter` (short for Gradient Connected Components with Elliptical Filter) counts
|
| 54 |
+
the pupae present in an image utilizing clusters. The algorithm first creates a gray scale image
|
| 55 |
+
from the provided image and applies a simple threshold in order to find pixels that are in a region
|
| 56 |
+
containing pupae. A filter is then applied to filter out any pixels flagged as pupae that aren't part
|
| 57 |
+
of a roughly elliptical pixel region. The algorithm than iterates over each overlapping cluster of
|
| 58 |
+
pupae (hence the connected components) and calculated the number of pupae in each cluster by
|
| 59 |
+
the area of the cluster in pixels.
|
| 60 |
+
|
| 61 |
+
#### Parameters
|
| 62 |
+
|
| 63 |
+
- `Color Threshold`: Cutoff below which a pixel in the gray-scale image will be considered to be
|
| 64 |
+
in a region containing pupae. The allowed values range from (0-255).
|
| 65 |
+
- `Average Area`: Average area (in pixels) of one pupae.
|
| 66 |
+
- `Max Pupae Per Cluster (Optional)`: Maximum number of pupae per cluster, used to filter out any
|
| 67 |
+
large dark blobs in the image.
|
| 68 |
+
- `Filter Kernel Width (Pixels)`: Width (in pixels) of the elliptical filter.
|
| 69 |
+
- `Filter Kernel Height (Pixels)`: Height (in pixels) of the elliptical filter.
|
| 70 |
+
|
| 71 |
+
### Gradient CC
|
| 72 |
+
|
| 73 |
+
Same as `Gradient CC w/ Filter` but without the elliptical filter.
|
| 74 |
+
|
| 75 |
+
#### Parameters
|
| 76 |
+
|
| 77 |
+
- `Color Threshold`: Cutoff below which a pixel in the gray-scale image will be considered to be
|
| 78 |
+
in a region containing pupae. The allowed values range from (0-255).
|
| 79 |
+
- `Average Area`: Average area (in pixels) of one pupae.
|
| 80 |
+
- `Max Pupae Per Cluster (Optional)`: Maximum number of pupae per cluster, used to filter out any
|
| 81 |
+
large dark blobs in the image.
|
| 82 |
+
|
| 83 |
+
### Gradient Contour w/ Filter
|
| 84 |
+
|
| 85 |
+
Similar to `Gradient CC w/ Filter`, but instead of iterating over every blob of "pupae pixels"
|
| 86 |
+
it iterates over regions contained within (an) elliptical contour(s).
|
| 87 |
+
|
| 88 |
+
#### Parameters
|
| 89 |
+
|
| 90 |
+
- `Color Threshold`: Cutoff below which a pixel in the gray-scale image will be considered to be
|
| 91 |
+
in a region containing pupae. The allowed values range from (0-255).
|
| 92 |
+
- `Average Area`: Average area (in pixels) of one pupae.
|
| 93 |
+
- `Max Pupae Per Cluster (Optional)`: Maximum number of pupae per cluster, used to filter out any
|
| 94 |
+
large dark blobs in the image.
|
| 95 |
+
- `Filter Kernel Width (Pixels)`: Width (in pixels) of the elliptical filter.
|
| 96 |
+
- `Filter Kernel Height (Pixels)`: Height (in pixels) of the elliptical filter.
|
larvaecount/gradient.py
CHANGED
|
@@ -65,6 +65,7 @@ def component_filter_thresh(
|
|
| 65 |
"Num-Eggs": num_eggs
|
| 66 |
},
|
| 67 |
"vis": {
|
|
|
|
| 68 |
"Egg-Mask": bin_mask.astype(np.uint8),
|
| 69 |
"Ellipse-Filter": close.astype(np.uint8),
|
| 70 |
"Visualization": visualization_img.astype(np.uint8)
|
|
@@ -131,6 +132,7 @@ def component_thesh(
|
|
| 131 |
"Num-Eggs": num_eggs
|
| 132 |
},
|
| 133 |
"vis": {
|
|
|
|
| 134 |
"Egg-Mask": bin_mask.astype(np.uint8),
|
| 135 |
"Visualization": visualization_img.astype(np.uint8)
|
| 136 |
}
|
|
@@ -183,6 +185,7 @@ def contour_thresh(
|
|
| 183 |
"Num-Eggs": num
|
| 184 |
},
|
| 185 |
"vis": {
|
|
|
|
| 186 |
"Egg-Mask": bin_mask.astype(np.uint8),
|
| 187 |
"Ellipse-Filter": close.astype(np.uint8),
|
| 188 |
"Visualization": visualization_img.astype(np.uint8)
|
|
|
|
| 65 |
"Num-Eggs": num_eggs
|
| 66 |
},
|
| 67 |
"vis": {
|
| 68 |
+
"Gray-Scale": img_gray.astype(np.uint8),
|
| 69 |
"Egg-Mask": bin_mask.astype(np.uint8),
|
| 70 |
"Ellipse-Filter": close.astype(np.uint8),
|
| 71 |
"Visualization": visualization_img.astype(np.uint8)
|
|
|
|
| 132 |
"Num-Eggs": num_eggs
|
| 133 |
},
|
| 134 |
"vis": {
|
| 135 |
+
"Gray-Scale": img_gray.astype(np.uint8),
|
| 136 |
"Egg-Mask": bin_mask.astype(np.uint8),
|
| 137 |
"Visualization": visualization_img.astype(np.uint8)
|
| 138 |
}
|
|
|
|
| 185 |
"Num-Eggs": num
|
| 186 |
},
|
| 187 |
"vis": {
|
| 188 |
+
"Gray-Scale": img_gray.astype(np.uint8),
|
| 189 |
"Egg-Mask": bin_mask.astype(np.uint8),
|
| 190 |
"Ellipse-Filter": close.astype(np.uint8),
|
| 191 |
"Visualization": visualization_img.astype(np.uint8)
|
larvaecount/pages/guide.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dash import html, dcc, callback, Input, Output, State
|
| 2 |
+
from larvaecount.ui.ui_utils import read_md_file
|
| 3 |
+
|
| 4 |
+
import dash
|
| 5 |
+
import dash_bootstrap_components as dbc
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
dash.register_page(__name__, path = "/guide")
|
| 9 |
+
|
| 10 |
+
USER_GUIDE_PATH = os.path.abspath(
|
| 11 |
+
os.path.join(
|
| 12 |
+
os.path.dirname(__file__),
|
| 13 |
+
os.pardir,
|
| 14 |
+
"docs",
|
| 15 |
+
"user_guide.md"
|
| 16 |
+
)
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
layout = dbc.Container(
|
| 20 |
+
children = dcc.Markdown(
|
| 21 |
+
children = read_md_file(USER_GUIDE_PATH),
|
| 22 |
+
className = "wiki"
|
| 23 |
+
),
|
| 24 |
+
class_name = "text-center mt-3"
|
| 25 |
+
)
|
| 26 |
+
|
larvaecount/pages/home.py
CHANGED
|
@@ -31,11 +31,11 @@ UPLOAD_HEIGHT = "25vh"
|
|
| 31 |
|
| 32 |
COUNT_FUNCS = {
|
| 33 |
"Gradient CC": get_cc_ui,
|
| 34 |
-
"Gradient CC w/
|
| 35 |
-
"Contour": get_contour_ui
|
| 36 |
}
|
| 37 |
|
| 38 |
-
DEFAULT_STRATEGY = "Gradient CC"
|
| 39 |
|
| 40 |
def get_initial_upload_container() -> dbc.Container:
|
| 41 |
return dcc.Upload(
|
|
@@ -64,7 +64,10 @@ def get_new_upload_container(
|
|
| 64 |
image_data = BytesIO(decoded_bytes)
|
| 65 |
pil_img = Image.open(image_data)
|
| 66 |
img = np.array(pil_img)
|
| 67 |
-
image_fig = px.imshow(
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
return dbc.Container(
|
| 70 |
children = [
|
|
@@ -72,7 +75,9 @@ def get_new_upload_container(
|
|
| 72 |
children = file_name,
|
| 73 |
className = "p-2 text-start",
|
| 74 |
),
|
| 75 |
-
dcc.Graph(
|
|
|
|
|
|
|
| 76 |
dbc.Container(
|
| 77 |
children = dcc.Upload(
|
| 78 |
children = dbc.Button(
|
|
|
|
| 31 |
|
| 32 |
COUNT_FUNCS = {
|
| 33 |
"Gradient CC": get_cc_ui,
|
| 34 |
+
"Gradient CC w/ Filter": get_cc_filter_ui,
|
| 35 |
+
"Gradient Contour w/ Filter": get_contour_ui
|
| 36 |
}
|
| 37 |
|
| 38 |
+
DEFAULT_STRATEGY = "Gradient CC w/ Filter"
|
| 39 |
|
| 40 |
def get_initial_upload_container() -> dbc.Container:
|
| 41 |
return dcc.Upload(
|
|
|
|
| 64 |
image_data = BytesIO(decoded_bytes)
|
| 65 |
pil_img = Image.open(image_data)
|
| 66 |
img = np.array(pil_img)
|
| 67 |
+
image_fig = px.imshow(
|
| 68 |
+
img,
|
| 69 |
+
height = 750,
|
| 70 |
+
)
|
| 71 |
|
| 72 |
return dbc.Container(
|
| 73 |
children = [
|
|
|
|
| 75 |
children = file_name,
|
| 76 |
className = "p-2 text-start",
|
| 77 |
),
|
| 78 |
+
dcc.Graph(
|
| 79 |
+
figure = image_fig,
|
| 80 |
+
),
|
| 81 |
dbc.Container(
|
| 82 |
children = dcc.Upload(
|
| 83 |
children = dbc.Button(
|
larvaecount/ui/ui_utils.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
from dash import html, dcc
|
| 2 |
from typing import Dict
|
|
|
|
| 3 |
|
| 4 |
import plotly.express as px
|
| 5 |
import numpy as np
|
|
@@ -33,13 +34,6 @@ def get_navbar() -> dbc.Nav:
|
|
| 33 |
href = "/guide",
|
| 34 |
class_name = "text-light"
|
| 35 |
)
|
| 36 |
-
),
|
| 37 |
-
dbc.NavItem(
|
| 38 |
-
children = dbc.NavLink(
|
| 39 |
-
children = "About",
|
| 40 |
-
href = "/about",
|
| 41 |
-
class_name = "text-light"
|
| 42 |
-
)
|
| 43 |
)
|
| 44 |
],
|
| 45 |
className = "bg-dark d-flex flex-row justify-content-start align-items-center",
|
|
@@ -247,7 +241,10 @@ def get_results_container(result: Dict) -> dbc.Container:
|
|
| 247 |
|
| 248 |
for vis_name, vis_pic in result["vis"].items():
|
| 249 |
children.append(html.H4(vis_name.replace("-", " ")))
|
| 250 |
-
image_fig = px.imshow(
|
|
|
|
|
|
|
|
|
|
| 251 |
children.append(
|
| 252 |
dcc.Graph(
|
| 253 |
figure = image_fig,
|
|
@@ -259,3 +256,7 @@ def get_results_container(result: Dict) -> dbc.Container:
|
|
| 259 |
children = children,
|
| 260 |
className = "p-3 m-0 border border-dark d-flex flex-column justify-content-center align-items-center"
|
| 261 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from dash import html, dcc
|
| 2 |
from typing import Dict
|
| 3 |
+
from os import PathLike
|
| 4 |
|
| 5 |
import plotly.express as px
|
| 6 |
import numpy as np
|
|
|
|
| 34 |
href = "/guide",
|
| 35 |
class_name = "text-light"
|
| 36 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
)
|
| 38 |
],
|
| 39 |
className = "bg-dark d-flex flex-row justify-content-start align-items-center",
|
|
|
|
| 241 |
|
| 242 |
for vis_name, vis_pic in result["vis"].items():
|
| 243 |
children.append(html.H4(vis_name.replace("-", " ")))
|
| 244 |
+
image_fig = px.imshow(
|
| 245 |
+
vis_pic,
|
| 246 |
+
height = 750
|
| 247 |
+
)
|
| 248 |
children.append(
|
| 249 |
dcc.Graph(
|
| 250 |
figure = image_fig,
|
|
|
|
| 256 |
children = children,
|
| 257 |
className = "p-3 m-0 border border-dark d-flex flex-column justify-content-center align-items-center"
|
| 258 |
)
|
| 259 |
+
|
| 260 |
+
def read_md_file(md_file_path: PathLike) -> str:
|
| 261 |
+
with open(md_file_path, "r") as file:
|
| 262 |
+
return file.read()
|
setup.py
CHANGED
|
@@ -4,5 +4,12 @@ setup(
|
|
| 4 |
name = "larvaecount",
|
| 5 |
version = "0.1",
|
| 6 |
packages = find_packages(),
|
| 7 |
-
package_data=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
)
|
|
|
|
| 4 |
name = "larvaecount",
|
| 5 |
version = "0.1",
|
| 6 |
packages = find_packages(),
|
| 7 |
+
package_data = {
|
| 8 |
+
"larvaecount": [
|
| 9 |
+
"assets/*.jpg",
|
| 10 |
+
"assets/*.png",
|
| 11 |
+
"assets/*.css",
|
| 12 |
+
"docs/*.md"
|
| 13 |
+
]
|
| 14 |
+
}
|
| 15 |
)
|