# import libraries here import math import cv2 import numpy as np def count_blood_cells(image_path): """ Procedura prima putanju do fotografije i vraca broj crvenih krvnih zrnaca, belih krvnih zrnaca i informaciju da li pacijent ima leukemiju ili ne, na osnovu odnosa broja krvnih zrnaca Ova procedura se poziva automatski iz main procedure i taj deo kod nije potrebno menjati niti implementirati. :param image_path: Putanja do ulazne fotografije. :return: Broj prebrojanih crvenih krvnih zrnaca, broj prebrojanih belih krvnih zrnaca, da li pacijent ima leukemniju (True ili False) """ red_blood_cell_count = 0 white_blood_cell_count = 0 has_leukemia = None # Ucitavanje slike img_bgr = cv2.imread(image_path) # TODO - Prebrojati crvena i bela krvna zrnca i vratiti njihov broj kao povratnu vrednost ove procedure white_blood_cell_count, wbc_contours, wbc_radius = count_white_cells(img_bgr) red_blood_cell_count = count_red_cells(img_bgr, wbc_contours, wbc_radius) # TODO - Odrediti da li na osnovu broja krvnih zrnaca pacijent ima leukemiju i vratiti True/False kao povratnu vrednost ove procedure has_leukemia = (white_blood_cell_count or 0) / (red_blood_cell_count or 1) > 0.085 return red_blood_cell_count, white_blood_cell_count, has_leukemia def count_white_cells(img_bgr): # Binary image from HSV img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV) min_blue = (50, 50, 20) max_blue = (145, 255, 255) img_hsw_filtered = cv2.inRange(img_hsv, min_blue, max_blue) # Binary image from threshold img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) _, img_thresh_filtered = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY) img_thresh_filtered = cv2.bitwise_not(img_thresh_filtered) # Concatenate both images img = cv2.bitwise_or(img_hsw_filtered, img_thresh_filtered) # Fill holes kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) _, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours_by_size = sorted(contours, key=cv2.contourArea, reverse=True) wbc_contours = [] contours_to_remove = [] min_area = 0 for c in contours_by_size: area = cv2.contourArea(c) if area > min_area: wbc_contours.append(c) min_area = area / 3 else: contours_to_remove.append(c) # Remove small contours for c in contours_to_remove: cv2.drawContours(img, [c], -1, 0, -1) if not wbc_contours: return 0, None, 0 # Assume that last item in wbc_contours is a single cell wbc_area = cv2.contourArea(wbc_contours[-1]) radius = np.round(2 * math.sqrt(wbc_area / math.pi)) min_radius = np.int(radius * 0.3) max_radius = np.int(radius * 1.3) img_edges = cv2.Canny(img, 75, 200) circles = cv2.HoughCircles(img_edges, cv2.HOUGH_GRADIENT, 1.3, radius, param1=200, param2=30, minRadius=min_radius, maxRadius=max_radius) wbc_count = 0 if circles is not None: circles = np.round(circles[0, :]).astype("int") circles = sorted(circles, key=lambda x: x[2], reverse=True) biggest_radius = circles[0][2] for (x, y, r) in circles: for c in wbc_contours: # If center is in contour or near contour closes_contour_point = cv2.pointPolygonTest(c, (x, y), True) if closes_contour_point >= 0 or -closes_contour_point < min_radius: # cv2.circle(img_bgr, (x, y), r, (0, 255, 0), 2) # cv2.rectangle(img_bgr, (x - 2, y - 2), (x + 2, y + 2), (0, 128, 255), -1) wbc_count += np.int(biggest_radius / r) break print(wbc_count) return wbc_count, wbc_contours, radius def count_red_cells(img_bgr, wbc_contours, wbc_radius): img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) img = cv2.medianBlur(img_gray, 5) img = cv2.GaussianBlur(img, (5, 5), 0) img = cv2.equalizeHist(img) img = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 41, 5) img = cv2.bitwise_not(img) # Remove wbc countours for c in wbc_contours: cv2.drawContours(img, [c], -1, 0, -1) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) _, contours, _ = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) for c in contours: cv2.drawContours(img, [c], 0, 255, -1) _, contours, _ = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) rbc_radii = [] for c in contours: if len(c) >= 5: ellipse = cv2.fitEllipse(c) widthE = ellipse[1][0] heightE = ellipse[1][1] if widthE > heightE: coef = heightE / widthE else: coef = widthE / heightE if coef > 0.8: (x, y), r = cv2.minEnclosingCircle(c) rbc_radii.append(r) min_radius = 0 similar_radii = [] for r in sorted(rbc_radii, reverse=True): if r > min_radius: min_radius = r * 0.8 similar_radii.append(r) radius = np.average(similar_radii) min_radius = np.int(radius * 0.6) # Detect rbc_contours = [] rbc_count = 0 for c in contours: (x, y), r = cv2.minEnclosingCircle(c) if r > min_radius: cv2.circle(img_bgr, (int(x), int(y)), int(r), (0, 255, 0), 2) rbc_contours.append(c) rbc_count += int(r / min_radius) return len(rbc_contours)