Skip to content
Snippets Groups Projects
Commit 741908c4 authored by ssibirtsev's avatar ssibirtsev
Browse files

Upload New File

parent fd82fc8b
No related branches found
No related tags found
No related merge requests found
"""
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"\tshortest distance to any box:\t{round(shortest_distance,2)}")
print(f"\tsize difference to closest box:\t{round(closest_size_diff*100,2)}%")
else:
print("Droplet is stuck to Lens:")
print(f"\tdistance too close to box:\t{round(shortest_distance,2)}")
print(f"\tsize 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))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment