Freehand pixel-level annotation
Implementing reading and writing pixel-level annotation is the major feature that the proposed library provides. This allows pathologists to do freehand annotations and image processing algorithms to do image segmentation. The following image shows the result of writing two consecutive overlapping freehand annotations. The program writes two annotations to lower layers and read the result from a higher layer. It shows that the library is able to automatically choose the layer to satisfy the accessing resolution, and pass the changes between different layers.
# Load the created OpenHIL project. slide = libmi.Slide('proj_freehand') slide.LoadProject() # Read two freehand annotations from image files. fig1 = cv2.imread('freehand1.png') fig2 = cv2.imread('freehand2.png') # Get and insert the alpha channel since we need 32-bit images. alpha1 = cv2.bitwise_not(fig1[...,0]) alpha2 = cv2.bitwise_not(fig2[...,1]) fig1 = numpy.insert(fig1, 3, values = alpha1, axis = 2) fig2 = numpy.insert(fig2, 3, values = alpha2, axis = 2)
First we read two freehand shapes from the image files, and extract the alpha channel since we need the mask to write pixel-level annotation. We also extend the image data array from 3 channels to 4 channels since the project pixel size is 4 bytes.
# Write the first annotation from (0, 0) to (2048, 2048). slide.SetCurrentRegion((0, 0), (2048, 2048)) slide.SetRegionSize((512, 512)) slide.WriteSegm(fig1, alpha1) # Write the second annotation from (1024, 1024) to (3072, 3072). slide.SetCurrentRegion((1024, 1024), (2048, 2048)) slide.SetRegionSize((1024, 1024)) slide.WriteSegm(fig2, alpha2)
The SetCurrentRegion function is used to assign the operating region, and the SetRegionSize function is used to assign the image resolution when reading and writing segmentation data. For example, in the first case we write an image with the resolution 512, 512 to the region of the whole-slide image from (0, 0) to (2048, 2048). The library will automatically choose the correct layer in the pyramid image, and scale the image data to fit the assigned region size.
# Read the final annotation as a 512 x 512 image. slide.SetCurrentRegion((0, 0), (3072, 3072)) slide.SetRegionSize((512, 512)) fig3 = slide.ReadSegm() # Reshape the array from int64 array to RGBA. fig3.dtype = numpy.uint8 fig3 = fig3.reshape((512, 512, 4)) # Write the image file and close the project to save data. cv2.imwrite('freehand.png', fig3) slide.SaveProject()
The ReadSegm returns a numpy array with [512, 512] resolution and dtype = uint32 since the pixel size is 4. to visualize the result, we transform the array to [512, 512, 4] with dtype = uint8, and write it to the output image.