finitedepth.coatinglayer#

Analyze coating layer and similarity measures [1] [2].

Module Contents#

Classes#

CoatingLayerBase

Abstract base class for coating layer object.

CoatingLayer

Basic implementation of coating layer without any analysis.

RectLayerShape

Coating layer over rectangular substrate.

Functions#

images_XOR(img1, img2[, point])

Perform subtraction between two images by XOR operation.

images_ANDXOR(img1, img2[, point])

Perform subtraction between two images by AND and XOR operations.

equidistant_interpolate(points, n)

Interpolate points with equidistant new points.

parallel_curve(curve, dist)

Return parallel curve of curve with offset distance dist.

acm(cm)

Compute accumulated cost matrix from local cost matrix.

owp(acm)

Compute optimal warping path from accumulated cost matrix.

integfrechet_G1(T1, T2, sigma)

Approximate integral Fréchet distance over uniform grid [4].

class finitedepth.coatinglayer.CoatingLayerBase(image, substrate, *, tempmatch=None)[source]#

Bases: abc.ABC, Generic[SubstTypeVar, DataTypeVar]

Abstract base class for coating layer object.

Coating layer object stores SubstrateBase object and target image, which is a binary image of coated substrate. The role of coating layer object is to acquire coating layer region by template matching and analyze its shape.

External API can use the following members to get analysis results of concrete subclasses.

Parameters:
  • image (numpy.typing.NDArray[numpy.uint8]) – Binary target image.

  • substrate (SubstTypeVar) – Substrate instance storing binary reference image.

  • tempmatch (tuple[tuple[int, Ellipsis], float] | None) – Pre-computed template matching result. External constructor can pass this argument to force the template matching result. If not passed, match_template() performs matching.

property image: numpy.typing.NDArray[numpy.uint8][source]#

Binary target image.

For immutability, this image is not writable.

Return type:

numpy.typing.NDArray[numpy.uint8]

property substrate: SubstTypeVar[source]#

Substrate instance which contains substrate and reference information.

Return type:

SubstTypeVar

property tempmatch: tuple[tuple[int, Ellipsis], float][source]#

Template matching location and score.

Return type:

tuple[tuple[int, Ellipsis], float]

DataType: type[DataTypeVar][source]#

Return type of analyze.

Concrete subclass must assign this attribute with dataclass type.

match_template(image, template)[source]#

Perform template matching between image and template.

Template matching is performed using cv2.matchTemplate() with cv2.TM_SQDIFF_NORMED. Subclass may override this method to apply other algorithm.

Parameters:
  • image (numpy.typing.NDArray[numpy.uint8]) – Binary target image.

  • template (numpy.typing.NDArray[numpy.uint8]) – Binary template image.

Return type:

tuple[tuple[int, Ellipsis], float]

substrate_point()[source]#

Upper left point of the substrate image in target image.

Returns:

Coordinates in (x, y).

Return type:

numpy.typing.NDArray[numpy.int32]

coated_substrate()[source]#

Coated substrate region.

Returns:

Target image without artifacts, e.g., bath surface.

Return type:

numpy.typing.NDArray[numpy.bool_]

extract_layer()[source]#

Coating layer region extracted from target image.

Return type:

numpy.typing.NDArray[numpy.bool_]

abstract valid()[source]#

Return if the analysis can be performed as expected.

Sometimes, the coating layer instance should be constructed but not analyzed at all. For example, the coating video may contains frames where the capillary bridge is not ruptured yet. This method allows analyzer to skip such instances.

Return type:

bool

abstract analyze()[source]#

Return analysis result as dataclass.

Return type must be DataType.

Return type:

DataTypeVar

abstract draw(*args, **kwargs)[source]#

Return visualization result.

Return type:

numpy.typing.NDArray[numpy.uint8]

class finitedepth.coatinglayer.CoatingLayer(image, substrate, *, tempmatch=None)[source]#

Bases: CoatingLayerBase[finitedepth.substrate.SubstrateBase, CoatingLayerData]

Basic implementation of coating layer without any analysis.

Parameters:
  • image (numpy.typing.NDArray[numpy.uint8]) – Binary target image.

  • substrate (SubstTypeVar) – Substrate instance.

  • tempmatch (tuple[tuple[int, Ellipsis], float] | None) – Pre-computed template matching result.

Examples

Construct substrate instance first.

>>> import cv2
>>> from finitedepth import get_sample_path, Reference, Substrate
>>> img = cv2.imread(get_sample_path("ref.png"), cv2.IMREAD_GRAYSCALE)
>>> _, bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
>>> ref = Reference(bin, (10, 10, 1250, 200), (100, 100, 1200, 500))
>>> subst = Substrate(ref)
>>> import matplotlib.pyplot as plt 
>>> plt.imshow(subst.draw()) 
../../../_images/index-11.png

Then, construct coating layer instance.

>>> from finitedepth import CoatingLayer
>>> img = cv2.imread(get_sample_path("coat.png"), cv2.IMREAD_GRAYSCALE)
>>> _, bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
>>> coat = CoatingLayer(bin, subst)
>>> plt.imshow(coat.draw()) 
../../../_images/index-2.png
DataType[source]#

Return CoatingLayerData.

valid()[source]#

Return true, as analysis is not performed at all.

Return type:

bool

analyze()[source]#

Return empty CoatingLayerData.

draw(subtraction_mode='none', layer_color=(255, 0, 0), layer_thickness=-1)[source]#

Subtract the template match result and paint the coating layer.

Parameters:
  • subtraction_mode ({‘none’, ‘template’, ‘substrate’, ‘full’}) – Subtraction mode. ‘template’ and ‘substrate’ removes overlapping template region and substrate region, respectively. ‘full’ removes both.

  • layer_color (tuple[int, int, int]) – Layer color for cv2.drawContours().

  • layer_thickness (int) – Layer thickness for cv2.drawContours().

Return type:

numpy.typing.NDArray[numpy.uint8]

class finitedepth.coatinglayer.RectLayerShape(image, substrate, opening_ksize, reconstruct_radius, roughness_measure, ifd_gsize=None, *, tempmatch=None)[source]#

Bases: CoatingLayerBase[finitedepth.substrate.RectSubstrate, RectLayerShapeData]

Coating layer over rectangular substrate.

Parameters:
  • image (numpy.typing.NDArray[numpy.uint8]) – Binary target image.

  • substrate (finitedepth.substrate.RectSubstrate) – Substrate instance.

  • opening_ksize (tuple[int, int]) – Kernel size for morphological operation. Elements must be zero or odd number.

  • reconstruct_radius (int) – Radius of the “safe zone” for noise removal. Imaginary circles with this radius are drawn on bottom corners of the substrate. Connected components not passing these circles are regarded as image artifacts.

  • roughness_measure (str) –

    Similarity measure to quantify roughness.

    ’DTW’

    Dynamice time warping.

    ’SDTW’

    Root mean square of dynamic time warping.

    ’IFD’

    Approximated integral Fréchet distance. Requires ifd_gsize.

  • ifd_gsize (float | None) – Grid size to approximate integral Fréchet distance. Ignored if roughness_measure is not ‘IFD’.

  • tempmatch (tuple[tuple[int, Ellipsis], float] | None) – Pre-computed template matching result.

Examples

Construct substrate instance first.

>>> import cv2
>>> from finitedepth import get_sample_path, Reference, RectSubstrate
>>> img = cv2.imread(get_sample_path("ref.png"), cv2.IMREAD_GRAYSCALE)
>>> _, bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
>>> ref = Reference(bin, (10, 10, 1250, 200), (100, 100, 1200, 500))
>>> subst = RectSubstrate(ref, 3.0, 1.0, 0.01)
>>> import matplotlib.pyplot as plt 
>>> plt.imshow(subst.draw()) 
../../../_images/index-3.png

Then, construct coating layer instance.

>>> from finitedepth import RectLayerShape
>>> img = cv2.imread(get_sample_path("coat.png"), cv2.IMREAD_GRAYSCALE)
>>> _, bin = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
>>> coat = RectLayerShape(bin, subst, (1, 1), 50, "IFD", 3)
>>> plt.imshow(
...     coat.draw(conformality_step=10, roughness_step=5)
... ) 
../../../_images/index-4.png
property opening_ksize: tuple[int, int][source]#

Kernel size for morphological operation.

Return type:

tuple[int, int]

property reconstruct_radius: int[source]#

Radius of the “safe zone” for noise removal.

Return type:

int

property roughness_measure: str[source]#

Similarity measure to quantify roughness.

Return type:

str

property ifd_gsize: float | None[source]#

Grid size to quantify roughness if roughness_measure is IFD.

Return type:

float | None

DataType[source]#

Return RectLayerShapeData.

valid[source]#
layer_contours()[source]#

Find contours of coating layer region.

This method finds external contours of extract_layer(). Each contour encloses each discrete region of coating layer.

Return type:

tuple[numpy.typing.NDArray[numpy.int32], Ellipsis]

interfaces()[source]#

Find solid-liquid interfaces.

A substrate can have contact with multiple discrete coating layer regions, and a single coating layer region can have multiple contacts to the substrate. This method returns indices for contour() where solid-liquid interfaces start and stop.

Returns:

tuple of arrays. i-th array represents i-th coating layer region in layer_contours(). Shape of the array is (N, 2), where N is the number of contacts the coating layer region makes. Each column represents starting and ending indices for the interface interval in substrate contour.

Return type:

tuple[numpy.typing.NDArray[numpy.int64], Ellipsis]

Note

Each interval describes continuous patch on the substrate contour covered by the layer. To acquire the interface points, slice substrate’s contour() with the indices.

contour()[source]#

Contour of the entire coated substrate.

Return type:

numpy.typing.NDArray[numpy.int32]

capbridge_broken()[source]#

Check if capillary bridge is ruptured.

As substrate is withdrawn from fluid bath, capillary bridge forms between the coating layer and bulk fluid and then ruptures.

Return type:

bool

extract_layer()[source]#

Coating layer region extracted from target image.

Error pixels are removed by performing morphological operation and removing the disconnected components that are far from the substrate.

Return type:

numpy.typing.NDArray[numpy.bool_]

surface()[source]#

Liquid-gas interface of the coating layer.

Substrate surface exposed to air is considered to be covered by coating layer with zero thickness.

Returns:

Starting and ending indices for the surface interval in coated substrate contour.

Return type:

tuple[numpy.int64, numpy.int64]

Note

To acquire the surface points, slice contour() with the indices.

uniform_layer()[source]#

Imaginary uniform layer.

Uniform layer is a parallel curve [3] of substrate surface which has the same cross-sectional area as the actual coating layer.

Returns:

Thickness and polyline vertices of the uniform layer.

Return type:

tuple[numpy.float64, numpy.typing.NDArray[numpy.float64]]

conformality()[source]#

DTW-based conformality of the coating layer.

Returns:

Conformality between layer surface and substrate surface and its pair of points in curve space.

Return type:

tuple[float, numpy.typing.NDArray[numpy.int32]]

roughness()[source]#

Similarity-based surface roughness of the coating layer.

Returns:

Roughness between layer surface and uniform layer and its pair of points in curve space.

Return type:

tuple[float, numpy.typing.NDArray[numpy.float64]]

max_thickness()[source]#

Regional maximum thicknesses.

Coating layer is segmented using RectSubstrate.sideline_intersections(). Points on layer surface and sideline for maximum distance in each region are found.

Returns:

tuple of two arrays. The first array contains maximum thickness values on left, bottom, and right region. Value of 0 indicates no coating layer on that region. The second array contains points on layer surface and substrate lines for the maximum thickness. Shape of the array is (3, 2, 2); 1st axis indicates left, bottom and right region, 2nd axis indicates layer surface and substrate line, and 3rd axis indicates (x, y) coordinates.

Return type:

tuple[numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.float64]]

analyze()[source]#

Return RectLayerShapeData.

draw(background_mode='image', subtraction_mode='none', layer_color=(255, 0, 0), layer_thickness=-1, contactline_color=(0, 255, 0), contactline_thickness=1, maxthickness_color=(0, 255, 0), maxthickness_thickness=1, uniformlayer_color=(0, 0, 255), uniformlayer_thickness=1, conformality_color=(0, 0, 255), conformality_thickness=1, conformality_step=1, roughness_color=(0, 0, 255), roughness_thickness=1, roughness_step=1)[source]#

Visualize the analysis result.

  1. Draw the substrate with by PaintMode.

  2. Display the template matching result with SubtractionMode.

  3. Draw coating layer and contact line.

  4. If capillary bridge is broken, draw regional maximum thicknesses, uniform layer, conformality pairs and roughness pairs.

Parameters:
  • background_mode ({‘image’, ‘empty’}) – Determine how background is drawn. ‘image’ draws original background image while ‘empty’ draws on empty frame.

  • subtraction_mode ({‘none’, ‘template’, ‘substrate’, ‘full’}) – Subtraction mode. ‘template’ and ‘substrate’ removes overlapping template region and substrate region, respectively. ‘full’ removes both.

  • layer_color (tuple[int, int, int]) – Layer contour’s color for cv2.drawContours().

  • layer_thickness (int) – Layer contour’s thickness for cv2.drawContours().

  • contactline_color (tuple[int, int, int]) – Contact line’s color for cv2.line().

  • contactline_thickness (int) – Contact line’s thickness for cv2.line().

  • maxthickness_color (tuple[int, int, int]) – Regional maximum thickness line’s color for cv2.polylines().

  • maxthickness_thickness (int) – Regional maximum thickness line’s thickness for cv2.polylines().

  • uniformlayer_color (tuple[int, int, int]) – Imaginary uniform layer’s color for cv2.polylines().

  • uniformlayer_thickness (int) – Imaginary uniform layer’s thickness for cv2.polylines().

  • conformality_color (tuple[int, int, int]) – Conformality pairs’ color for cv2.polylines().

  • conformality_thickness (int) – Conformality pairs’ thickness for cv2.polylines().

  • conformality_step (int) – Step size to skip conformality pairs.

  • roughness_color (tuple[int, int, int]) – Roughness pairs’ color for cv2.polylines().

  • roughness_thickness (int) – Roughness pairs’ thickness for cv2.polylines().

  • roughness_step (int) – Step size to skip roughness pairs.

Return type:

numpy.typing.NDArray[numpy.uint8]

finitedepth.coatinglayer.images_XOR(img1, img2, point=(0, 0))[source]#

Perform subtraction between two images by XOR operation.

Parameters:
  • img1 (numpy.typing.NDArray[numpy.bool_]) – Image from which img2 is subtracted.

  • img2 (numpy.typing.NDArray[numpy.bool_]) – Image patch which is subtracted from img1.

  • point (tuple[int, int]) – Location in img1 where img2 is subtracted.

Return type:

numpy.typing.NDArray[numpy.bool_]

finitedepth.coatinglayer.images_ANDXOR(img1, img2, point=(0, 0))[source]#

Perform subtraction between two images by AND and XOR operations.

Parameters:
  • img1 (numpy.typing.NDArray[numpy.bool_]) – Image from which img2 is subtracted.

  • img2 (numpy.typing.NDArray[numpy.bool_]) – Image patch which is subtracted from img1.

  • point (tuple[int, int]) – Location in img1 where img2 is subtracted.

Return type:

numpy.typing.NDArray[numpy.bool_]

finitedepth.coatinglayer.equidistant_interpolate(points, n)[source]#

Interpolate points with equidistant new points.

Parameters:
  • points – Points that are interpolated. The shape must be (N, 1, D) where N is the number of points and D is the dimension.

  • n – Number of new points.

Returns:

Interpolated points. If N is positive number, the shape is (n, 1, D). If N is zero, the shape is (n, 0, D).

Return type:

numpy.typing.NDArray[numpy.float64]

finitedepth.coatinglayer.parallel_curve(curve, dist)[source]#

Return parallel curve of curve with offset distance dist.

Parameters:
  • curve (numpy.typing.NDArray) – Vertices of a polyline. The shape is (V, 1, D), where V is the number of vertices and D is the dimension.

  • dist (float) – offset distance of the parallel curve.

Returns:

Round-joint parallel curve of shape (V, 1, D).

Return type:

numpy.typing.NDArray

finitedepth.coatinglayer.acm(cm)[source]#

Compute accumulated cost matrix from local cost matrix.

Parameters:

cm (numpy.typing.NDArray[numpy.float64]) – Local cost matrix.

Returns:

Accumulated cost matrix. The element at [-1, -1] is the total sum along the optimal path. If cm is empty, return value is an empty array.

Return type:

numpy.typing.NDArray[numpy.float64]

finitedepth.coatinglayer.owp(acm)[source]#

Compute optimal warping path from accumulated cost matrix.

Parameters:

acm (numpy.typing.NDArray[numpy.float64]) – Accumulated cost matrix.

Returns:

Indices for the two series to get the optimal warping path.

Return type:

numpy.typing.NDArray[numpy.int32]

finitedepth.coatinglayer.integfrechet_G1(T1, T2, sigma)[source]#

Approximate integral Fréchet distance over uniform grid [4].

Parameters:
  • T1 (numpy.typing.NDArray) – Polyline vertices. shape=(nodes, 2).

  • T2 (numpy.typing.NDArray) – Polyline vertices. shape=(nodes, 2).

  • sigma (float) – Grid size.

Returns:

Approximated integral Fréchet distance between two polygonal curves, its optimal path in parameter space, and its point pairs in curve space.

Return type:

tuple[float, numpy.typing.NDArray[numpy.int32], numpy.typing.NDArray[numpy.float64]]