module demonstrating the usage of k-means algorithm on a 2d image
More...
module demonstrating the usage of k-means algorithm on a 2d image
Overview
This application classifies the pixels of a given 2d image using K-means algorithm. It also involves, among others, median2dImg, standardizeImg and lawTexture2dImg algorithms
Usage
This is a standalone script file which takes no input argument.
Here is a snapshot of input image used for the following example :
Problem statement
Having a quick look at the input image, the human eye clearly distinguishes 4 distinct types of regions (or classes):
- class #1: the background, dark and textured
- class #2: two bright and textured regions
- class #3: 2 dark and homogeneous regions
- class #4: 1 bright and homogeneous region
However, distinguishing these 4 classes using the k-means algorithm is more complicated than expected: if we simply provide as input of the k-means algorithm the original image, we obtain an image of classes that is far from the expected result (see image below).
image of classes resulting from k-means application using only the image of intensities as input
There are 2 problems with the current input image that prevent us from relying only on the information of intensity:
- means of intensities are equal between class #1 and class #3, and between class #2 and class #4
- standard deviations of intensities in classes #1 and #2 (resp. in classes #3 and #4) are so great that some pixels in class #1 (resp. class #3] have the same intensity that some pixels and class #2 (resp. class #4].
In fact, to do the classification, the human eye implicitly tries to agglomerate neighbouring pixels that share the same properties. In this case, common properties are:
- intensities
- local texture
Using k-means, we can at least rely on the the cunjunction of these 2 properties, provided we feed the algorithm with the appropriate images:
first, extract local texture information using lawTexture2dImg. In our case, the
image seems to give the bests results (see image below, we clearly distinguish the 2 types of textures - classes #1 and #2 on one hand, classes #3 and #4 on the other hand)
R5R5 Law's texture energy map associated to input image
- then, unnoise the input image using the median2dImg filter, to slightly decrease the standard deviation of intensities of each class
- concatenate the unnoised image of intensities with the image resulting from the lawTexture2dImg filter application in a temporal sequence image
- standardize the sequence image, so that the intensity information and the local texture information have the same weight
- pass the standardized sequence image as input of k-means algorithm.
The result is closer from what we can expect, even if it's still not perfect (pixels on the boundary of region associated to class #4 are not correctly classified), but some filters applied as post-process (morphologic opening, for instance) can improve the result.
image of classes resulting from sample application execution
Source code documentation
We start by importing all necessary libraries:
import os
import sys
import PyIPSDK
import PyIPSDK.IPSDKIPLClassification as classif
import PyIPSDK.IPSDKIPLFiltering as filter
import PyIPSDK.IPSDKIPLIntensityTransform as itrans
import PyIPSDK.IPSDKIPLStats as stats
import PyIPSDK.IPSDKIPLUtility as util
Then we define the input parameters.
imagesSamplePath = PyIPSDK.getIPSDKDirectory(PyIPSDK.eInternalDirectory.eID_Images)
inputImgPath = os.path.join(imagesSamplePath, "sampleKMeansInputImage.tif")
tmpPath = PyIPSDK.getIPSDKDefaultDirectory(PyIPSDK.eDefaultExternalDirectory.eDED_Tmp)
outputClassImgPath = os.path.join(tmpPath, "outClassImg.tif")
We load our input image from the associated Tiff file, by calling the function ipsdk::image::file::loadTiffImageFile.
inImg = PyIPSDK.loadTiffImageFile(inputImgPath, PyIPSDK.eTiffDirectoryMode.eTDM_Volume)
We then call the median 2d filter, to unnoise the input image.
unnoisedImg = filter.median2dImg(inImg, 1, 1)
To extract local texture information, we apply Law's texture 2D filter to the input image
preProcParams = PyIPSDK.createMeanLawTexPreProcParams(7)
postProcParams = PyIPSDK.createMeanAbsLawTexPostProcParams(7)
kernelTypes = PyIPSDK.createLawTextureKernel2dTypes()
kernelTypes.flagL5E5_E5L5 = False
kernelTypes.flagL5S5_S5L5 = False
kernelTypes.flagL5R5_R5L5 = False
kernelTypes.flagE5E5 = False
kernelTypes.flagE5S5_S5E5 = False
kernelTypes.flagE5R5_R5E5 = False
kernelTypes.flagS5S5 = False
kernelTypes.flagS5R5_R5S5 = False
lawTexImg = stats.lawTexture2dImg(inImg, kernelTypes, preProcParams, postProcParams)
Next, we concatenate intensities and local texture images into a temporal sequence image
seqImg = util.appendSeqImg(lawTexImg, util.convertImg(unnoisedImg, PyIPSDK.eImageBufferType.eIBT_Real32))
The sequence is then standardized, to give the same weight to intensities and to local texture information
seqImg = itrans.standardizeImg(seqImg)
We are now able to launch k-means classifier on the standardized sequence, to classify our image into 4 classes
outClassImg = PyIPSDK.createImage(inImg.getGeometry())
K = 4
classif.kMeansImg(seqImg, K, 20, 1000, 0.001, outClassImg)
Finally, we save the image resulting from the operation to the Tiff file.
print("Writing result in file : " + outputClassImgPath)
PyIPSDK.saveTiffImageFile(outputClassImgPath, outClassImg)
See the full source listing