# -*- coding: utf-8 -*-
"""
This module contains all basic masking and pre-processing (as in segmenting phase) methods
"""
from __future__ import division
from __future__ import absolute_import
from builtins import range
import cv2
import numpy as np
from .basic import (findminima, im2shapeFormat, getOtsuThresh, findContours,
isnumpy)
from .filters import smooth
[docs]def brightness(img):
"""
get brightness from an image
:param img: BGR or gray image
:return:
"""
# LESS BRIGHT http://alienryderflex.com/hsp.html
#b,g,r = cv2.split(img.astype("float"))
# return np.sqrt( .299*(b**2) + .587*(g**2) + .114*(r**2)).astype("uint8")
# Normal gray
return im2shapeFormat(img, img.shape[:2])
# HSV
# return cv2.cvtColor(img,cv2.COLOR_BGR2HSV)[:,:,2]
[docs]def background(gray, mask=None, iterations=3):
"""
get the background mask of a gray image. (this it the inverted of :func:`foreground`)
:param gray: gray image
:param mask: (None) input mask to process gray
:param iterations: (3) number of iterations to detect background
with otsu threshold.
:return: output mask
"""
# gray = works with any gray image
if mask is None:
mask = np.ones_like(gray)
for i in range(iterations):
hist, bins = np.histogram(
gray[mask.astype(bool)].flatten(), 256, [0, 256])
thresh = getOtsuThresh(hist)
cv2.threshold(gray, thresh, 1, cv2.THRESH_BINARY_INV, dst=mask)
return mask
[docs]def foreground(gray, mask=None, iterations=3):
"""
get the foreground mask of a gray image. (this it the inverted of :func:`background`)
:param gray: gray image
:param mask: (None) input mask to process gray
:param iterations: (3) number of iterations to detect foreground
with otsu threshold.
:return: output mask
"""
return 1 - background(gray, mask, iterations)
[docs]def multiple_otsu(gray, mask=None, flag=cv2.THRESH_BINARY, iterations=1):
"""
get the mask of a gray image applying Otsu threshold.
:param gray: gray image
:param mask: (None) input mask to process gray
:param iterations: (1) number of iterations to detect Otsu threshold.
:return: thresh, mask
"""
# get mask
if mask is None and flag == cv2.THRESH_BINARY_INV:
mask = np.ones_like(gray)
if mask is None and flag == cv2.THRESH_BINARY:
mask = np.zeros_like(gray)
if iterations > 0:
for i in range(iterations):
hist, bins = np.histogram(
gray[mask.astype(bool)].flatten(), 256, [0, 256])
thresh = getOtsuThresh(hist)
cv2.threshold(gray, thresh, 1, flag, dst=mask)
return thresh, mask
else:
raise Exception(
"iterations must be greater than 0 and got {}".format(iterations))
[docs]def hist_cdf(img, window_len=0, window='hanning'):
"""
Get image histogram and the normalized cumulative distribution function.
:param img: imaeg
:param window_len:
:param window:
:return: histogram (int), normalized cdf (float)
"""
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
if window_len:
hist = smooth(hist, window_len, window) # if window_len=0 => no filter
cdf = hist.cumsum() # cumulative distribution function
cdf_normalized = cdf * (hist.max()) / cdf.max() # normalized cdf
return hist, cdf_normalized
[docs]def thresh_hist(gray):
"""
Get best possible thresh to threshold object from the gray image.
:param gray: gray image.
:return: thresh value.
"""
hist, cdf = hist_cdf(gray, 11)
th1 = 130 # np.min(np.where(cdf.max()*0.2<=cdf))
th2 = np.max(np.where(hist.max() == hist))
th3 = np.min(np.where(np.mean(cdf) <= cdf))
th4 = findminima(hist, np.mean([th1, th2, th3]))
return th4
[docs]def threshold_opening(src, thresh, maxval, type):
"""
Eliminate small objects from threshold.
:param src:
:param thresh:
:param maxval:
:param type:
:return:
"""
kz = np.mean(src.shape) / 50 # proportion to src
kernel = np.ones((kz, kz), np.uint8) # kernel of ones
retval, th = cv2.threshold(src, thresh, maxval, type) # apply threshold
th = cv2.morphologyEx(th, cv2.MORPH_OPEN, kernel) # apply opening
return th
[docs]def biggestCntData(contours):
"""
Gets index and area of biggest contour.
:param contours:
:return: index, area
"""
index, maxarea = 0, 0
for i in range(len(contours)):
area = cv2.contourArea(contours[i])
if area > maxarea:
index, maxarea = i, area
return index, maxarea
[docs]def biggestCnt(contours):
"""
Filters contours to get biggest contour.
:param contours:
:return: cnt
"""
if contours:
return contours[biggestCntData(contours)[0]]
# return empty array if there is not anything to choose
return np.array([])
[docs]def cnt_hist(gray):
"""
Mask of a ellipse enclosing retina using histogram threshold.
:param gray: gray image
:param invert: invert mask
:return: mask
"""
thresh = thresh_hist(gray) # obtain optimum threshold
rough_mask = threshold_opening(gray, thresh, 1, 0)
contours, hierarchy = findContours(
rough_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
return biggestCnt(contours)
[docs]def mask_watershed(BGR, GRAY=None):
"""
Get retinal mask with watershed method.
:param BGR:
:param GRAY:
:return: mask
"""
if GRAY is None:
GRAY = brightness(BGR) # get image brightness
thresh, sure_bg = cv2.threshold(
GRAY, 0, 1, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # obtain over threshold
thresh, sure_fg = cv2.threshold(GRAY, thresh + 10, 1, cv2.THRESH_BINARY)
markers = np.ones_like(GRAY, np.int32) # make background markers
markers[sure_bg == 1] = 0 # mark unknown markers
markers[sure_fg == 1] = 2 # mark sure object markers
cv2.watershed(BGR, markers) # get watershed on markers
retval, mask = cv2.threshold(markers.astype(
"uint8"), 1, 1, cv2.THRESH_BINARY) # get binary image of contour
return mask
[docs]def thresh_biggestCnt(thresh):
"""
From threshold obtain biggest contour.
:param thresh: binary image
:return: cnt
"""
# http://docs.opencv.org/master/d9/d8b/tutorial_py_contours_hierarchy.html#gsc.tab=0
contours, hierarchy = findContours(
thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
return biggestCnt(contours)
[docs]def gethull(contours):
"""
Get convex hull.
:param contours: contours or mask array
:return: cnt
"""
if isnumpy(contours):
contours, _ = findContours(
contours.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
allcontours = np.vstack(contours[i] for i in np.arange(len(contours)))
return cv2.convexHull(allcontours)