Skip to content

Commit

Permalink
intial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
harrylal committed Nov 26, 2023
1 parent d9c4fc1 commit 4cf39ea
Show file tree
Hide file tree
Showing 48 changed files with 221 additions and 0 deletions.
Binary file added assets/fault_icon.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ok.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_07.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_08.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_09.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_17.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_18.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_19.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_20.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/results/output_bottle_crate_21.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions config/setting.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
CANNY_THRESHOLD: 66
FEATHER:
X: 0.075
Y: 0.075
HOUGHCIRCLES:
THRESHOLD: 200
PARAM1: 50
PARAM2: 22
MIN_RADIUS: 0
MAX_RADIUS: 0
IMAGE:
HEIGHT: 512
WIDTH: 640
CRATE:
ROWS: 4
COLS: 5
TEMPLATE:
IMAGE: assets/ok.png
WIDTH: 100 # resizes template 100x100
HEIGHT: 100
MATCHING:
ERROR_THRESHOLD: 40 # 0-100 percentage

Binary file added data/bottle_crate_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/bottle_crate_02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/bottle_crate_03.png
Binary file added data/bottle_crate_04.png
Binary file added data/bottle_crate_05.png
Binary file added data/bottle_crate_06.png
Binary file added data/bottle_crate_07.png
Binary file added data/bottle_crate_08.png
Binary file added data/bottle_crate_09.png
Binary file added data/bottle_crate_10.png
Binary file added data/bottle_crate_11.png
Binary file added data/bottle_crate_12.png
Binary file added data/bottle_crate_13.png
Binary file added data/bottle_crate_14.png
Binary file added data/bottle_crate_15.png
Binary file added data/bottle_crate_16.png
Binary file added data/bottle_crate_17.png
Binary file added data/bottle_crate_18.png
Binary file added data/bottle_crate_19.png
Binary file added data/bottle_crate_20.png
Binary file added data/bottle_crate_21.png
24 changes: 24 additions & 0 deletions inspect_crate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import cv2
import sys
from utils.crate_analysis import CrateQualityAnalyzer


if __name__ == "__main__":

quality_analyzer = CrateQualityAnalyzer("config/setting.yaml")

if len(sys.argv) != 2:
print("Usage: python inspect_crate.py <image_path>")

else:
image_path = sys.argv[1]
image = cv2.imread(image_path)

crate_quality, img_results = quality_analyzer.inspect(image)

print(f"Crate quality: {crate_quality}")

while cv2.waitKey(1) != 27:
cv2.imshow("Inspection Results", img_results)
cv2.imshow("image", image)

Binary file added utils/__pycache__/crate_analysis.cpython-310.pyc
Binary file not shown.
174 changes: 174 additions & 0 deletions utils/crate_analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import cv2
import numpy as np
import yaml
from skimage.metrics import structural_similarity as ssim

class CrateQualityAnalyzer():
""" CrateQualityAnalyzer"""

HEALTH_OK = "OK"
HEALTH_BAD = "FAULTY"

def __init__(self, path_settings):
""" CrateQualityAnalyzer Constructor """
self.settings = self.load_settings(path_settings)

self.ok_case_template = cv2.imread(self.settings["TEMPLATE"]["IMAGE"], 0)
self.ok_case_template = cv2.resize(self.ok_case_template, (self.settings["TEMPLATE"]["WIDTH"], self.settings["TEMPLATE"]["HEIGHT"]))

self.fault_icon = cv2.imread("assets/fault_icon.jpg")
self.fault_icon = cv2.resize(self.fault_icon, (self.settings["TEMPLATE"]["WIDTH"], self.settings["TEMPLATE"]["HEIGHT"]))

def load_settings(self, path_settings):
try:
with open(path_settings, "r") as f:
settings = yaml.load(f, Loader=yaml.FullLoader)
except FileNotFoundError as err:
raise FileNotFoundError("Settings file not found") from err

return settings


def preprocess(self, image):
""" Preprocess image for analysis """

proc_img = image.copy()
proc_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

return proc_img

def apply_feather(self, rectangle):
""" Apply feather to rectangle """
x, y, w, h = rectangle
feather_x = int(self.settings["FEATHER"]["X"] * w)
feather_y = int(self.settings["FEATHER"]["Y"] * h)
return x + feather_x, y + feather_y, w - 2 * feather_x, h - 2 * feather_y


def get_slots(self, gray_image):
slots = []
img = gray_image.copy()

edges = cv2.Canny(img, self.settings["CANNY_THRESHOLD"], 255)

contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
all_contours = np.concatenate(contours)

crate_bb = cv2.boundingRect(all_contours)
crate_x, crate_y, crate_w, crate_h = self.apply_feather(crate_bb)
cell_width = crate_w // self.settings["CRATE"]["COLS"]
cell_height = crate_h // self.settings["CRATE"]["ROWS"]

for x in range(crate_x, crate_x + crate_w - cell_width + 1, cell_width):
for y in range(crate_y, crate_y + crate_h - cell_height + 1, cell_height):
pt1 = (x, y)
pt2 = (x + cell_width, y)
pt3 = (x + cell_width, y + cell_height)
pt4 = (x, y + cell_height)
slots.append((pt1, pt2, pt3, pt4))

return slots

def annotate_quality_results(self, image, quality_report):
""" Annotate image with quality results """

img = image.copy()
overlay_mask = np.zeros((img.shape[0], img.shape[1], 3), np.uint8)


crate_quality = self.assess_crate_health(quality_report)


for slot_id, (slot, health) in quality_report.items():
center = (int((slot[0][0] + slot[2][0]) / 2), int((slot[0][1] + slot[2][1]) / 2))
if health == self.HEALTH_BAD and crate_quality == self.HEALTH_BAD:
icon_mapped = cv2.resize(self.fault_icon, (slot[2][0] - slot[0][0], slot[2][1] - slot[0][1]))
overlay_mask[slot[0][1]:slot[2][1], slot[0][0]:slot[2][0]] = icon_mapped
elif health == self.HEALTH_OK and crate_quality == self.HEALTH_OK:
cv2.drawMarker(overlay_mask, center, (0, 255, 0), cv2.MARKER_CROSS, 20, 1)

if(crate_quality == self.HEALTH_BAD):
img = cv2.addWeighted(overlay_mask, 0.5, img, 1 - 0.5, 0, img)
cv2.putText(img, "BAD !", (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
else:
img = cv2.addWeighted(overlay_mask, 0.5, img, 1 - 0.5, 0, img)
cv2.putText(img, "OK !", ( 10, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
return img

def detect_circle(self, cropped_img):

img = cropped_img.copy()
img = cv2.threshold(img, self.settings["HOUGHCIRCLES"]["THRESHOLD"], 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

img = cv2.medianBlur(img, 5)

circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 20, param1=self.settings["HOUGHCIRCLES"]["PARAM1"], param2=self.settings["HOUGHCIRCLES"]["PARAM2"], minRadius=self.settings["HOUGHCIRCLES"]["MIN_RADIUS"], maxRadius=self.settings["HOUGHCIRCLES"]["MAX_RADIUS"])
largest_circle = None

if circles is not None:
circles = np.uint16(np.around(circles))
for circle in circles[0]:
if largest_circle is None or circle[2] > largest_circle[2]:
largest_circle = circle

# returns None if no circle is found
return largest_circle


def assess_slot_health(self, slot, image):
""" Check if slot is good via template matching"""

mask = np.zeros_like(image)
cv2.fillPoly(mask, np.array([list(slot)]), (255,255,255))
slot_img = cv2.bitwise_and(image, mask)

slot_img_cropped = slot_img[slot[0][1]:slot[2][1], slot[0][0]:slot[2][0]]
slot_img_cropped = cv2.resize(slot_img_cropped, (self.ok_case_template.shape[1], self.ok_case_template.shape[0]))


possible_bottle_mouth = self.detect_circle(slot_img_cropped)
template_bottle_mouth = self.detect_circle(self.ok_case_template)

if possible_bottle_mouth is not None and template_bottle_mouth is not None:
percentage_error = 100 * abs(int(possible_bottle_mouth[2]) - int(template_bottle_mouth[2])) / (0.5 * (abs(int(template_bottle_mouth[2])) + abs(int(possible_bottle_mouth[2]))))
if percentage_error <= self.settings["MATCHING"]["ERROR_THRESHOLD"]:
return self.HEALTH_OK

return self.HEALTH_BAD


def assess_crate_health(self, quality_report):
""" Check if crate is good via slot health"""

for _ , health in quality_report.values():
if health == self.HEALTH_BAD:
return self.HEALTH_BAD

return self.HEALTH_OK

def inspect(self, image):
""" Inspect crate for quality """
quality_report = {}
org_img = image.copy()
org_img = cv2.resize(org_img,
(self.settings["IMAGE"]["WIDTH"],
self.settings["IMAGE"]["HEIGHT"]))

# preprocess image to grayscale
processed_img = self.preprocess(org_img)

# get slots [(p1, p2, p3, p4)]
slots = self.get_slots(processed_img)

for slot_id, slot in enumerate(slots):
slot_quality = self.assess_slot_health(slot,processed_img)
quality_report[slot_id] = [slot, slot_quality]

crate_quality = self.assess_crate_health(quality_report)

annotated_img = self.annotate_quality_results(org_img, quality_report)


return crate_quality, annotated_img


0 comments on commit 4cf39ea

Please sign in to comment.