IPSDK  4_1_0_2
IPSDK : Image Processing Software Development Kit

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 :

inputImage.png

Problem statement

Having a quick look at the input image, the human eye clearly distinguishes 4 distinct types of regions (or classes):

taggedInputImage.png

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).

kmeans_intensityOnly.png
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:

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:

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:

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.

kmeans.png
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 # for classif.kMeansImg
import PyIPSDK.IPSDKIPLFiltering as filter # for filter.median2dImg
import PyIPSDK.IPSDKIPLIntensityTransform as itrans # for itrans.standardizeImg
import PyIPSDK.IPSDKIPLStats as stats # for stats.lawTexture2dImg
import PyIPSDK.IPSDKIPLUtility as util # for util.convertImg and util.appendSeqImg

Then we define the input parameters.

# define input image path
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.

# opening of 2D input image
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)
# here, only R5R5 Law's texture energy map interests us, so we reset all flags but R5R5 one (remember, all flags are set to True by default)
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: number of expected classes
K = 4
classif.kMeansImg(seqImg, K, 20, 1000, 0.001, outClassImg)

Finally, we save the image resulting from the operation to the Tiff file.

# save generated image
print("Writing result in file : " + outputClassImgPath)
PyIPSDK.saveTiffImageFile(outputClassImgPath, outClassImg)

See the full source listing