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 |
)
|