This commit is contained in:
matteo porta 2022-08-02 18:15:30 +02:00
parent df3be4c784
commit 304fd9d664
28 changed files with 590 additions and 179 deletions

View File

@ -44,7 +44,4 @@ port: COM1
baudrate: 9600 baudrate: 9600
[vision] [vision]
detection_threshold: 0.5
neural_network: hs5-20000 neural_network: hs5-20000
; recipes_path: ./config/vision_test_recipes

View File

@ -22,7 +22,7 @@ instruction: CONTROLLARE PRESENZA TERMORESTRINGENTE
[markers] [markers]
[zones] [zones]
p1: 1000,500 800,800 hs-ok # HEATSINK PRESENT p1: 1200,600 1800,1200 hs-ok # HEATSINK PRESENT
[labels] [labels]
p1: 660,1200 120 0xffffffff 0xff000000 4 TERMORESTRINGENTE p1: 660,1200 120 0xffffffff 0xff000000 4 TERMORESTRINGENTE

View File

@ -31,6 +31,7 @@ $* 2> >(sed $'s/.*/\e[31m&\e[m/' >&2) # &
# --auto-login-user \ # --auto-login-user \
# --autotests-archive \ # --autotests-archive \
# --camera-edits \ # --camera-edits \
# --fail-vision \
# --full-screen \ # --full-screen \
# --interact \ # --interact \
# --maximized \ # --maximized \

View File

@ -49,7 +49,7 @@ registers = {
180: "END SEQUENCE PROGRAM", 180: "END SEQUENCE PROGRAM",
190: "END PLUG", 190: "END PLUG",
200: "END CAGE", 200: "END CAGE",
210: "FINE TEST", # WAITING THE START OF A NEW TEST", 210: "FINE TEST", # "WAITING THE START OF A NEW TEST",
}, }], }, }],
"Running test: phase backwards time": [33 - 1, {"dt": "16bit_uint", "f": 26, }], "Running test: phase backwards time": [33 - 1, {"dt": "16bit_uint", "f": 26, }],
"Running test: filling pressure": [34 - 1, {"dt": "32bit_int", "f": 21, }], "Running test: filling pressure": [34 - 1, {"dt": "32bit_int", "f": 21, }],

View File

@ -47,6 +47,11 @@ else:
def Interpreter(*args, **kwargs): def Interpreter(*args, **kwargs):
raise ValueError("\"--no-tflite\" in sys.argv") raise ValueError("\"--no-tflite\" in sys.argv")
if "--fail-vision" not in sys.argv:
vision_override = None
else:
vision_override = False
# # Patch the location of gfile # # Patch the location of gfile
# tf.gfile = tf.io.gfile # tf.gfile = tf.io.gfile
@ -95,7 +100,7 @@ class Vision(Component):
def config_changed(self): def config_changed(self):
# OBJECT DETECTION # OBJECT DETECTION
self.detection_threshold = float(self.config[self.name]["detection_threshold"]) self.detection_threshold = float(self.config[self.name].get("detection_threshold", 0.5))
# recipe # recipe
self.zones = None self.zones = None
self.labels = None self.labels = None
@ -122,20 +127,26 @@ class Vision(Component):
self.num_classes = len(label_map.item) self.num_classes = len(label_map.item)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=self.num_classes, use_display_name=True) categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=self.num_classes, use_display_name=True)
self.category_index = label_map_util.create_category_index(categories) self.category_index = label_map_util.create_category_index(categories)
for k in self.category_index:
self.category_index[k]["color"] = self.category_index[k]["color"].replace("0x", "#")
self.classes_map = {c["name"]: k for k, c in self.category_index.items()} self.classes_map = {c["name"]: k for k, c in self.category_index.items()}
self.zone_detection_filter_mode = self.config[self.name].get("zone_detection_filter_mode", "box_touches") self.zone_detection_filter_mode = self.config[self.name].get("zone_detection_filter_mode", "box_inside")
self.zone_detection_preference_mode = self.config[self.name].get("zone_detection_preference_mode", "distance") self.zone_detection_preference_mode = self.config[self.name].get("zone_detection_preference_mode", "score")
def get_center(self, rect): @staticmethod
def get_center(rect):
return [(rect[0] + rect[2]) / 2, (rect[1] + rect[3]) / 2] return [(rect[0] + rect[2]) / 2, (rect[1] + rect[3]) / 2]
def get_size(self, rect): @staticmethod
def get_size(rect):
return [rect[2] - rect[0], rect[3] - rect[1]] return [rect[2] - rect[0], rect[3] - rect[1]]
def get_box(self, center, size): @staticmethod
def get_box(center, size):
return [center[0] - size[0] / 2, center[1] - size[1] / 2, center[0] + size[0] / 2, center[1] + size[1] / 2] return [center[0] - size[0] / 2, center[1] - size[1] / 2, center[0] + size[0] / 2, center[1] + size[1] / 2]
def get_distance(self, p1, p2): @staticmethod
def get_distance(p1, p2):
return pow((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2, 1 / 2) return pow((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2, 1 / 2)
def clear_recipe(self): def clear_recipe(self):
@ -180,6 +191,7 @@ class Vision(Component):
if recipe is None: if recipe is None:
self.clear_recipe() self.clear_recipe()
else: else:
self.recipe = recipe
self._set_recipe(self.recipes_dir / str(recipe)) self._set_recipe(self.recipes_dir / str(recipe))
def parse_markers(self, config=None): def parse_markers(self, config=None):
@ -229,9 +241,10 @@ class Vision(Component):
shape = "ellipse" shape = "ellipse"
d_class = self.category_index[self.classes_map[d_class]] d_class = self.category_index[self.classes_map[d_class]]
zones[zone_name] = { zones[zone_name] = {
"border_color": QColor(d_class["color"].replace("0x", "#")), "border_color": QColor(d_class["color"]),
"border_thickness": 25, "border_thickness": 25,
"box": self.get_box(center, size), "box": self.get_box(center, size),
"convert_negative_placement": False,
"center": center, "center": center,
"class": d_class, "class": d_class,
"fill_color": QColor("#00000000"), "fill_color": QColor("#00000000"),
@ -426,22 +439,23 @@ class Vision(Component):
style = { style = {
"border_thickness": 25, "border_thickness": 25,
"fill_color": QColor("#00000000"), "fill_color": QColor("#00000000"),
"shape": "ellipse", "shape": "rect",
"convert_negative_placement": False,
} }
items = {} items = {}
for item_name, item in enumerate(detections): for item_name, item in enumerate(detections):
items[str(item_name)] = { items[str(item_name)] = {
"box": item["box"], **item,
**style, **style,
"border_color": QColor(item["class"]["color"].replace("0x", "#")), "border_color": QColor(item["class"]["color"]),
} }
return items return items
else: else:
return {} return {}
def process_detections(self, detections): def process_detections(self, detections):
if self.zones is None or not len(self.zones) or detections is None or not len(detections): if self.zones is None or not len(self.zones):
return {} return None
# MATCH DETECTIONS WITH RECIPE # MATCH DETECTIONS WITH RECIPE
results = dict.fromkeys(self.zones) results = dict.fromkeys(self.zones)
for detection in detections: for detection in detections:
@ -533,8 +547,13 @@ class Vision(Component):
"expected": expected_class, "expected": expected_class,
"detection": detection, "detection": detection,
} }
global vision_override
if vision_override is None:
ok = all(map(lambda detection: detection["ok"] is True, checked.values()))
else:
ok = vision_override
return { return {
"ok": all(map(lambda detection: detection["ok"] is True, checked.values())), "ok": ok,
"results": checked, "results": checked,
} }
@ -542,6 +561,7 @@ class Vision(Component):
# DRAW ZONES RESULTS # DRAW ZONES RESULTS
if self.zones is not None and len(self.zones) and results is not None and len(results): if self.zones is not None and len(self.zones) and results is not None and len(results):
style = { style = {
"pen_line": "SolidLine",
"border_thickness": 50, "border_thickness": 50,
"fill_color": QColor("#00000000"), "fill_color": QColor("#00000000"),
} }
@ -561,9 +581,7 @@ class Vision(Component):
for item_name, item in results["results"].items(): for item_name, item in results["results"].items():
zone = self.zones[item_name] zone = self.zones[item_name]
items[str(item_name)] = { items[str(item_name)] = {
"center": zone["center"], **zone,
"size": zone["size"],
"shape": zone["shape"],
**style, **style,
"border_color": Qt.green if item["ok"] else Qt.red, "border_color": Qt.green if item["ok"] else Qt.red,
} }
@ -571,9 +589,30 @@ class Vision(Component):
else: else:
return {} return {}
@staticmethod
def convert_negative_placement(p, painter):
for k in p:
if p[k] < 0:
if k.startswith("x") or k.startswith("w"):
p[k] += painter.device().width()
elif k.startswith("y") or k.startswith("h"):
p[k] += painter.device().height()
else:
raise AssertionError("could not detect variable direction")
@staticmethod
def apply_placement_offset(p, offset):
for k in p:
if k.startswith("x"):
p[k] += offset["x"]
elif k.startswith("y"):
p[k] += offset["y"]
else:
raise AssertionError("could not detect variable direction")
def render_items(self, items, offset=None, qimage=None, painter=None): def render_items(self, items, offset=None, qimage=None, painter=None):
if offset is None: if offset is None:
offset = [0, 0] offset = {"x": 0, "y": 0}
if painter is None: if painter is None:
if qimage is None: if qimage is None:
raise AssertionError("one of 'qimage' or 'painter' parameter must not be None") raise AssertionError("one of 'qimage' or 'painter' parameter must not be None")
@ -581,35 +620,36 @@ class Vision(Component):
painter.begin(qimage) painter.begin(qimage)
for item_name, item in items.items(): for item_name, item in items.items():
try: try:
v = {} convert_negative_placement = item.get("convert_negative_placement", True)
if "box" in item: if "box" in item:
v["x1"], v["y1"], v["x2"], v["y2"] = item["box"][1] + offset[1], item["box"][0] + offset[0], item["box"][3] + offset[1], item["box"][2] + offset[0] p = {"x1": item["box"][1], "y1": item["box"][0], "x2": item["box"][3], "y2": item["box"][2], }
v["w"], v["h"] = v["x2"] - v["x1"], v["y2"] - v["y1"] if convert_negative_placement:
v["xc"], v["yc"] = self.get_center([v["x1"], v["y1"], v["x2"], v["y2"]]) Vision.convert_negative_placement(p, painter)
Vision.apply_placement_offset(p, offset)
p["w"], p["h"] = p["x2"] - p["x1"], p["y2"] - p["y1"]
p["xc"], p["yc"] = Vision.get_center([p["x1"], p["y1"], p["x2"], p["y2"]])
elif "location" in item: elif "location" in item:
v["x1"], v["y1"] = item["location"][1] + offset[1], item["location"][0] + offset[0] p = {"x1": item["location"][1], "y1": item["location"][0], }
if convert_negative_placement:
Vision.convert_negative_placement(p, painter)
Vision.apply_placement_offset(p, offset)
if "size" in item: if "size" in item:
v["w"], v["h"] = item["size"][1], item["size"][0] p["w"], p["h"] = item["size"][1], item["size"][0]
v["x2"], v["y2"] = v["x1"] + v["w"], v["y1"] + v["h"] p["x2"], p["y2"] = p["x1"] + p["w"], p["y1"] + p["h"]
v["xc"], v["yc"] = self.get_center([v["x1"], v["y1"], v["x2"], v["y2"]]) p["xc"], p["yc"] = Vision.get_center([p["x1"], p["y1"], p["x2"], p["y2"]])
else: else:
v["w"], v["h"] = 0, 0 p["w"], p["h"] = 0, 0
v["x2"], v["y2"] = v["x1"], v["y1"] p["x2"], p["y2"] = p["x1"], p["y1"]
v["xc"], v["yc"] = v["x1"], v["y1"] p["xc"], p["yc"] = p["x1"], p["y1"]
elif "center" in item and "size" in item: elif "center" in item and "size" in item:
v["xc"], v["yc"] = item["center"][1] + offset[1], item["center"][0] + offset[0] p = {"xc": item["center"][1], "yc": item["center"][0], }
v["w"], v["h"] = item["size"][1], item["size"][0] if convert_negative_placement:
v["x1"], v["y1"], v["x2"], v["y2"] = self.get_box([v["xc"], v["yc"]], [v["w"], v["h"]]) Vision.convert_negative_placement(p, painter)
Vision.apply_placement_offset(p, offset)
p["w"], p["h"] = item["size"][1], item["size"][0]
p["x1"], p["y1"], p["x2"], p["y2"] = Vision.get_box([p["xc"], p["yc"]], [p["w"], p["h"]])
else: else:
raise AssertionError("item has no valid positioning information") raise AssertionError("item has no valid positioning information")
for k in list(v):
if v[k] < 0:
if k.startswith("x") or k.startswith("w"):
v[k] = painter.device().width() + v[k]
elif k.startswith("y") or k.startswith("h"):
v[k] = painter.device().height() + v[k]
else:
raise AssertionError("could not detect variable direction")
painter.setOpacity(item.get("opacity", 0.5)) painter.setOpacity(item.get("opacity", 0.5))
painter.setBrush(QBrush(item.get("fill_color", QColor("#ffffff")), getattr(Qt, item.get("brush_pattern", "SolidPattern")))) painter.setBrush(QBrush(item.get("fill_color", QColor("#ffffff")), getattr(Qt, item.get("brush_pattern", "SolidPattern"))))
painter.setPen(QPen( painter.setPen(QPen(
@ -620,14 +660,14 @@ class Vision(Component):
getattr(Qt, item.get("pen_join", "MiterJoin")), getattr(Qt, item.get("pen_join", "MiterJoin")),
)) ))
if item["shape"] == "ellipse": if item["shape"] == "ellipse":
painter.drawEllipse(QPointF(v["xc"], v["yc"]), v["w"] / 2, v["h"] / 2) painter.drawEllipse(QPointF(p["xc"], p["yc"]), p["w"] / 2, p["h"] / 2)
elif item["shape"] == "cross": elif item["shape"] == "cross":
painter.drawLine(QLineF(v["xc"], v["y1"], v["xc"], v["y2"])) painter.drawLine(QLineF(p["xc"], p["y1"], p["xc"], p["y2"]))
painter.drawLine(QLineF(v["x1"], v["yc"], v["x2"], v["yc"])) painter.drawLine(QLineF(p["x1"], p["yc"], p["x2"], p["yc"]))
elif item["shape"] == "line": elif item["shape"] == "line":
painter.drawLine(QLineF(v["xc"], v["yc"], v["x2"], v["y2"])) painter.drawLine(QLineF(p["xc"], p["yc"], p["x2"], p["y2"]))
elif item["shape"] == "rect": elif item["shape"] == "rect":
painter.drawRect(QRectF(v["x1"], v["y1"], v["x2"], v["y2"])) painter.drawRect(QRectF(QPointF(p["x1"], p["y1"]), QPointF(p["x2"], p["y2"])))
elif item["shape"] == "text": elif item["shape"] == "text":
old_render_hints = painter.renderHints() old_render_hints = painter.renderHints()
painter.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing, True) painter.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing, True)
@ -640,7 +680,7 @@ class Vision(Component):
font.setWordSpacing(item.get("font_word_spacing", 0)) font.setWordSpacing(item.get("font_word_spacing", 0))
font.setPixelSize(round(item.get("font_size", 25))) font.setPixelSize(round(item.get("font_size", 25)))
path = QPainterPath() path = QPainterPath()
path.addText(v["x1"], v["y1"], font, item["text"]) path.addText(p["x1"], p["y1"], font, item["text"])
painter.drawPath(path) painter.drawPath(path)
painter.setRenderHints(old_render_hints, True) painter.setRenderHints(old_render_hints, True)
else: else:

View File

@ -1,4 +1,5 @@
import glob import glob
import json
import os import os
import shutil import shutil
from datetime import datetime from datetime import datetime
@ -26,7 +27,11 @@ class VisionSaver(Component):
self.minimum_disk_free_space_gb = float(self.minimum_disk_free_space_gb) self.minimum_disk_free_space_gb = float(self.minimum_disk_free_space_gb)
self.time_format = self.config[self.name]["time_format"] self.time_format = self.config[self.name]["time_format"]
def save(self, save_time, img, mask=True, resize=True): def save(self, save_time=None, suffix=None, frame=None, vision=None, resize=None, mask=None):
self.remove_older_images_if_needed()
if type(save_time) is None:
save_time = datetime.now()
else:
if type(save_time) is float: if type(save_time) is float:
save_time = int(save_time) save_time = int(save_time)
if type(save_time) is int: if type(save_time) is int:
@ -36,24 +41,46 @@ class VisionSaver(Component):
timestamp = save_time.strftime(self.time_format) timestamp = save_time.strftime(self.time_format)
save_dir = self.location / save_time.strftime("%Y") / save_time.strftime("%m") save_dir = self.location / save_time.strftime("%Y") / save_time.strftime("%m")
os.makedirs(save_dir, exist_ok=True) os.makedirs(save_dir, exist_ok=True)
out_path = save_dir / f"{timestamp}.png" out_paths = []
self.log.info(f"saving {out_path}") if frame is not None:
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) if suffix is not None:
if resize and self.resize_resolution is not None: out_paths.append(save_dir / f"{timestamp}.{suffix}.png")
img = cv2.resize(img, self.resize_resolution, interpolation=cv2.INTER_LINEAR)
if mask and self.mask_zones is not None:
height, width, channels = img.shape
out = np.full(
[height, width, channels],
[0] * channels
)
for zone_name in self.mask_zones:
zone = self.bench.zones[zone_name]["box"]
out[zone[1]:zone[3], zone[0]:zone[2]] = img[zone[1]:zone[3], zone[0]:zone[2]]
else: else:
out = img out_paths.append(save_dir / f"{timestamp}.png")
cv2.imwrite(str(out_path), out) self.log.info(f"saving {out_paths[-1]}")
return out_path frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
# resize
if resize is None or resize is True:
resize = self.resize_resolution
elif resize is False:
resize = None
if resize is not None:
frame = cv2.resize(frame, resize, interpolation=cv2.INTER_LINEAR)
# mask
if mask is None or mask is True:
mask = self.mask_zones
elif mask is False:
mask = None
if mask is not None:
height, width, channels = frame.shape
out = np.full([height, width, channels], [0] * channels)
for zone_name in mask:
zone = self.bench.zones[zone_name]["box"]
out[zone[1]:zone[3], zone[0]:zone[2]] = frame[zone[1]:zone[3], zone[0]:zone[2]]
else:
out = frame
# save frame
cv2.imwrite(str(out_paths[-1]), out)
if vision is not None:
if suffix is not None:
out_paths.append(save_dir / f"{timestamp}.{suffix}.json")
else:
out_paths.append(save_dir / f"{timestamp}.json")
self.log.info(f"saving {out_paths[-1]}")
# save vision
with open(out_paths[-1], "w") as f:
json.dump(vision, f)
return out_paths
def remove_older_images_if_needed(self): def remove_older_images_if_needed(self):
if self.minimum_disk_free_space_gb is None: if self.minimum_disk_free_space_gb is None:

View File

@ -46,19 +46,19 @@ class Archive(Widget):
replace_widget(self, "crud_w", self.crud) replace_widget(self, "crud_w", self.crud)
self.selected = None self.selected = None
self.print_b.setEnabled(False) self.print_b.setEnabled(False)
self.crud().db_tw.setSelectionBehavior(QAbstractItemView.SelectRows) self.crud.db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
self.crud().db_tw.setSelectionMode(QAbstractItemView.SingleSelection) self.crud.db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
self.crud().db_tw.itemSelectionChanged.connect(self.check) self.crud.db_tw.itemSelectionChanged.connect(self.check)
self.print_b.clicked.connect(self.print_label) self.print_b.clicked.connect(self.print_label)
def check(self): def check(self):
if not self.crud().modified: if not self.crud.modified:
selected = self.crud().get_selected_rows() selected = self.crud.get_selected_rows()
if len(selected) == 1: if len(selected) == 1:
selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line) selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line)
if selected >= 0 and selected < len(self.crud().data_index): if selected >= 0 and selected < len(self.crud.data_index):
selected = self.crud().data_index[selected] selected = self.crud.data_index[selected]
self.selected = self.crud().db.table_model.get_by_id(selected) self.selected = self.crud.db.table_model.get_by_id(selected)
self.print_b.setEnabled(True) self.print_b.setEnabled(True)
return return
self.selected = None self.selected = None

View File

@ -46,19 +46,19 @@ class Autotests_Archive(Widget):
replace_widget(self, "crud_w", self.crud) replace_widget(self, "crud_w", self.crud)
self.selected = None self.selected = None
self.print_b.setEnabled(False) self.print_b.setEnabled(False)
self.crud().db_tw.setSelectionBehavior(QAbstractItemView.SelectRows) self.crud.db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
self.crud().db_tw.setSelectionMode(QAbstractItemView.SingleSelection) self.crud.db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
self.crud().db_tw.itemSelectionChanged.connect(self.check) self.crud.db_tw.itemSelectionChanged.connect(self.check)
self.print_b.clicked.connect(self.print_label) self.print_b.clicked.connect(self.print_label)
def check(self): def check(self):
if not self.crud().modified: if not self.crud.modified:
selected = self.crud().get_selected_rows() selected = self.crud.get_selected_rows()
if len(selected) == 1: if len(selected) == 1:
selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line) selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line)
if selected >= 0 and selected < len(self.crud().data_index): if selected >= 0 and selected < len(self.crud.data_index):
selected = self.crud().data_index[selected] selected = self.crud.data_index[selected]
self.selected = self.crud().db.table_model.get_by_id(selected) self.selected = self.crud.db.table_model.get_by_id(selected)
self.print_b.setEnabled(True) self.print_b.setEnabled(True)
return return
self.selected = None self.selected = None

View File

@ -26,6 +26,18 @@
<string>Crud</string> <string>Crud</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QGroupBox" name="db_gb"> <widget class="QGroupBox" name="db_gb">
<property name="sizePolicy"> <property name="sizePolicy">

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>28</width> <width>94</width>
<height>28</height> <height>16</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -20,6 +20,18 @@
<string>Dialog</string> <string>Dialog</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QWidget" name="centralWidget" native="true"> <widget class="QWidget" name="centralWidget" native="true">
<property name="sizePolicy"> <property name="sizePolicy">

View File

@ -1,4 +1,10 @@
from PyQt5.QtGui import QColor
def calc_foreground_color(background_color): def calc_foreground_color(background_color):
if type(background_color) is QColor:
background_color = [background_color.red(), background_color.green(), background_color.blue(), ]
to_qcolor = True
colors = [] colors = []
for c in background_color[:3]: for c in background_color[:3]:
c /= 255 # 8bit to sRGB c /= 255 # 8bit to sRGB
@ -8,6 +14,8 @@ def calc_foreground_color(background_color):
c = ((c + 0.055) / 1.055) ** 2.4 c = ((c + 0.055) / 1.055) ** 2.4
colors.append(c) colors.append(c)
# Luminance # Luminance
L = 0.2126 * colors[0] + 0.7152 * colors[1] + 0.0722 * colors[1] L = 0.2126 * colors[0] + 0.7152 * colors[1] + 0.0722 * colors[2]
foreground_color = [(1 - round(L)) * 255] * 3 foreground_color = [(1 - round(L)) * 255] * 3
if to_qcolor:
foreground_color = QColor(*foreground_color)
return foreground_color return foreground_color

View File

@ -6,14 +6,26 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>987</width> <width>955</width>
<height>210</height> <height>161</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Leak Step Editor</string> <string>Leak Step Editor</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="font"> <property name="font">

View File

@ -6,14 +6,26 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>347</width>
<height>600</height> <height>50</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Recipe Selection</string> <string>Recipe Selection</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QPushButton" name="select_b"> <widget class="QPushButton" name="select_b">
<property name="sizePolicy"> <property name="sizePolicy">

View File

@ -38,18 +38,18 @@ class Test(Widget):
self.step = None self.step = None
# INIT CYCLE STATES # INIT CYCLE STATES
self.cycle_available_steps = { self.cycle_available_steps = {
# "assembly_1": Test_Assembly(self.select_step_img("assembly_1"), u"INSERIRE SENSORE"), # "assembly_1": Test_Assembly(img_path=self.select_step_img("assembly_1"), text=u"INSERIRE SENSORE", widget=None),
"autotest": Test_Assembly(None, u"ESEGUIRE PROCEDURA DI AUTOTEST", Test_Autotest()), "autotest": Test_Assembly(img_path=None, text=u"ESEGUIRE PROCEDURA DI AUTOTEST", widget=Test_Autotest()),
"barcodes": Test_Assembly(self.select_step_img("scan"), u"LEGGERE IL BARCODE DEL PEZZO DA COLLAUDARE", Test_Barcodes()), "barcodes": Test_Assembly(img_path=self.select_step_img("scan"), text=u"LEGGERE IL BARCODE DEL PEZZO DA COLLAUDARE", widget=Test_Barcodes()),
"done": Test_Assembly(self.select_step_img("success"), u"COLLAUDO COMPLETATO"), "done": Test_Assembly(img_path=self.select_step_img("success"), text=u"COLLAUDO COMPLETATO", widget=None),
"emergency": Test_Assembly(self.select_step_img("reset_emergency"), u"EMERGENZA INTERVENUTA - RIPRISTINARE PULSANTE E SELEZIONARE \"RESET EMERGENZA\" DAL MEN\u00d9 \"STRUMENTI\""), "emergency": Test_Assembly(img_path=self.select_step_img("reset_emergency"), text=u"EMERGENZA INTERVENUTA - RIPRISTINARE PULSANTE E SELEZIONARE \"RESET EMERGENZA\" DAL MEN\u00d9 \"STRUMENTI\"", widget=None),
"fail": Test_Assembly(self.select_step_img("fail"), u"CICLO INTERROTTO"), "fail": Test_Assembly(img_path=self.select_step_img("fail"), text=u"CICLO INTERROTTO", widget=None),
"leak": Test_Assembly(None, u"PREMERE START PER INIZIARE LA PROVA TENUTA", Test_Leak(components=self.components, recipe=self.recipe, step=self.step)), "leak": Test_Assembly(img_path=None, text=None, widget=Test_Leak(components=self.components, recipe=self.recipe, step=self.step)),
"print": Test_Assembly(self.select_step_img("print"), u"STAMPA ETICHETTA IN CORSO"), "print": Test_Assembly(img_path=self.select_step_img("print"), text=u"STAMPA ETICHETTA IN CORSO", widget=None),
"select_recipe": Test_Assembly(None, u"SELEZIONARE IL CODICE DA COLLAUDARE", Recipe_Selection()), "select_recipe": Test_Assembly(img_path=None, text=u"SELEZIONARE IL CODICE DA COLLAUDARE", widget=Recipe_Selection()),
"vision": Test_Assembly(None, u"VERIFICARE CONTROLLO CON TELECAMERA", Test_Vision(components=self.components, recipe=self.recipe, step=self.step)), "vision": Test_Assembly(img_path=None, text=u"VERIFICARE CONTROLLO CON TELECAMERA", widget=Test_Vision(components=self.components, recipe=self.recipe, step=self.step)),
"wait": Test_Assembly(self.select_step_img("wait"), u"ATTENDERE - PAUSA INTER CICLO"), "wait": Test_Assembly(img_path=self.select_step_img("wait"), text=u"ATTENDERE - PAUSA INTER CICLO", widget=None),
None: Test_Assembly(self.select_step_img("warning"), u"ATTENZIONE - LA RICETTA SELEZIONATA NON CONTIENE FASI DI TEST"), None: Test_Assembly(img_path=self.select_step_img("warning"), text=u"ATTENZIONE - LA RICETTA SELEZIONATA NON CONTIENE FASI DI TEST", widget=None),
} }
self.cycle_steps = None self.cycle_steps = None
self.cycle_index = -1 self.cycle_index = -1
@ -155,16 +155,19 @@ class Test(Widget):
# RESET TEST DATA # RESET TEST DATA
self.data = {"ok": True, "overridden": False} self.data = {"ok": True, "overridden": False}
self.archived = None self.archived = None
# COUNT RESET
self.pieces[0] = 0
self.pieces[1] = 0
elif action == "fail": elif action == "fail":
self.log.info(f"cycle next: action: {action!r}") self.log.info(f"cycle next: action: {action!r}")
# COUNT FAIL
self.pieces[1] += 1
# FAIL AND RESTART TEST # FAIL AND RESTART TEST
self.step = Steps(type="fail") self.step = Steps(type="fail")
self.cycle_index = -1 self.cycle_index = -1
# RESET TEST DATA # RESET TEST DATA
self.data = {"ok": True, "overridden": False} self.data = {"ok": True, "overridden": False}
self.archived = None self.archived = None
# COUNT FAIL
self.pieces[1] += 1
elif action is not None: elif action is not None:
raise NotImplementedError(f"cycle next: action {action!r} is not a valid action") raise NotImplementedError(f"cycle next: action {action!r} is not a valid action")
# if action did not set the next cycle step # if action did not set the next cycle step
@ -270,11 +273,13 @@ class Test(Widget):
def done(self, ok=True): def done(self, ok=True):
self.log.info("cycle done") self.log.info("cycle done")
t = self.data.get("vision", {}).get(0, {}).get("time", None) vision_test_1 = self.data.get("vision", {}).get(0, {})
f = self.data.get("vision", {}).get(0, {}).get("frame", None) out_paths = self.components["vision_saver"].save(
if t is not None and f is not None: save_time=vision_test_1.get("time", None),
file_path = self.components["vision_saver"].save(t, f) frame=vision_test_1.get("frame", None),
self.log.info(f"cycle vision image saved locally: {file_path!r}") # vision=vision_test_1.get("detections", None),
)
self.log.info(f"cycle vision saved locally: {out_paths!r}")
for vision in self.data["vision"].values(): for vision in self.data["vision"].values():
vision.pop("frame", None) vision.pop("frame", None)
vision.pop("render", None) vision.pop("render", None)
@ -287,10 +292,14 @@ class Test(Widget):
def print(self, archived=None): def print(self, archived=None):
self.log.info("cycle print") self.log.info("cycle print")
# LABEL PRINT # LABEL PRINT
leak_test_1 = self.data.get("leak", {}).get(0, {})
leak_test_1_step_spec = leak_test_1.get("step", {}).get("spec", {})
leak_test_1_results = leak_test_1.get("results", {})
leak_test_1_results_data = leak_test_1_results.get("data", {})
datamatrix = str(archived.recipe.part_number) + archived.time.strftime("%m%y") + f"{archived.id:0>5}" datamatrix = str(archived.recipe.part_number) + archived.time.strftime("%m%y") + f"{archived.id:0>5}"
pmax = self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_pressure", "-") pmax = leak_test_1_step_spec.get("test_pressure", "-")
pmin = self.data.get("leak", {}).get(0, {}).get("results", {}).get("data", {}).get("Running test: pressure at the end of settling", "-") pmin = leak_test_1_results_data.get("Running test: pressure at the end of settling", "-")
leak = self.data.get("leak", {}).get(0, {}).get("results", {}).get("data", {}).get("Running test: measured leak", "-") leak = leak_test_1_results_data.get("Running test: measured leak", "-")
context = { context = {
"DATAMATRIX": datamatrix, "DATAMATRIX": datamatrix,
"DATAMATRIX_TEXT": datamatrix, "DATAMATRIX_TEXT": datamatrix,
@ -302,13 +311,13 @@ class Test(Widget):
"PMAX": f"{pmax:.3f}" if type(pmax) is not str else pmax, "PMAX": f"{pmax:.3f}" if type(pmax) is not str else pmax,
"PMIN": f"{pmin:.3f}" if type(pmin) is not str else pmin, "PMIN": f"{pmin:.3f}" if type(pmin) is not str else pmin,
"LEAK": f"{leak:.3f}" if type(leak) is not str else leak, "LEAK": f"{leak:.3f}" if type(leak) is not str else leak,
"TFILL": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("filling_time", "-")), "TFILL": str(leak_test_1_step_spec.get("filling_time", "-")),
"TSTAB": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("settling_time", "-")), "TSTAB": str(leak_test_1_step_spec.get("settling_time", "-")),
"TTEST": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_time", "-")), "TTEST": str(leak_test_1_step_spec.get("test_time", "-")),
"MAXLEAK": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_pressure_max_delta", "-")), "MAXLEAK": str(leak_test_1_step_spec.get("test_pressure_max_delta", "-")),
"PTESTMIN": str(self.data.get("leak", {}).get(0, {}).get("step", {}).get("spec", {}).get("test_pressure_min_delta", "-")), "PTESTMIN": str(leak_test_1_step_spec.get("test_pressure_min_delta", "-")),
"RESULT_L1": "ESITO" + str(" FORZATO" if self.data.get("overridden", False) else ""), "RESULT_L1": "ESITO" + str(" FORZATO" if self.data.get("overridden", False) else ""),
"RESULT_L2": str("CONFORME" if self.data.get("leak", {}).get(0, {}).get("results", {}).get("ok", False) else "SCARTO"), "RESULT_L2": str("CONFORME" if leak_test_1_results.get("ok", False) else "SCARTO"),
} }
self.components["label_printer"].print_label("EtichettaR5", context=context) self.components["label_printer"].print_label("EtichettaR5", context=context)
self.log.info(f"cycle printed: {context!r}") self.log.info(f"cycle printed: {context!r}")

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1003</width> <width>804</width>
<height>255</height> <height>86</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>422</width> <width>184</width>
<height>139</height> <height>103</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">

View File

@ -1,3 +1,5 @@
import weakref
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from ui.helpers import replace_widget from ui.helpers import replace_widget
@ -39,6 +41,8 @@ class Test_Assembly(Widget):
else: else:
if hasattr(self, attr): if hasattr(self, attr):
delattr(self, attr) delattr(self, attr)
if hasattr(widget, "set_parent_assembly_widget"):
widget.set_parent_assembly_widget(weakref.ref(self))
self.widget.setVisible(True) self.widget.setVisible(True)
else: else:
self.widget.setVisible(False) self.widget.setVisible(False)

View File

@ -7,13 +7,25 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>94</width> <width>94</width>
<height>108</height> <height>89</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Test Assembly</string> <string>Test Assembly</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="img_l"> <widget class="QLabel" name="img_l">
<property name="sizePolicy"> <property name="sizePolicy">

View File

@ -6,14 +6,27 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>18</width> <width>94</width>
<height>18</height> <height>16</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Test Autotest</string> <string>Test Autotest</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"/> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -6,14 +6,26 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>187</width> <width>169</width>
<height>162</height> <height>144</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Test Barcodes</string> <string>Test Barcodes</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="13" column="0"> <item row="13" column="0">
<widget class="QLineEdit" name="barcodes_le"/> <widget class="QLineEdit" name="barcodes_le"/>
</item> </item>

View File

@ -15,6 +15,10 @@ class Test_Leak(Test_Test):
self.components["tecna_t3"].write_recipe(self.recipe, self.step) self.components["tecna_t3"].write_recipe(self.recipe, self.step)
self.get_connection = self.components["tecna_t3"].out.connect(self.get) self.get_connection = self.components["tecna_t3"].out.connect(self.get)
self.components["tecna_t3"].resume() self.components["tecna_t3"].resume()
if self.parent_assembly_widget is not None:
self.parent_assembly_widget().set_text(text=None)
self.start_b.setEnabled(False)
self.stop_b.setEnabled(False)
def stop(self): def stop(self):
# disable tes loop # disable tes loop
@ -22,6 +26,10 @@ class Test_Leak(Test_Test):
self.components["tecna_t3"].pause() self.components["tecna_t3"].pause()
self.disconnect(self.get_connection) self.disconnect(self.get_connection)
super().stop() super().stop()
if self.parent_assembly_widget is not None:
self.parent_assembly_widget().set_text(text=None)
self.start_b.setEnabled(False)
self.stop_b.setEnabled(False)
def get(self, data=None, override=False): def get(self, data=None, override=False):
if self.done: # avoid proccessing if completed if self.done: # avoid proccessing if completed
@ -62,6 +70,21 @@ class Test_Leak(Test_Test):
if type(v) is float: if type(v) is float:
v = round(v, 2) v = round(v, 2)
l.setText(str(v)) l.setText(str(v))
if d.get("Running test: active phase", None) in {
"WAITING START",
"ATTESA START",
"WAITING THE START OF A NEW TEST"
"FINE TEST",
}:
if self.parent_assembly_widget is not None:
self.parent_assembly_widget().set_text(text=u"PREMERE START PER INIZIARE LA PROVA TENUTA")
self.start_b.setEnabled(True)
self.stop_b.setEnabled(False)
else:
if self.parent_assembly_widget is not None:
self.parent_assembly_widget().set_text(text=u"PROVA TENUTA IN CORSO")
self.start_b.setEnabled(False)
self.stop_b.setEnabled(True)
super().visualize(data) super().visualize(data)
def save_last(self): def save_last(self):

View File

@ -6,14 +6,26 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>980</width> <width>370</width>
<height>704</height> <height>491</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Test Leak</string> <string>Test Leak</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="font"> <property name="font">

View File

@ -1,4 +1,5 @@
import sys import sys
import weakref
from lib.helpers import timing from lib.helpers import timing
from PyQt5.QtCore import Qt, QTimer, pyqtSignal from PyQt5.QtCore import Qt, QTimer, pyqtSignal
@ -40,6 +41,7 @@ class Test_Test(Widget):
self.override_b.clicked.connect(self.override) self.override_b.clicked.connect(self.override)
self.override_b.setEnabled(True) self.override_b.setEnabled(True)
# setup vision staus gui # setup vision staus gui
self.parent_assembly_widget = None
self.status_imgs_full = { self.status_imgs_full = {
True: QPixmap("src/ui/imgs/success.png"), True: QPixmap("src/ui/imgs/success.png"),
"": QPixmap("src/ui/imgs/neo.ico"), "": QPixmap("src/ui/imgs/neo.ico"),
@ -61,6 +63,14 @@ class Test_Test(Widget):
self.status_palettes[""].setColor(QPalette.Base, QColor(255, 255, 0)) self.status_palettes[""].setColor(QPalette.Base, QColor(255, 255, 0))
self.visualize() self.visualize()
def set_parent_assembly_widget(self, parent_assembly_widget=None):
if parent_assembly_widget is None:
self.parent_assembly_widget = None
elif type(parent_assembly_widget) is weakref.ReferenceType:
self.parent_assembly_widget = parent_assembly_widget
else:
self.parent_assembly_widget = weakref.ref(parent_assembly_widget)
def start(self, recipe=None, step=None): def start(self, recipe=None, step=None):
self.admin_challenged = False self.admin_challenged = False
self.recipe = recipe self.recipe = recipe

View File

@ -32,7 +32,7 @@
<property name="title"> <property name="title">
<string>Risultato</string> <string>Risultato</string>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QProgressBar" name="ok_counter_pb"> <widget class="QProgressBar" name="ok_counter_pb">
<property name="sizePolicy"> <property name="sizePolicy">
@ -99,7 +99,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>SALVA IMMAGINE</string> <string>SALVA</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -1,7 +1,9 @@
import sys import sys
from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtGui import QColor, QImage, QPalette, QPixmap
from PyQt5.QtWidgets import QHeaderView, QProgressBar, QTableWidgetItem
from ui.helpers import calc_foreground_color
from ui.test_test import Test_Test from ui.test_test import Test_Test
@ -13,7 +15,17 @@ class Test_Vision(Test_Test):
self.ok_counter_limit = 2 self.ok_counter_limit = 2
else: else:
self.ok_counter_limit = 1 self.ok_counter_limit = 1
# DETECTIONS TABLE
self.results_table_headers = {
"class": "RILEVATO",
"expected": "ATTESO",
"score": "PUNTEGGIO",
}
super().__init__(components=components, recipe=recipe, step=step) super().__init__(components=components, recipe=recipe, step=step)
self.results_tw.setColumnCount(len(self.results_table_headers))
self.results_tw.setHorizontalHeaderLabels(self.results_table_headers.values())
self.results_tw.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.results_tw.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
def start(self, recipe=None, step=None): def start(self, recipe=None, step=None):
super().start(recipe=recipe, step=step) super().start(recipe=recipe, step=step)
@ -27,6 +39,9 @@ class Test_Vision(Test_Test):
self.components["vision"].resume() self.components["vision"].resume()
self.components["galaxy_camera"].resume() self.components["galaxy_camera"].resume()
self.request_frame.emit() # request first frame self.request_frame.emit() # request first frame
# DETECTIONS TABLE
# hide previous results
self.results_tw.setRowCount(0)
def stop(self): def stop(self):
# disable camera-vision loop # disable camera-vision loop
@ -52,12 +67,36 @@ class Test_Vision(Test_Test):
"render": data["vision"].get("render", None), "render": data["vision"].get("render", None),
}], override=override) }], override=override)
@staticmethod
def tr(text):
translations = {
"no_detection": "non_rilevato",
"_": " ",
"big": "grande",
"black": "nero",
"blue": "blu",
"brown": "marrone",
"empty": "vuoto",
"green": "verde",
"ko": "ko",
"ok": "ok",
"orange": "arancione",
"red": "rosso",
"small": "piccolo",
"white": "bianco",
"yellow": "giallo",
}
for s, t in translations.items():
text = text.replace(s, t)
return text
def visualize(self, data=None): def visualize(self, data=None):
if data is None: if data is None:
data = {} data = {}
overridden = data.get("overridden", False) overridden = data.get("overridden", False)
frame = data.get("frame", None) frame = data.get("frame", None)
render = data.get("render", None) render = data.get("render", None)
results = data.get("results", {}).get("results", None)
if overridden: if overridden:
img = self.status_imgs_full["warning"] img = self.status_imgs_full["warning"]
elif render is not None: elif render is not None:
@ -81,12 +120,84 @@ class Test_Vision(Test_Test):
self.components["neo_pixels"].set_all_pixel_color("#ff0000") self.components["neo_pixels"].set_all_pixel_color("#ff0000")
else: else:
self.components["neo_pixels"].set_all_pixel_color("#ffffff") self.components["neo_pixels"].set_all_pixel_color("#ffffff")
# DETECTIONS TABLE
if results is not None:
self.results_tw.setRowCount(len(results))
self.results_tw.setVerticalHeaderLabels(list(results))
for r, [zone_name, result] in enumerate(results.items()):
for c, header in enumerate(self.results_table_headers):
if header == "class":
i = self.results_tw.item(r, c)
if i is None:
i = QTableWidgetItem()
self.results_tw.setItem(r, c, i)
detection = result.get("detection", None)
if detection is not None:
text = self.tr(detection["class"]["name"])
color = QColor(detection["class"]["color"])
else:
text = ""
color = QColor("#ffffff")
i.setText(text)
i.setBackground(color)
i.setForeground(calc_foreground_color(color))
elif header == "expected":
i = self.results_tw.item(r, c)
if i is None:
i = QTableWidgetItem()
self.results_tw.setItem(r, c, i)
expected = result.get("expected", None)
if expected is not None:
text = self.tr(expected["name"])
color = QColor(expected["color"])
else:
text = ""
color = QColor("#ffffff")
i.setText(text)
i.setBackground(color)
i.setForeground(calc_foreground_color(color))
elif header == "score":
w = self.results_tw.cellWidget(r, c)
if w is None:
w = QProgressBar()
w.setRange(0, 100)
self.results_tw.setCellWidget(r, c, w)
score = result.get("detection", {}).get("score", None)
ok = result.get("ok", None)
if score is not None:
value = int(score * 100)
else:
value = 0
w.setValue(value)
if ok is not None:
if ok:
if score is not None:
color = QColor(int(255 - max(score - 0.5, 0) / 0.5 * 255), 255, 0)
else:
color = QColor("#00ff00")
else:
color = QColor("#ff0000")
else:
color = QColor("#0000ff")
p = QPalette()
p.setColor(QPalette.Highlight, color)
w.setPalette(p)
else:
raise NotImplementedError()
else:
self.results_tw.setRowCount(0)
super().visualize(data, img=img) super().visualize(data, img=img)
def save_last(self): def save_last(self):
if self.last is None: if self.last is None:
return return
self.components["vision_saver"].save(self.last["time"], self.last["frame"]) self.components["vision_saver"].save(
save_time=self.last.get("time", None),
frame=self.last.get("frame", None),
vision=self.last.get("detections", None),
suffix="manual_save",
resize=[256, 256],
)
def emit_ok(self): def emit_ok(self):
super().emit_ok() super().emit_ok()

View File

@ -6,15 +6,27 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>618</width> <width>880</width>
<height>835</height> <height>629</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Test Vision</string> <string>Test Vision</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="2" column="0"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="3" column="0">
<widget class="QPushButton" name="override_b"> <widget class="QPushButton" name="override_b">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@ -27,13 +39,32 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="3" column="1">
<widget class="QPushButton" name="save_b">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>SALVA IMMAGINE</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title"> <property name="title">
<string>Risultato</string> <string>Risultato</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="1" column="0">
<widget class="QProgressBar" name="ok_counter_pb"> <widget class="QProgressBar" name="ok_counter_pb">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -49,7 +80,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="1" column="1">
<widget class="QLabel" name="state_l"> <widget class="QLabel" name="state_l">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum"> <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
@ -72,6 +103,58 @@
</widget> </widget>
</item> </item>
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
<widget class="QWidget" name="widget" native="true">
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Visione</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTableWidget" name="results_tw">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="img_l"> <widget class="QLabel" name="img_l">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -81,8 +164,8 @@
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>600</width> <width>612</width>
<height>700</height> <height>512</height>
</size> </size>
</property> </property>
<property name="text"> <property name="text">
@ -90,17 +173,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> </layout>
<widget class="QPushButton" name="save_b">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>SALVA IMMAGINE</string>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -6,14 +6,26 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>174</width> <width>143</width>
<height>91</height> <height>67</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Vision Step Edito</string> <string>Vision Step Edito</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_6"> <widget class="QGroupBox" name="groupBox_6">
<property name="font"> <property name="font">

View File

@ -13,7 +13,6 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Window</string> <string>Window</string>
</property> </property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar"> <widget class="QMenuBar" name="menubar">
<property name="geometry"> <property name="geometry">
<rect> <rect>