Mathematical MorphologyΒΆ

This file is meant to demonstrate the use of gstlearn for Morphological Operations

In [1]:
import numpy as np
import pandas as pd
import sys
import os
import gstlearn as gl
import gstlearn.plot as gp
import gstlearn.document as gdoc

gdoc.setNoScroll()

Setting some global variables

In [2]:
# Set the Global Options
verbose = True
flagGraphic = True

# Define the Space Dimension
ndim = 2
gl.defineDefaultSpace(gl.ESpaceType.RN, ndim)

# Set the Seed for the Random Number generator
gl.law_set_random_seed(5584)
In [3]:
nx = ny = 100
nxy = [nx,ny]
ntotal = nx * ny
dx = dy = 0.01

In several usages, we will need a VectorDouble dimensionned to the total number of pixels. This is created next.

In [4]:
localVD = gl.VectorDouble(ntotal)

Generating an initial square grid covering a 1 by 1 surface (100 meshes along each direction).

In [5]:
grid = gl.DbGrid.create(nxy, [dx,dy])
model = gl.Model.createFromParam(gl.ECov.CUBIC, 0.1, 1.)
gl.simtub(None, grid, model)
if verbose:
    grid.display()
if flagGraphic:
    gp.plot(grid,"Simu")
    gp.decoration(title="Initial Data Set")
Data Base Grid Characteristics
==============================

Data Base Summary
-----------------
File is organized as a regular grid
Space dimension              = 2
Number of Columns            = 4
Total number of samples      = 10000

Grid characteristics:
---------------------
Origin :      0.000     0.000
Mesh   :      0.010     0.010
Number :        100       100

Variables
---------
Column = 0 - Name = rank - Locator = NA
Column = 1 - Name = x1 - Locator = x1
Column = 2 - Name = x2 - Locator = x2
Column = 3 - Name = Simu - Locator = z1
No description has been provided for this image

Basic operationsΒΆ

We retreive the newly simulated variable (called Simu) from the grid Db into a local Vector (called tab). This vector is then transformed by thresholding and loaded into an image object (called image). This object is very efficient as each pixel is stored into a single bit. A secondary Image object (called image2) is created and will be used in subsequent diadic operations.

In [6]:
vmin = -1
vmax = +1
image2 = gl.BImage(nxy)
tab = grid.getColumn("Simu")
image = gl.morpho_double2image(nxy,tab,vmin,vmax)
In [7]:
volume = gl.morpho_count(image)
print("Grain Volume =",volume, " /",ntotal,"(pixels)\n")
Grain Volume = 6920  / 10000 (pixels)

For visualization (and i the current version), we must first convert the image into a vector and load it into a grid.

In [8]:
if flagGraphic:
    gl.morpho_image2double(image, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Initial Image",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

The next step interchanges grain and pore

In [9]:
gl.morpho_negation(image, image2)

if flagGraphic:
    gl.morpho_image2double(image2, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Negative Image",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

Basic Mophological Image transformationsΒΆ

We start with the initial image and perform an erosion. The second argument defines the type of structuring element: either Cross (0) or Block (1)

In [10]:
gl.morpho_erosion(0, [1,1], image, image2)

if flagGraphic:
    gl.morpho_image2double(image2, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Erosion - Cross",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

We check the result of ersosion when choosing the Block structuring element

In [11]:
gl.morpho_erosion(1, [1,1], image, image2)

if flagGraphic:
    gl.morpho_image2double(image2, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Erosion - Block",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

We now perform the dilation of the Initial image (only the Cross structuring element will be used in the next paragraphs)

In [12]:
gl.morpho_dilation(0, [1,1], image, image2)

if flagGraphic:
    gl.morpho_image2double(image2, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Dilation - Cross",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

Combining the elementary operations (Erosion and Dilation), we can perform directly an opening

In [13]:
gl.morpho_opening(0, [1,1], image, image2)

if flagGraphic:
    gl.morpho_image2double(image2, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Opening - Cross",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

And the closing

In [14]:
gl.morpho_closing(0, [1,1], image, image2)

if flagGraphic:
    gl.morpho_image2double(image2, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Closing - Cross",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

Connected componentsΒΆ

Starting from the Initial image (stored in the Data Base as Reference), we now wish to determine the connected components

In [15]:
gl.morpho_negation(image, image)

if flagGraphic:
    gl.morpho_image2double(image, 0, 1., 0., localVD)
    iuid = grid.addColumns(localVD,"Reference",gl.ELoc.Z)
    gp.plot(grid)
    gp.decoration(title="Starting image used for Connected Components")
No description has been provided for this image

Compute the connected components

In [16]:
cc = gl.morpho_labelling(0, 0, image, np.nan)

if flagGraphic:
    iuid = grid.addColumns(cc,"Connect Components by Rank",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image
In [17]:
cc = gl.morpho_labelling(0, 1, image, np.nan)

if flagGraphic:
    iuid = grid.addColumns(cc,"Connect Components by Volume",gl.ELoc.Z)
    res = gp.plot(grid)
No description has been provided for this image

Some shortcutsΒΆ

The procedure is made easier for basic morphological operations, using directly the method morpho. This method operates from a variable stored in a Db (organized as a Grid) and returns the result in the same Db.

In [18]:
grid.setLocator("Reference",gl.ELoc.Z)
dum = grid.morpho(gl.EMorpho.NEGATION,0.5,1.5,verbose=False)

if flagGraphic:
    res = gp.plot(grid)
No description has been provided for this image
In [19]:
grid.setLocator("Reference",gl.ELoc.Z)
dum = grid.morpho(gl.EMorpho.CC,0.5, 1.5)

if flagGraphic:
    gp.plot(grid)
    gp.decoration(title="Connected Components by Rank")
No description has been provided for this image
In [20]:
grid.setLocator("Reference",gl.ELoc.Z)
dum = grid.morpho(gl.EMorpho.CCSIZE,0.5,1.5)

if flagGraphic:
    gp.plot(grid)
    gp.decoration(title="Connected Components by Volume")
No description has been provided for this image

Calculation of distance to the edge of the grain

In [21]:
grid.setLocator("Simu",gl.ELoc.Z)
dum = grid.morpho(gl.EMorpho.DISTANCE,radius=[1,1],verbose=False)

if flagGraphic:
    gp.plot(grid, "*DISTANCE")
    gp.decoration(title="Distance to the Edge")
No description has been provided for this image

Calculation of the 2-D angle of the gradient

In [22]:
grid.setLocator("Simu",gl.ELoc.Z)
dum = grid.morpho(gl.EMorpho.ANGLE,radius=[1,1],verbose=False)

if flagGraphic:
    gp.plot(grid,"*ANGLE")
    gp.decoration(title="Angle of Gradient")
No description has been provided for this image

Calculation of Gradient components

In [23]:
grid.setLocator("Simu",gl.ELoc.Z)
dum = grid.morpho(gl.EMorpho.GRADIENT)

if flagGraphic:
    fig, axs = gp.init(1, 2, figsize=(10,5))
    axs[0].raster(grid,"Morpho.Simu.GRADIENT.1")
    axs[0].decoration(title="Gradient along X")
    axs[1].raster(grid,"Morpho.Simu.GRADIENT.2")
    axs[1].decoration(title="Gradient along Y")
No description has been provided for this image

Smoothing the input image

In [24]:
grid.setLocator("Simu",gl.ELoc.Z,0,True)
neighI = gl.NeighImage([3,3])
dum = grid.smooth(neighI)

if flagGraphic:
    gp.plot(grid,"Smooth*")
    gp.decoration(title="Smoothed Image")
No description has been provided for this image

Testing the Bitmap Image printoutΒΆ

In [25]:
nx = 10
ny = 12
nxy = [nx,ny]
localVD = gl.VectorDouble(nx * ny)
grid = gl.DbGrid.create(nxy)
model = gl.Model.createFromParam(gl.ECov.CUBIC, 6, 1.)
gl.simtub(None, grid, model)
if flagGraphic:
    gp.plot(grid)
    gp.decoration(title="Initial Data Set")
No description has been provided for this image
In [26]:
vmin = -1
vmax = +1
image2 = gl.BImage(nxy)
tab = grid.getColumn("Simu")
image = gl.morpho_double2image(nxy,tab,vmin,vmax)

gl.morpho_image2double(image, 0, 1., 0., localVD)
iuid = grid.addColumns(localVD,"Initial Image",gl.ELoc.Z)
res = gp.plot(grid)
No description has been provided for this image
In [27]:
image
Out[27]:
Array dimension = 2
- Dimension #1 : 10
- Dimension #2 : 12

  1234567890
1 1110010011
2 1110011011
3 1110011111
4 1110011111
5 1110001111
6 1111001111
7 1111111110
8 1111111100
9 1110111110
0 0110011110
1 0110000111
2 0011011111
In [28]:
bstrfmt = gl.BImageStringFormat('+','.',[2,3])
image.display(bstrfmt)
Array dimension = 2
- Dimension #1 : 10
- Dimension #2 : 12

  34567890
4 .++.....
5 .+++....
6 ..++....
7 .......+
8 ......++
9 .+.....+
0 .++....+
1 .++++...
2 ..+.....