def test_empty_selem(): # check that min, max and mean returns zeros if structuring element is # empty image = np.zeros((5, 5), dtype=np.uint16) out = np.zeros_like(image) mask = np.ones_like(image, dtype=np.uint8) res = np.zeros_like(image) image[2, 2] = 255 image[2, 3] = 128 image[1, 2] = 16 elem = np.array([[0, 0, 0], [0, 0, 0]], dtype=np.uint8) rank.mean(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(res, out) rank.minimum(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(res, out) rank.maximum(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(res, out)
def test_smallest_selem16(): # check that min, max and mean returns identity if structuring element # contains only central pixel image = np.zeros((5, 5), dtype=np.uint16) out = np.zeros_like(image) mask = np.ones_like(image, dtype=np.uint8) image[2, 2] = 255 image[2, 3] = 128 image[1, 2] = 16 elem = np.array([[1]], dtype=np.uint8) rank.mean(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(image, out) rank.minimum(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(image, out) rank.maximum(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(image, out)
def test_compare_with_cmorph_erode(): # compare the result of maximum filter with erode image = (np.random.rand(100, 100) * 256).astype(np.uint8) out = np.empty_like(image) mask = np.ones(image.shape, dtype=np.uint8) for r in range(1, 20, 1): elem = np.ones((r, r), dtype=np.uint8) rank.minimum(image=image, selem=elem, out=out, mask=mask) cm = cmorph._erode(image=image, selem=elem) assert_array_equal(out, cm)
def test_compare_with_cmorph_erode(): # compare the result of maximum filter with erode image = (np.random.random((100, 100)) * 256).astype(np.uint8) out = np.empty_like(image) mask = np.ones(image.shape, dtype=np.uint8) for r in range(1, 20, 1): elem = np.ones((r, r), dtype=np.uint8) rank.minimum(image=image, selem=elem, out=out, mask=mask) cm = cmorph.erode(image=image, selem=elem) assert_array_equal(out, cm)
def test_percentile_min(): # check that percentile p0 = 0 is identical to local min img = data.camera() img16 = img.astype(np.uint16) selem = disk(15) # check for 8bit img_p0 = rank.percentile(img, selem=selem, p0=0) img_min = rank.minimum(img, selem=selem) assert_array_equal(img_p0, img_min) # check for 16bit img_p0 = rank.percentile(img16, selem=selem, p0=0) img_min = rank.minimum(img16, selem=selem) assert_array_equal(img_p0, img_min)
def substract_background(data, radius, background='mean', show=0): dmin, dmax = data.min(), data.max() tmp = ((data - dmin) / (dmax - dmin) * 255).astype(np.uint8) lforeground = rank.minimum(tmp, disk(radius // 2)) if background == 'mean': lbackground = rank.mean(lforeground, disk(radius * 4)).astype(int) elif background == 'median': lbackground = rank.median(lforeground, disk(radius * 4)).astype(int) elif background == 'maximum': lbackground = rank.maximum(lforeground, disk(radius * 4)).astype(int) else: lbackground = rank.modal(lforeground, disk(radius * 4)).astype(int) rdata = (lforeground - lbackground) if show > 2: fig, axes = plt.subplots(ncols=3, nrows=1, figsize=(30, 10)) for ax, im, title in zip(axes, [lforeground, lbackground, rdata], ['Foreground', 'Background', 'Substraction']): ax.imshow(im, 'optiona') ax.set_xticks([]) ax.set_yticks([]) ax.set_title(title) plt.show() return rdata
def test_16bit(): image = np.zeros((21, 21), dtype=np.uint16) selem = np.ones((3, 3), dtype=np.uint8) for bitdepth in range(17): value = 2**bitdepth - 1 image[10, 10] = value assert rank.minimum(image, selem)[10, 10] == 0 assert rank.maximum(image, selem)[10, 10] == value assert rank.mean(image, selem)[10, 10] == int(value / selem.size)
def test_16bit(): image = np.zeros((21, 21), dtype=np.uint16) selem = np.ones((3, 3), dtype=np.uint8) for bitdepth in range(17): value = 2 ** bitdepth - 1 image[10, 10] = value assert rank.minimum(image, selem)[10, 10] == 0 assert rank.maximum(image, selem)[10, 10] == value assert rank.mean(image, selem)[10, 10] == int(value / selem.size)
def run(self, workspace): image = workspace.image_set.get_image(self.image_name.value) nuclei_image = image.pixel_data[:,:] image_collection = [] # #Get the global Threshold with Otsu algorithm and smooth nuclei image # # nuclei_smoothed = self.smooth_image(image_collection[3][0], image.mask, 1) global_threshold_nuclei = otsu(nuclei_image, min_threshold=0, max_threshold=1) print "the threshold compute by the Otsu algorythm is %f" % global_threshold_nuclei # #Binary thee "DAPI" Image (Nuclei) and labelelize the nuclei # binary_nuclei = (nuclei_image >= global_threshold_nuclei) labeled_nuclei, object_count = scipy.ndimage.label(binary_nuclei, np.ones((3,3), bool)) print "the image got %d detected" % object_count # #Fill the hole and delete object witch touch the border. #labeled_nuclei is modify after the function #Filter small object and split object # labeled_nuclei = fill_labeled_holes(labeled_nuclei) labeled_nuclei = self.filter_on_border(labeled_nuclei) labeled_nuclei = self.filter_on_size(labeled_nuclei, object_count) labeled_nuclei = self.split_object(labeled_nuclei) # #Edge detection of nuclei image and object are more separated # labeled_nuclei_canny = skf.sobel(labeled_nuclei) labeled_nuclei[labeled_nuclei_canny > 0] = 0 labeled_nuclei = skr.minimum(labeled_nuclei.astype(np.uint16), skm.disk(3)) image_collection.append((nuclei_image, "Original")) image_collection.append((labeled_nuclei, "Labelized image")) workspace.display_data.image_collection = image_collection # #Create a new object which will be add to the workspace # objects = cellprofiler.objects.Objects() objects.segmented = labeled_nuclei objects.parent_image = nuclei_image workspace.object_set.add_objects(objects, self.object_name.value)
def test_empty_selem(): # check that min, max and mean returns zeros if structuring element is empty image = np.zeros((5, 5), dtype=np.uint16) out = np.zeros_like(image) mask = np.ones_like(image, dtype=np.uint8) res = np.zeros_like(image) image[2, 2] = 255 image[2, 3] = 128 image[1, 2] = 16 elem = np.array([[0, 0, 0], [0, 0, 0]], dtype=np.uint8) rank.mean(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(res, out) rank.minimum(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(res, out) rank.maximum(image=image, selem=elem, out=out, mask=mask, shift_x=0, shift_y=0) assert_array_equal(res, out)
.. note:: `skimage.dilate` and `skimage.erode` are equivalent filters (see below for comparison). Here is an example of the classical morphological gray-level filters: opening, closing and morphological gradient. """ from skimage.filter.rank import maximum, minimum, gradient noisy_image = img_as_ubyte(data.camera()) closing = maximum(minimum(noisy_image, disk(5)), disk(5)) opening = minimum(maximum(noisy_image, disk(5)), disk(5)) grad = gradient(noisy_image, disk(5)) # display results fig = plt.figure(figsize=[10, 7]) plt.subplot(2, 2, 1) plt.imshow(noisy_image, cmap=plt.cm.gray) plt.title('Original') plt.axis('off') plt.subplot(2, 2, 2) plt.imshow(closing, cmap=plt.cm.gray) plt.title('Gray-level closing') plt.axis('off')
.. note:: `skimage.dilate` and `skimage.erode` are equivalent filters (see below for comparison). Here is an example of the classical morphological greylevel filters: opening, closing and morphological gradient. """ from skimage.filter.rank import maximum, minimum, gradient ima = data.camera() closing = maximum(minimum(ima, disk(5)), disk(5)) opening = minimum(maximum(ima, disk(5)), disk(5)) grad = gradient(ima, disk(5)) # display results fig = plt.figure(figsize=[10, 7]) plt.subplot(2, 2, 1) plt.imshow(ima, cmap=plt.cm.gray) plt.xlabel('original') plt.subplot(2, 2, 2) plt.imshow(closing, cmap=plt.cm.gray) plt.xlabel('greylevel closing') plt.subplot(2, 2, 3) plt.imshow(opening, cmap=plt.cm.gray) plt.xlabel('greylevel opening') plt.subplot(2, 2, 4)
def processFile(f): # Handle DICOM if isdicom(f): ds = dicom.read_file(f) fname = f.rsplit('.', 1)[0]+'.tif' # make a tiff file under the same name to read from pil_dcm = get_dicom_PIL(ds) pil_dcm.save(fname) else: fname = f # Set output file name fname_out = f.rsplit('.', 1)[0]+"-out.jpg" # Open the image file for processing print "File to process: "+fname origimg = cv2.imread(fname, cv2.CV_LOAD_IMAGE_GRAYSCALE) # Chop off the top of the image b/c there is often noncontributory artifact & make numpy arrays img = origimg[25:,:] imarray = np.array(img) imarraymarkup = imarray maskarray = np.zeros_like(imarray) contoursarray = np.zeros_like(imarray) onesarray = np.ones_like(imarray) # Store dimensions for subsequent calculcations max_imheight = maskarray.shape[0] max_imwidth = maskarray.shape[1] if DEBUG: print max_imwidth, max_imheight # Choose the minimum in the entire array as the threshold value b/c some mammograms have > 0 background which screws up the contour finding if based on zero or some arbitrary number ret,thresh = cv2.threshold(imarray,np.amin(imarray),255,cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) biggest_contour = [] for n, contour in enumerate(contours): if len(contour) > len(biggest_contour): biggest_contour = contour # Get the lower most extent of the contour (biggest y-value) max_vals = np.argmax(biggest_contour, axis = 0) min_vals = np.argmin(biggest_contour, axis = 0) #print max_vals[0,1] bc_max_y = biggest_contour[max_vals[0,1],0,1] # get the biggest contour max y bc_min_y = biggest_contour[min_vals[0,1],0,1] # get the biggest contour min y #print "Biggest Contour Max Y:" #print bc_max_y #print "Biggest Contour Min Y:" #print bc_min_y cv2.drawContours(contoursarray,biggest_contour,-1,(255,255,255),15) # Calculate R/L sidedness using centroid M = cv2.moments(biggest_contour) cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) right_side = cx > max_imwidth/2 # Plot the center of mass cv2.circle(contoursarray,(cx,cy),100,[255,0,255],-1) # Approximate the breast epsilon = 0.001*cv2.arcLength(biggest_contour,True) approx = cv2.approxPolyDP(biggest_contour,epsilon,True) # Calculate the hull and convexity defects drawhull = cv2.convexHull(approx) #cv2.drawContours(contoursarray,drawhull,-1,(0,255,0),60) hull = cv2.convexHull(approx, returnPoints = False) defects = cv2.convexityDefects(approx,hull) # Plot the defects and find the most superior. Note: I think the superior and inferior ones have to be kept separate # Also make sure that these are one beyond a reasonable distance from the centroid (arbitrarily cdist_factor = 80%) to make sure that nipple-related defects don't interfere supdef_y = maskarray.shape[0] supdef_tuple = [] cdist_factor = 0.80 if defects is not None: for i in range(defects.shape[0]): s,e,f,d = defects[i,0] far = tuple(approx[f][0]) if far[1] < (cy*cdist_factor) and far[1] < supdef_y: supdef_y = far[1] supdef_tuple = far cv2.circle(contoursarray,far,50,[255,0,255],-1) # Find lower defect if there is one # Considering adding if a lower one is at least greater than 1/2 the distance between the centroid and the lower most border of the contour (see IMGS_MLO/IM4010.tif) infdef_y = 0 infdef_tuple = [] if defects is not None: for i in range(defects.shape[0]): s,e,f,d = defects[i,0] far = tuple(approx[f][0]) if far[1] > infdef_y and supdef_tuple: # cy + 3/4*(bc_max_y - cy) = (bc_max_y + cy)/2 if (right_side and far[0] > supdef_tuple[0]) or (not right_side and far[0] < supdef_tuple[0]): infdef_y = far[1] infdef_tuple = far cv2.circle(contoursarray,far,50,[255,0,255],-1) # Try cropping contour beyond certain index; get indices of supdef/infdef tuples, and truncate vector beyond those indices cropped_contour = biggest_contour[:,:,:] if supdef_tuple: sup_idx = [i for i, v in enumerate(biggest_contour[:,0,:]) if v[0] == supdef_tuple[0] and v[1] == supdef_tuple[1]] if sup_idx: if right_side: cropped_contour = cropped_contour[sup_idx[0]:,:,:] else: cropped_contour = cropped_contour[:sup_idx[0],:,:] if infdef_tuple: inf_idx = [i for i, v in enumerate(cropped_contour[:,0,:]) if v[0] == infdef_tuple[0] and v[1] == infdef_tuple[1]] if inf_idx: if right_side: cropped_contour = cropped_contour[:inf_idx[0],:,:] else: cropped_contour = cropped_contour[inf_idx[0]:,:,:] if right_side: cropped_contour = cropped_contour[cropped_contour[:,0,1] != 1] else: cropped_contour = cropped_contour[cropped_contour[:,0,0] != 1] # Draw the cropped contour #cv2.drawContours(imarraymarkup,cropped_contour,-1,(255,255,0),30) #cv2.drawContours(imarraymarkup,biggest_contour,-1,(255,0,0),30) # Fill in the cropped polygon to mask #cv2.fillPoly(maskarray, pts = [cropped_contour], color=(255,255,255)) cv2.fillPoly(maskarray, pts = [cropped_contour], color=(255,255,255)) #maskarray = ~np.all(maskarray == 0, axis=1)a #print maskarray #maskarray[~np.all(maskarray == 0, axis=2)] # Threshold the cropped version #ret_otsu,thresh_otsu = cv2.threshold(imarray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) # Multiply original image to the mask to get the cropped image imarray2 = imarray + onesarray imcrop_orig = cv2.bitwise_and(imarray2, maskarray) #cv2.drawContours(maskarray,biggest_contour,-1,(255,0,0),3) rankmin_out = rank.minimum(imcrop_orig, disk(20)) thresh = mahotas.otsu(rankmin_out, ignore_zeros = True) # Draw thick black contour to eliminate the skin and nipple from the image cv2.drawContours(rankmin_out,cropped_contour,-1,(0,0,0),255) # #cv2.drawContours(maskarray,cropped_contour,-1,(0,0,0),255) # # Apply the thresholding to generate a new matrix and convert to int type otsubool_out = rankmin_out > thresh otsubinary_out = otsubool_out.astype('uint8') otsuint_out = otsubinary_out * 255 # Crop out the fibroglandular tissue #print output2.shape, imarray2.shape, np.amax(output2), np.amax(imarray2), np.amax(maskarray) imcrop_fgt = cv2.bitwise_and(imarray2, otsuint_out) # both arrays are uint8 type segmented = maskarray > 0 segmented = segmented.astype(int) segmented_sum = segmented.sum() otsubinary_sum = otsubinary_out.sum() density = (otsubinary_sum*100/segmented_sum).astype(int) if density < 25: dcat = 'Fatty' elif density < 50: dcat = 'Scattered' elif density < 75: dcat = 'Heterogenous' else: dcat = 'Extremely Dense' if right_side: side = 'Right' else: side = 'Left' if bc_min_y > 1: view = 'CC' else: view = 'MLO' avg = (imcrop_fgt.sum()/otsubinary_sum).astype(int) print side, view, otsubinary_sum, segmented_sum, density, dcat, avg, avg/np.amax(imcrop_fgt), np.amax(imcrop_fgt) # Create pil images pil1 = Image.fromarray(imarray2) pil2 = Image.fromarray(imcrop_fgt) pil3 = Image.fromarray(contoursarray) pil4 = Image.fromarray(maskarray) # Pasting images above to a pil background along with text. There's a lot of particular measurements sizing the fonts & pictures so that everything fits. It's somewhat arbitrary with lots of trial and error, but basically everything is based off the resized width of the first image. Images needed to be resized down b/c they were too high resolution for canvas. rf = 2 # rf = resize factor w1,h1 = pil1.size pil1_sm = pil1.resize((w1/rf,h1/rf)) w1_sm,h1_sm = pil1_sm.size pil2_sm = pil2.resize((w1_sm,h1_sm)) pil3_sm = pil3.resize((w1_sm,h1_sm)) pil4_sm = pil4.resize((w1_sm,h1_sm)) pil_backdrop = Image.new('L', (100+2*w1_sm,2*h1_sm+h1_sm/2), "white") pil_backdrop.paste(pil1_sm, (0,h1_sm/8)) pil_backdrop.paste(pil2_sm, (100+w1_sm,h1_sm/8)) pil_backdrop.paste(pil3_sm, (0,h1_sm/4+h1_sm)) pil_backdrop.paste(pil4_sm, (100+w1_sm,h1_sm/4+h1_sm)) font = ImageFont.truetype(FONT_PATH+"Arial.ttf",w1_sm/18) draw = ImageDraw.Draw(pil_backdrop) draw.text((0,0),"Original Image",0,font=font) draw.text((100+w1_sm,0),"Fibroglandular Tissue",0,font=font) draw.text((0,h1_sm+h1_sm/6),"Breast Contouring",0,font=font) draw.text((100+w1_sm,h1_sm+h1_sm/6),"Breast Segmentation",0,font=font) pil_backdrop.save(fname_out) return density, dcat, side, view