# 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: <String> Putanja do ulazne fotografije.
:return: <int> Broj prebrojanih crvenih krvnih zrnaca,
<int> broj prebrojanih belih krvnih zrnaca,
<bool> 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)