I am currently working on a medical dataset containing whole slide images (~300 images).
These images are pretty big (.tif, average of 7k by 7k pixels). I am currently dividing each of these images into patches of size 224x224 and getting rid of the images showing just the background. Here is a code I quickly made for that using the patchify module I found.
import cv2
from patchify import patchify
import numpy as np
img = cv2.imread('path/to/image.tif')
patches = patchify(img, (224,224,3), step=(224,224,3))
def is_blank(img, threashold=1):
return np.std(img)<threshold
filtered_patches = []
for k in range(patches.shape[0]):
for n in range(patches.shape[1]):
for m in range(patches.shape[2]):
# print(np.std(patches[k][n][m]))
if not is_blank(patches[k][n][m]):
filtered_patches.append(patches[k][n][m])
I was wondering if there was a more efficient/optimized way of first divinding each image in patches and then flagging the background images (which are almost completely white).
These images also come with color-annotated images (see the link below) that I am also going to divide into patches and I was wondering how I could detect which colors (RGB) are in a given image, because these colors correspond to a type of tissue. link to the whole-slide image and the color-annotated image
Here is the dataset : https://espace.library.uq.edu.au/view/UQ:8be4bd0
Colors can be detected from the annotated image using OpenCV. If HSV color are preferred then uncomment line containing img_hsv and pass that image as arg to contour_center_colors or contour_avg_colors.
Half the image is used since in the used sample the annotated part was at the right.
Further filtering can be applied if tissue colors are known (see below).
import cv2 as cv
import random as rng
rng.seed(12345)
def contour_avg_colors(image, contours):
for contour in contours:
x, y, w, h = cv.boundingRect(contour)
roi = image[y:y+h, x:x+w]
# average color within the ROI (Region Of Interest)
average_color_per_row = cv.mean(roi)
average_color = (average_color_per_row[0], average_color_per_row[1], average_color_per_row[2])
print(f"Average color for contour at ({x},{y}): {average_color}")
def contour_center_colors(image, contours):
for i, contour in enumerate(contours):
# color at center of the contour
M = cv.moments(contour)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
color_at_center = image[cy, cx]
print(f"contour {i:>2} color: {color_at_center}")
def thresh_callback(im, val, orig_img):
threshold = val
# Detect edges using Canny
canny_output = cv.Canny(src_gray, threshold, threshold * 2)
# Find contours
contours, hierarchy = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
contour_center_colors(orig_img, contours)
for i in range(len(contours)):
#color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
color = (252,226,5)
cv.drawContours(im, contours, i, color, 2, cv.LINE_8, hierarchy, 0)
# Show in a window
cv.imshow('Contours', im)
img = cv.imread('/home/lmc/tmp/8MxYtM8T.png')
#img_hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
h, w = img.shape[:2] # We only need height and width for splitting
#half width
width= w // 2
# right_part
src = img[:, width:]
# Convert image to gray and blur it
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
src_gray = cv.blur(src_gray, (3,3))
max_thresh = 5000
thresh = 50
thresh_callback(src, thresh, img)
if cv.waitKey(0) & 0xFF == ord('q'):
cv.destroyAllWindows()
contour 0 color: [242 221 241]
contour 1 color: [234 193 217]
contour 3 color: [224 148 216]
contour 4 color: [216 115 203]
contour 5 color: [245 223 245]
...
Regions of Interest (roi) or patches as named by OP, can be obtained by crafting the coordinates
roi = image[y:y+h, x:x+w]
Contours draw with a single color over the color image to identify opened contours and other irregularities.

Using HSV image color, get the list of colors above, pick a specific color and get contours
def get_center(contour):
M = cv.moments(contour)
area = M['m00']
if area != 0:
cx = int(M["m10"] / area)
cy = int(M["m01"] / area)
else:
cx = contour[0][0][0]
cy = contour[0][0][1]
return cx, cy, area
upper_red= np.array([125, 43, 157], dtype = "uint8")
mask = cv.inRange(orig_img, upper_red, upper_red)
detected_output = cv.bitwise_and(orig_img, orig_img, mask = mask)
contours1, hierarch1 = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
for cnt in contours1:
cx, cy, area = get_center(cnt)
if area > 0.0:
centroid_color = detected_output[cy, cx]
if np.array_equal(upper_red, centroid_color):
print(f"Centroid: ({cx},{cy}), area: {area}")
cv.putText(detected_output, str(area), (cx, cy), cv.FONT_HERSHEY_SIMPLEX, 0.45, (0,255,0), 1, cv.LINE_AA)
# Draw contours on the original image (optional)
cv.drawContours(detected_output, contours1, -1, (0, 255, 0), 2)
#center1 = get_center(contours[i])
cv.imshow("red color detection", detected_output)
Centroid: (385,401), area: 361618.5
Centroid: (703,325), area: 2.0
Centroid: (267,248), area: 2.0
Centroid: (180,210), area: 10156.0
Centroid: (374,93), area: 2.0

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With