Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
M
MRCNN Particle Detection
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
AVT-FVT
public
MRCNN Particle Detection
Commits
741908c4
Commit
741908c4
authored
1 year ago
by
ssibirtsev
Browse files
Options
Downloads
Patches
Plain Diff
Upload New File
parent
fd82fc8b
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
classes/droplet/process_automated_droplet.py
+745
-0
745 additions, 0 deletions
classes/droplet/process_automated_droplet.py
with
745 additions
and
0 deletions
classes/droplet/process_automated_droplet.py
0 → 100644
+
745
−
0
View file @
741908c4
"""
MRCNN Particle Detection
Process images with MRCNN model trained on the droplet class.
The source code of
"
MRCNN Particle Detection
"
(https://git.rwth-aachen.de/avt-fvt/private/mrcnn-particle-detection)
is based on the source code of
"
Mask R-CNN
"
(https://github.com/matterport/Mask_RCNN).
The source code of
"
Mask R-CNN
"
is licensed under the MIT License (MIT).
Copyright (c) 2017 Matterport, Inc.
Written by Waleed Abdulla
All source code modifications to the source code of
"
Mask R-CNN
"
in
"
MRCNN Particle Detection
"
are licensed under the Eclipse Public License v2.0 (EPL 2.0).
Copyright (c) 2022-2023 Fluid Process Engineering (AVT.FVT), RWTH Aachen University
Edited by Stepan Sibirtsev, Mathias Neufang & Jakob Seiler
The coyprights and license terms are given in LICENSE.
Ideas and a small code snippets were adapted from these sources:
https://github.com/mat02/Mask_RCNN
"""
### --------------------------- ###
### Input processing parameters ###
### --------------------------- ###
# Is the script executed on the cluster?
# E.g., RWTH High Performance Computing cluster?
# True=yes, False=no
cluster
=
False
### Please specify only for non-cluster evaluations
# File format of images
file_format
=
"
jpg
"
# Input dataset folder located in path: "...\datasets\input\..."
dataset_path
=
"
test_input
"
# Output images folder located in path: "...\datasets\output\..."
save_path
=
"
test_output
"
# Name of the excel output file located in path: "...\datasets\output\..."
name_result_file
=
"
DSD
"
# Folder of the MRCNN model located in: "...\models\..."
# Weights of the individual epochs=MRCNN models
weights_path
=
"
weights
"
# MRCNN model name located in path: "...\models\<WeightsFolderName>\"
weights_name
=
"
weights_name
"
# Generate detection masks?
# True=yes, False=no
masks
=
False
# Use GPU or CPU?
# True=GPU, False=CPU
device
=
False
# Save n-th result image
save_nth_image
=
1
# Pixel size in [µm/px].
# To read the pixel size value from Sopat log file enter pixelsize=0
# (Sopat generates a JSON file with including information)
pixelsize
=
1
# Do you want the image to be center cropped before detection?
# no=None, yes=(x, y) coordinates (e.g.: image_crop=(1000, 1500))
image_crop
=
None
### Specifications for the processing parameters
# Number of images used to evaluate with the MRCNN model on each GPU.
# If only one GPU is used, this parameter is equivalent to batch size
# (see BATCH_SIZE parameter in config.py).
# A 12GB GPU can typically handle 2 images of 1024x1024px resolution.
# Adjust this parameter based on your GPU memory and image resolution.
images_gpu
=
1
# Image resolution (see IMAGE_MAX_DIM parameter in config.py).
# Should be the same value as for training.
# 0=512, 1=1024, 2=2048
image_max
=
1
# Skip detections with confidence < specified confidence parameter value
confidence
=
0.7
### Specifications for filters
# Detect and mark reflections in droplets?
# The detected reflections are excluded from the evaluation
# and do not appear in the excel output file.
# Marking color is blue.
# True=yes, False=no
detect_reflections
=
False
# Detect and mark oval droplets?
# The detected oval droplets are excluded from the evaluation
# and do not appear in the excel output file.
# Marking color is red.
# True=yes, False=no
detect_oval_droplets
=
False
# Minimum aspect ratio: filter for elliptical shapes
min_aspect_ratio
=
0.9
# Detect and mark adhesive droplets?
# The detected adhesive droplets are excluded from the evaluation
# and do not appear in the excel output file.
# Marking color is orange.
# True=yes, False=no
detect_adhesive_droplets
=
False
# Save coordinates of detected adhesive droplets in excel output file?
# True=yes, False=no
save_coordinates
=
False
# Minimum velocity: threshold to filter adhesive droplets
# Minimum distance [% of droplet mean diameter] that a droplet has to travel between 2 frames
min_velocity
=
0.3
# Minimum size difference: threshold to filter adhesive droplets
# [%] to be considered a different droplet
min_size_diff
=
0.3
# Number of images that are being compared.
# This is necessary because adhesive droplets may not get detected every frame.
n_images_compared
=
3
# Number of times a droplet has to be detected at a similar position to be defined as adhesive.
n_adhesive_high
=
3
n_adhesive_low
=
2
low_distance_threshold
=
0.05
# Edge threshold: filter for image border intersecting droplets.
# Image border intersecting droplets are marked in color red.
edge_tolerance
=
0.01
# Use contrast adjustment?
# 0=no, 1=contrast limited adaptive histogram equalization, 2=contrast stretching
contrast
=
0
### ----------------------------------- ###
### Initialization ###
### ----------------------------------- ###
from
PIL
import
Image
import
os
import
json
import
sys
import
random
import
math
import
re
import
time
import
glob
import
itertools
import
numpy
as
np
import
tensorflow
as
tf
import
matplotlib
import
matplotlib.image
as
mpimg
import
matplotlib.pyplot
as
plt
import
matplotlib.patches
as
patches
import
cv2
import
pandas
as
pd
from
numpy
import
asarray
from
random
import
random
from
skimage
import
exposure
from
pathlib
import
Path
matplotlib
.
use
(
"
agg
"
)
start_time
=
time
.
time
()
tf
.
to_float
=
lambda
x
:
tf
.
cast
(
x
,
tf
.
float32
)
# Root directory of the project
if
cluster
is
False
:
ROOT_DIR
=
os
.
path
.
abspath
(
""
)
WEIGHTS_DIR
=
os
.
path
.
join
(
ROOT_DIR
,
"
models
"
,
weights_path
,
weights_name
+
'
.h5
'
)
DATASET_DIR
=
os
.
path
.
join
(
ROOT_DIR
,
"
datasets
\\
input
"
,
dataset_path
)
SAVE_DIR
=
os
.
path
.
join
(
ROOT_DIR
,
"
datasets
\\
output
"
,
save_path
)
EXCEL_DIR
=
os
.
path
.
join
(
SAVE_DIR
,
name_result_file
+
'
.xlsx
'
)
IMAGE_MAX
=
image_max
MASKS
=
masks
DEVICE
=
device
IMAGES_GPU
=
images_gpu
SAVE_NTH_IMAGE
=
save_nth_image
DETECT_OVAL_DROPLETS
=
detect_oval_droplets
DETECT_REFLECTIONS
=
detect_reflections
MIN_ASPECT_RATIO
=
min_aspect_ratio
PIXELSIZE
=
pixelsize
DETECT_ADHESIVE_DROPLETS
=
detect_adhesive_droplets
SAVE_COORDINATES
=
save_coordinates
MIN_VELOCITY
=
min_velocity
MIN_SIZE_DIFF
=
min_size_diff
N_IMAGES_COMPARED
=
n_images_compared
N_ADHESIVE_HIGH
=
n_adhesive_high
N_ADHESIVE_LOW
=
n_adhesive_low
LOW_DISTANCE_THRESHOLD
=
low_distance_threshold
EDGE_TOLERANCE
=
edge_tolerance
IMAGE_CROP
=
image_crop
CONTRAST
=
contrast
CONFIDENCE
=
confidence
FILE_FORMAT
=
file_format
else
:
import
argparse
# Parse command line arguments
parser
=
argparse
.
ArgumentParser
(
description
=
'
evaluation on cluster
'
)
parser
.
add_argument
(
'
--dataset_path
'
,
required
=
True
,
help
=
'
Dataset path to find in Mask_R_CNN\datasets\input
'
)
parser
.
add_argument
(
'
--save_path
'
,
required
=
False
,
default
=
'
test_output
'
,
help
=
'
Save path to find in Mask_R_CNN\datasets\output
'
)
parser
.
add_argument
(
'
--name_result_file
'
,
required
=
False
,
default
=
'
DSD
'
,
help
=
'
Name of the excel result file to find in Mask_R_CNN\datasets\output
'
)
parser
.
add_argument
(
'
--weights_path
'
,
required
=
True
,
help
=
'
Weights path to find in Mask_R_CNN\models
'
)
parser
.
add_argument
(
'
--weights_name
'
,
required
=
True
,
help
=
'
Choose Neuronal Network / Epoch to find in Mask_R_CNN\models
'
)
parser
.
add_argument
(
'
--file_format
'
,
required
=
True
,
help
=
''
)
parser
.
add_argument
(
'
--masks
'
,
required
=
False
,
type
=
str
,
default
=
"
False
"
,
help
=
'
Generate detection masks?
'
)
parser
.
add_argument
(
'
--device
'
,
required
=
True
,
type
=
str
,
help
=
'
is the evaluation done on CPU or GPU? 1=GPU, 0=CPU
'
)
parser
.
add_argument
(
'
--detect_oval_droplets
'
,
required
=
False
,
type
=
str
,
default
=
"
False
"
,
help
=
""
)
parser
.
add_argument
(
'
--detect_reflections
'
,
required
=
False
,
type
=
str
,
default
=
"
False
"
,
help
=
""
)
parser
.
add_argument
(
'
--detect_adhesive_droplets
'
,
required
=
False
,
type
=
str
,
default
=
"
False
"
,
help
=
""
)
parser
.
add_argument
(
'
--save_coordinates
'
,
required
=
False
,
type
=
str
,
default
=
"
False
"
,
help
=
""
)
parser
.
add_argument
(
'
--images_gpu
'
,
required
=
False
,
type
=
int
,
default
=
1
,
help
=
'
Number of images to train with on each GPU
'
)
parser
.
add_argument
(
'
--image_max
'
,
required
=
True
,
type
=
int
,
help
=
"
max. image size
"
)
parser
.
add_argument
(
'
--save_nth_image
'
,
required
=
False
,
type
=
int
,
default
=
1
,
help
=
""
)
parser
.
add_argument
(
'
--n_images_compared
'
,
required
=
False
,
type
=
int
,
default
=
3
,
help
=
""
)
parser
.
add_argument
(
'
--n_adhesive_high
'
,
required
=
False
,
type
=
int
,
default
=
3
,
help
=
""
)
parser
.
add_argument
(
'
--n_adhesive_low
'
,
required
=
False
,
type
=
int
,
default
=
2
,
help
=
""
)
parser
.
add_argument
(
'
--image_crop
'
,
required
=
False
,
type
=
int
,
default
=
None
,
help
=
""
)
parser
.
add_argument
(
'
--contrast
'
,
required
=
False
,
type
=
int
,
default
=
0
,
help
=
""
)
parser
.
add_argument
(
'
--min_aspect_ratio
'
,
required
=
False
,
type
=
float
,
default
=
0.9
,
help
=
""
)
parser
.
add_argument
(
'
--pixelsize
'
,
required
=
True
,
type
=
float
,
help
=
""
)
parser
.
add_argument
(
'
--min_velocity
'
,
required
=
False
,
type
=
float
,
default
=
0.3
,
help
=
""
)
parser
.
add_argument
(
'
--min_size_diff
'
,
required
=
False
,
type
=
float
,
default
=
0.3
,
help
=
""
)
parser
.
add_argument
(
'
--low_distance_threshold
'
,
required
=
False
,
type
=
float
,
default
=
0.05
,
help
=
""
)
parser
.
add_argument
(
'
--edge_tolerance
'
,
required
=
False
,
type
=
float
,
default
=
0.01
,
help
=
""
)
parser
.
add_argument
(
'
--confidence
'
,
required
=
False
,
type
=
float
,
default
=
0.5
,
help
=
""
)
args
=
parser
.
parse_args
()
ROOT_DIR
=
os
.
path
.
join
(
"
/rwthfs/rz/cluster
"
,
os
.
path
.
abspath
(
"
../..
"
))
WEIGHTS_DIR
=
os
.
path
.
join
(
ROOT_DIR
,
"
models
"
,
args
.
weights_path
,
args
.
weights_name
+
'
.h5
'
)
DATASET_DIR
=
os
.
path
.
join
(
ROOT_DIR
,
"
datasets/input
"
,
args
.
dataset_path
)
SAVE_DIR
=
os
.
path
.
join
(
ROOT_DIR
,
"
datasets/output
"
,
args
.
save_path
)
EXCEL_DIR
=
os
.
path
.
join
(
SAVE_DIR
,
args
.
name_result_file
+
'
.xlsx
'
)
FILE_FORMAT
=
args
.
file_format
if
args
.
detect_oval_droplets
==
"
True
"
:
DETECT_OVAL_DROPLETS
=
True
elif
args
.
detect_oval_droplets
==
"
False
"
:
DETECT_OVAL_DROPLETS
=
False
if
args
.
detect_reflections
==
"
True
"
:
DETECT_REFLECTIONS
=
True
elif
args
.
detect_reflections
==
"
False
"
:
DETECT_REFLECTIONS
=
False
if
args
.
masks
==
"
True
"
:
MASKS
=
True
elif
args
.
masks
==
"
False
"
:
MASKS
=
False
if
args
.
device
==
"
True
"
:
DEVICE
=
True
elif
args
.
device
==
"
False
"
:
DEVICE
=
False
if
args
.
detect_adhesive_droplets
==
"
True
"
:
DETECT_ADHESIVE_DROPLETS
=
True
elif
args
.
detect_adhesive_droplets
==
"
False
"
:
DETECT_ADHESIVE_DROPLETS
=
False
if
args
.
save_coordinates
==
"
True
"
:
SAVE_COORDINATES
=
True
elif
args
.
save_coordinates
==
"
False
"
:
SAVE_COORDINATES
=
False
#
IMAGE_MAX
=
args
.
image_max
IMAGES_GPU
=
args
.
images_gpu
SAVE_NTH_IMAGE
=
args
.
save_nth_image
N_IMAGES_COMPARED
=
args
.
n_images_compared
N_ADHESIVE_HIGH
=
args
.
n_adhesive_high
N_ADHESIVE_LOW
=
args
.
n_adhesive_low
IMAGE_CROP
=
args
.
image_crop
CONTRAST
=
args
.
contrast
#
MIN_ASPECT_RATIO
=
args
.
min_aspect_ratio
PIXELSIZE
=
args
.
pixelsize
MIN_VELOCITY
=
args
.
min_velocity
MIN_SIZE_DIFF
=
args
.
min_size_diff
LOW_DISTANCE_THRESHOLD
=
args
.
low_distance_threshold
EDGE_TOLERANCE
=
args
.
edge_tolerance
CONFIDENCE
=
args
.
confidence
# Directory to save logs and trained model
MODEL_DIR
=
os
.
path
.
join
(
ROOT_DIR
,
"
models
"
)
Path
(
SAVE_DIR
).
mkdir
(
parents
=
True
,
exist_ok
=
True
)
### mean pixel detectieren
images_mean_pixel
=
[]
images_path
=
glob
.
glob
(
os
.
path
.
join
(
DATASET_DIR
+
"
/*.
"
+
FILE_FORMAT
))
for
img_path
in
images_path
:
img
=
cv2
.
imread
(
img_path
)
img
=
cv2
.
cvtColor
(
img
,
cv2
.
COLOR_BGR2RGB
)
if
CONTRAST
==
1
:
# adaptive Equalization
img
=
cv2
.
cvtColor
(
img
,
cv2
.
COLOR_BGR2GRAY
)
img
=
exposure
.
equalize_adapthist
(
img
)
img
=
img
.
astype
(
'
float32
'
)
*
255
images_mean_pixel
.
append
(
img
)
color_sum
=
[
0
,
0
,
0
]
for
img2
in
images_mean_pixel
:
pixels
=
asarray
(
img2
)
pixels
=
pixels
.
astype
(
'
float32
'
)
# calculate per-channel means and standard deviations
means
=
pixels
.
mean
(
axis
=
(
0
,
1
),
dtype
=
'
float64
'
)
color_sum
+=
means
mean_pixel
=
color_sum
/
len
(
images_mean_pixel
)
# read pixelsize from JSON-File (if input data is from a Sopat measurement)
if
PIXELSIZE
==
0
:
sopat_find
=
[
file
for
file
in
os
.
listdir
(
DATASET_DIR
)
if
file
.
endswith
(
'
.json
'
)]
sopat_name
=
(
DATASET_DIR
+
'
/
'
+
sopat_find
[
0
])
sopat_name_new
=
(
DATASET_DIR
+
'
/Sopat_Log.json
'
)
with
open
(
sopat_name
,
"
r
"
,
encoding
=
"
utf-8
"
)
as
sopat_content
:
content_lines
=
sopat_content
.
readlines
()
current_line
=
1
with
open
(
sopat_name_new
,
"
w
"
,
encoding
=
"
utf-8
"
)
as
sopat_content_new
:
for
line
in
content_lines
:
if
current_line
==
30
:
pass
else
:
sopat_content_new
.
write
(
line
)
current_line
+=
1
sopat_data
=
json
.
load
(
open
(
sopat_name_new
,
"
r
"
,
encoding
=
"
utf-8
"
))
PIXELSIZE
=
sopat_data
[
"
sopatCamControlAcquisitionLog
"
][
"
conversionMicronsPerPx
"
]
# Import Mask RCNN
sys
.
path
.
append
(
ROOT_DIR
)
# To find local version of the library
from
mrcnn
import
utils
from
mrcnn
import
visualize
from
mrcnn.visualize
import
display_images
import
mrcnn.model
as
modellib
from
mrcnn.model
import
log
from
mrcnn.config
import
Config
class
DropletConfig
(
Config
):
"""
Configuration for training on the toy dataset.
Derives from the base Config class and overrides some values.
"""
# Give the configuration a recognizable name
NAME
=
"
droplet
"
# NUMBER OF GPUs to use. When using only a CPU, this needs to be set to 1.
GPU_COUNT
=
1
# Backbone network architecture
# Supported values are: resnet50, resnet101.
# You can also provide a callable that should have the signature
# of model.resnet_graph. If you do so, you need to supply a callable
# to COMPUTE_BACKBONE_SHAPE as well
BACKBONE
=
"
resnet50
"
# Generate detection masks
# False: Output only bounding boxes like in Faster-RCNN
# True: Generate masks as in Mask-RCNN
if
MASKS
is
True
:
GENERATE_MASKS
=
True
else
:
GENERATE_MASKS
=
False
# We use a GPU with 12GB memory, which can fit two images.
# Adjust down if you use a smaller GPU.
if
DEVICE
is
True
:
IMAGES_PER_GPU
=
IMAGES_GPU
else
:
IMAGES_PER_GPU
=
1
# Number of classes (including background)
NUM_CLASSES
=
1
+
1
# Background + droplet
# Skip detections with confidence < value
DETECTION_MIN_CONFIDENCE
=
CONFIDENCE
# Input image resizing
if
IMAGE_MAX
==
0
:
IMAGE_MAX_DIM
=
512
elif
IMAGE_MAX
==
1
:
IMAGE_MAX_DIM
=
1024
elif
IMAGE_MAX
==
2
:
IMAGE_MAX_DIM
=
2048
IMAGE_MIN_DIM
=
IMAGE_MAX_DIM
MEAN_PIXEL
=
mean_pixel
### Configurations
config
=
DropletConfig
()
config
.
display
()
### Notebook Preferences
# Device to load the neural network on.
# Useful if you're training a model on the same
# machine, in which case use CPU and leave the
# GPU for training.
if
DEVICE
is
True
:
dev
=
"
/gpu:0
"
# /cpu:0 or /gpu:0
else
:
dev
=
"
/cpu:0
"
# /cpu:0 or /gpu:0
# Inspect the model in training or inference modes
# values: 'inference' or 'training'
# TODO: code for 'training' test mode not ready yet
TEST_MODE
=
"
inference
"
def
get_ax
(
rows
=
1
,
cols
=
1
,
size
=
8
):
"""
Return a Matplotlib Axes array to be used in
all visualizations in the notebook. Provide a
central point to control graph sizes.
Adjust the size attribute to control how big to render images
"""
_
,
ax
=
plt
.
subplots
(
rows
,
cols
,
figsize
=
(
size
*
cols
,
size
*
rows
))
return
ax
### Load Model
# Create model in inference mode
with
tf
.
device
(
dev
):
model
=
modellib
.
MaskRCNN
(
mode
=
"
inference
"
,
model_dir
=
MODEL_DIR
,
config
=
config
)
# Load weights
print
(
"
Loading weights
"
,
WEIGHTS_DIR
)
model
.
load_weights
(
WEIGHTS_DIR
,
by_name
=
True
)
### Run Detection
class
Droplet
:
"""
Class to structure Droplet information in memory and calculate values for comparison
"""
def
__init__
(
self
,
roi
,
mask
,
img
):
self
.
roi
=
roi
if
config
.
GENERATE_MASKS
:
self
.
mask
=
mask
# calculate edge lengths and center of roi. roi[1]-roi[3]=box_width. roi[0]-roi[2]=image_height
self
.
range
=
(
abs
(
roi
[
1
]
-
roi
[
3
]),
abs
(
roi
[
0
]
-
roi
[
2
]))
self
.
center
=
(
abs
((
roi
[
0
]
+
roi
[
2
])
//
2
),
abs
((
roi
[
1
]
+
roi
[
3
])
//
2
))
# self.mean_diameter = abs((self.range[0]+self.range[1]+2)/2)
self
.
mean_diameter
=
((
self
.
range
[
1
]
+
1
)
*
(
self
.
range
[
0
]
+
1
)
**
2
)
**
(
1
/
3
)
self
.
mean_diameter_mm
=
round
(
self
.
mean_diameter
*
PIXELSIZE
/
1000
,
3
)
self
.
stuck
=
[]
self
.
check_roi
()
self
.
img
=
img
def
check_roi
(
self
):
"""
Run at Droplet creation to check for aspect ratio and whether it touches the edge
"""
global
DETECT_OVAL_DROPLETS
,
EDGE_TOLERANCE
,
MIN_ASPECT_RATIO
,
image_height
,
image_width
if
DETECT_OVAL_DROPLETS
is
True
:
if
(
# checks if bbox touches the edge
self
.
roi
[
0
]
<=
image_height
*
EDGE_TOLERANCE
or
self
.
roi
[
1
]
<=
image_width
*
EDGE_TOLERANCE
or
self
.
roi
[
2
]
>=
image_height
*
(
1
-
EDGE_TOLERANCE
)
or
self
.
roi
[
3
]
>=
image_width
*
(
1
-
EDGE_TOLERANCE
)
):
self
.
fault
=
1
else
:
self
.
fault
=
0
else
:
if
(
# checks if bbox touches the edge
self
.
roi
[
0
]
<=
image_height
*
EDGE_TOLERANCE
or
self
.
roi
[
1
]
<=
image_width
*
EDGE_TOLERANCE
or
self
.
roi
[
2
]
>=
image_height
*
(
1
-
EDGE_TOLERANCE
)
or
self
.
roi
[
3
]
>=
image_width
*
(
1
-
EDGE_TOLERANCE
)
or
# checks if bbox is within allowed aspect ratio
self
.
range
[
0
]
/
self
.
range
[
1
]
>=
1
/
MIN_ASPECT_RATIO
or
self
.
range
[
0
]
/
self
.
range
[
1
]
<=
MIN_ASPECT_RATIO
):
self
.
fault
=
1
else
:
self
.
fault
=
0
# Parameter um klebende Tropfen zu erkennen
def
distance
(
self
,
center
):
"""
Returns distance to given coordinates
"""
offset
=
np
.
array
([
center
[
0
]
-
self
.
center
[
0
],
center
[
1
]
-
self
.
center
[
1
]])
dist
=
np
.
linalg
.
norm
(
offset
)
dist
/=
self
.
mean_diameter
return
dist
def
size_difference
(
self
,
range
):
"""
Returns size difference in percent compared to given ranges
"""
size_diff
=
abs
(
1
-
((
self
.
range
[
0
]
*
self
.
range
[
1
])
/
(
range
[
0
]
*
range
[
1
])))
return
size_diff
def
visualize_result
(
memory
,
counter_1
):
"""
collects all necessary parameters from the first entry in memory and then calls visualize.display_instances()
Also appends droplet diameter to mean_diameter_total if no fault was determined
"""
# Create Lists to pass onto display_instances()
global
stuck_droplet_data
ax
=
get_ax
(
size
=
8
)
rois
,
masks
,
colors
,
diameter_vis_list
=
[],
[],
[],
[]
xl
=
False
if
memory
[
0
][
0
]:
print
(
f
"
Visualizing
{
memory
[
0
][
0
][
0
].
img
}
"
)
for
droplet
in
memory
[
0
][
0
]:
rois
.
append
(
droplet
.
roi
)
diameter_vis_list
.
append
(
droplet
.
mean_diameter_mm
)
if
config
.
GENERATE_MASKS
:
masks
.
append
(
droplet
.
mask
)
if
droplet
.
fault
==
0
:
mean_diameter_total
.
append
(
droplet
.
mean_diameter
)
colors
.
append
((
0
,
1
,
0
))
elif
droplet
.
fault
==
1
:
colors
.
append
((
1
,
0
,
0
))
elif
droplet
.
fault
==
4
:
colors
.
append
((
0
,
0
,
1
))
elif
DETECT_ADHESIVE_DROPLETS
is
False
:
mean_diameter_total
.
append
(
droplet
.
mean_diameter
)
colors
.
append
((
0
,
1
,
0
))
elif
droplet
.
fault
==
2
:
if
len
(
droplet
.
stuck
)
>=
N_ADHESIVE_HIGH
:
colors
.
append
((
1
,
0.65
,
0
))
if
not
xl
:
stuck_droplet_data
.
append
(
[
droplet
.
img
,
droplet
.
mean_diameter_mm
,
""
,
droplet
.
stuck
[
0
][
0
],
droplet
.
stuck
[
0
][
1
],
droplet
.
stuck
[
0
][
2
],
droplet
.
stuck
[
0
][
3
],
droplet
.
stuck
[
0
][
4
]])
xl
=
True
else
:
stuck_droplet_data
.
append
(
[
""
,
droplet
.
mean_diameter_mm
,
""
,
droplet
.
stuck
[
0
][
0
],
droplet
.
stuck
[
0
][
1
],
droplet
.
stuck
[
0
][
2
],
droplet
.
stuck
[
0
][
3
],
droplet
.
stuck
[
0
][
4
]])
for
data
in
droplet
.
stuck
[
1
:]:
stuck_droplet_data
.
append
(
[
""
,
""
,
""
,
data
[
0
],
data
[
1
],
data
[
2
],
data
[
3
],
data
[
4
]])
else
:
mean_diameter_total
.
append
(
droplet
.
mean_diameter
)
colors
.
append
((
0
,
1
,
0
))
elif
droplet
.
fault
==
3
:
if
len
(
droplet
.
stuck
)
>=
N_ADHESIVE_LOW
:
colors
.
append
((
1
,
0.65
,
0
))
if
not
xl
:
stuck_droplet_data
.
append
(
[
droplet
.
img
,
droplet
.
mean_diameter_mm
,
"
<5%
"
,
droplet
.
stuck
[
0
][
0
],
droplet
.
stuck
[
0
][
1
],
droplet
.
stuck
[
0
][
2
],
droplet
.
stuck
[
0
][
3
],
droplet
.
stuck
[
0
][
4
]])
xl
=
True
else
:
stuck_droplet_data
.
append
(
[
""
,
droplet
.
mean_diameter_mm
,
"
<5%
"
,
droplet
.
stuck
[
0
][
0
],
droplet
.
stuck
[
0
][
1
],
droplet
.
stuck
[
0
][
2
],
droplet
.
stuck
[
0
][
3
],
droplet
.
stuck
[
0
][
4
]])
for
data
in
droplet
.
stuck
[
1
:]:
stuck_droplet_data
.
append
(
[
""
,
""
,
"
<5%
"
,
data
[
0
],
data
[
1
],
data
[
2
],
data
[
3
],
data
[
4
]])
else
:
mean_diameter_total
.
append
(
droplet
.
mean_diameter
)
colors
.
append
((
0
,
1
,
0
))
# Convert Lists to numpy arrays and create placeholders for class_ids and scores
rois
=
np
.
array
(
rois
)
masks
=
np
.
array
(
masks
)
class_ids
=
np
.
array
(
range
(
len
(
colors
)))
scores
=
np
.
array
([
1
]
*
len
(
colors
))
img_name
=
"
result_{}.jpg
"
.
format
(
os
.
path
.
splitext
(
memory
[
0
][
2
])[
0
])
if
masks
.
any
():
masks
=
np
.
stack
(
masks
,
axis
=-
1
)
if
config
.
GENERATE_MASKS
:
visualize
.
display_instances
(
memory
[
0
][
1
],
rois
,
masks
,
class_ids
,
scores
,
ax
=
ax
,
colors
=
colors
,
captions
=
diameter_vis_list
,
title
=
None
,
save_dir
=
SAVE_DIR
,
img_name
=
img_name
,
save_img
=
True
,
number_saved_images
=
SAVE_NTH_IMAGE
,
counter_1
=
counter_1
)
else
:
visualize
.
display_instances
(
memory
[
0
][
1
],
rois
,
None
,
class_ids
,
scores
,
ax
=
ax
,
show_mask
=
False
,
colors
=
colors
,
captions
=
diameter_vis_list
,
title
=
None
,
save_dir
=
SAVE_DIR
,
img_name
=
img_name
,
save_img
=
True
,
number_saved_images
=
SAVE_NTH_IMAGE
,
counter_1
=
counter_1
)
def
pre_processing
(
image
,
crop
=
None
,
size_y
=
1024
,
size_x
=
1024
,
contrast
=
0
):
"""
Crops image and optional contrast adjustments
"""
if
crop
:
size_x
=
crop
[
0
]
size_y
=
crop
[
1
]
image_height
,
image_width
,
_
=
image
.
shape
# center crop image to given size
crop_y
=
(
image_height
-
size_y
)
//
2
crop_x
=
(
image_width
-
size_x
)
//
2
image
=
image
[
crop_y
:
crop_y
+
size_y
,
crop_x
:
crop_x
+
size_x
]
#im = Image.fromarray(image.astype(np.uint8))
#im.save(os.path.join(SAVE_DIR, 'test.bmp'))
# original = image.copy()
if
CONTRAST
!=
0
:
image
=
cv2
.
cvtColor
(
image
,
cv2
.
COLOR_BGR2GRAY
)
if
CONTRAST
==
1
:
# adaptive Equalization
image
=
exposure
.
equalize_adapthist
(
image
)
image
=
image
.
astype
(
'
float32
'
)
*
255
elif
CONTRAST
==
2
:
# contrast stretching
p2
,
p98
=
np
.
percentile
(
image
,
(
2
,
98
))
image
=
exposure
.
rescale_intensity
(
image
,
in_range
=
(
p2
,
p98
))
return
image
# Tropfendetektion & Erkennung von klebenden Tropfen
memory
=
[]
mean_diameter_total
=
[]
measurements
=
[]
stuck_droplet_data
=
[]
counter_1
=
SAVE_NTH_IMAGE
filenames
=
os
.
listdir
(
DATASET_DIR
)
filenames
.
sort
()
for
filename
in
filenames
:
if
not
filename
.
endswith
(
'
.json
'
):
image
=
cv2
.
imread
(
os
.
path
.
join
(
DATASET_DIR
,
filename
))
image
=
cv2
.
cvtColor
(
image
,
cv2
.
COLOR_BGR2RGB
)
# pre process image
if
IMAGE_CROP
or
CONTRAST
!=
0
:
image
=
pre_processing
(
image
,
crop
=
IMAGE_CROP
,
contrast
=
CONTRAST
)
image_height
,
image_width
,
_
=
image
.
shape
image_length_max
=
max
([
image_width
,
image_height
])
results
=
model
.
detect
([
image
],
filename
=
filename
,
verbose
=
1
)
r
=
results
[
0
]
# Creates a List of Droplet Objects with detected information
# Droplet object checks if droplet is too close to edge and whether aspec ratio is off on initialization
droplets
=
[]
if
config
.
GENERATE_MASKS
:
for
i
,
roi
in
enumerate
(
r
[
'
rois
'
]):
droplets
.
append
(
Droplet
(
roi
,
r
[
'
masks
'
][:,
:,
i
],
filename
))
else
:
for
roi
in
r
[
'
rois
'
]:
droplets
.
append
(
Droplet
(
roi
,
None
,
filename
))
print
(
f
"
Images in memory:
{
len
(
memory
)
}
"
)
if
DETECT_REFLECTIONS
==
True
:
for
a
,
b
in
itertools
.
combinations
(
droplets
,
2
):
if
(
a
.
center
[
0
]
>
b
.
center
[
0
]
*
0.95
and
a
.
center
[
0
]
<
b
.
center
[
0
]
*
1.05
and
a
.
center
[
1
]
>
b
.
center
[
1
]
*
0.95
and
a
.
center
[
1
]
<
b
.
center
[
1
]
*
1.05
):
if
a
.
mean_diameter_mm
<
b
.
mean_diameter_mm
:
a
.
fault
=
4
else
:
b
.
fault
=
4
# If memory is not empty iterate through droplets in current picture
if
memory
:
for
current
in
droplets
:
# If droplet has no faults so far compare to all droplets in memory
if
current
.
fault
==
0
:
shortest_distance
=
2048
closest_size_diff
=
0
# iterate memory from the back (most recent picture first)
# t is a variable for time between reference image and current memory entry
for
t
,
img
in
enumerate
(
reversed
(
memory
),
1
):
for
droplet
in
img
[
0
]:
measured_distance
=
current
.
distance
(
droplet
.
center
)
measured_size_diff
=
current
.
size_difference
(
droplet
.
range
)
measurements
.
append
([
measured_distance
,
measured_distance
/
t
,
measured_size_diff
])
# Checks whether measured distance and size diff is within defined threshholds
if
(
measured_distance
<
MIN_VELOCITY
*
t
and
measured_size_diff
<
MIN_SIZE_DIFF
):
shortest_distance
=
measured_distance
/
t
closest_size_diff
=
measured_size_diff
current
.
stuck
.
append
(((
-
1
*
t
),
droplet
.
mean_diameter_mm
,
measured_distance
,
measured_distance
/
t
,
measured_size_diff
))
droplet
.
stuck
.
append
((
t
,
current
.
mean_diameter_mm
,
measured_distance
,
measured_distance
/
t
,
measured_size_diff
))
if
measured_distance
<
LOW_DISTANCE_THRESHOLD
:
current
.
fault
=
3
else
:
current
.
fault
=
2
if
droplet
.
fault
==
2
or
droplet
.
fault
==
0
:
droplet
.
fault
=
current
.
fault
break
# Keeps track of shortest distances to help adjust threshholds
elif
measured_distance
<
shortest_distance
:
shortest_distance
=
measured_distance
/
t
closest_size_diff
=
measured_size_diff
if
current
.
fault
==
0
:
print
(
"
Droplet is valid:
"
)
print
(
f
"
\t
shortest distance to any box:
\t
{
round
(
shortest_distance
,
2
)
}
"
)
print
(
f
"
\t
size difference to closest box:
\t
{
round
(
closest_size_diff
*
100
,
2
)
}
%
"
)
else
:
print
(
"
Droplet is stuck to Lens:
"
)
print
(
f
"
\t
distance too close to box:
\t
{
round
(
shortest_distance
,
2
)
}
"
)
print
(
f
"
\t
size difference to that box:
\t
{
round
(
closest_size_diff
*
100
,
2
)
}
%
"
)
else
:
print
(
"
Droplet is either to close to the edge or aspect ratio is off
"
)
# Depending on the number of images campared, visualizes the one furthest back and removes it from the list
if
(
len
(
memory
)
==
N_IMAGES_COMPARED
):
visualize_result
(
memory
,
counter_1
)
memory
.
pop
(
0
)
# Append List of current droplets and image information to the memory
memory
.
append
((
droplets
,
image
,
filename
))
print
(
"
\n
"
)
if
counter_1
==
SAVE_NTH_IMAGE
:
counter_1
=
0
counter_1
=
counter_1
+
1
# Visualizes the remaining pictures
while
memory
:
visualize_result
(
memory
,
counter_1
)
memory
.
pop
(
0
)
if
counter_1
==
SAVE_NTH_IMAGE
:
counter_1
=
0
counter_1
=
counter_1
+
1
### Translate Mean Diameter In Actual Droplet Sizes (mm)
# recalculate mean diameter
mean_diameter_total_resize
=
[(
i
*
PIXELSIZE
/
1000
)
for
i
in
mean_diameter_total
]
### Convert Mean Diameter To Excel
df
=
pd
.
DataFrame
(
mean_diameter_total_resize
).
to_excel
(
EXCEL_DIR
,
header
=
False
,
index
=
False
)
### Save measured data for threshold tuning
if
SAVE_COORDINATES
is
True
:
pd
.
DataFrame
(
measurements
).
to_excel
(
os
.
path
.
join
(
SAVE_DIR
,
"
xyz_measurements.xlsx
"
),
header
=
False
,
index
=
False
)
pd
.
DataFrame
(
stuck_droplet_data
).
to_excel
(
os
.
path
.
join
(
SAVE_DIR
,
"
stuck_droplet_data.xlsx
"
),
header
=
False
,
index
=
False
)
print
(
"
--- %s seconds ---
"
%
(
time
.
time
()
-
start_time
))
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment