The below image is of the pattern of cracks in the surface of Johannes Vermeer's Girl with a pearl earring, captured using a method called "grazing incidence illumination" (the light is shined at the painting from a very large angle). There has been some simple image processing to reveal just the cracks. You'll notice the region of her face has very different crack statistics (density, average length of crack, etc.) than outside her face.
I'd like to develop a Mathematica tool to extract image data so I can compute statistics of such so-called "craquelure" in paintings such as this:
- histogram of crack length (distances between crack intersections), i.e., lengths of each "path" in a graph
- orientations of each crack ($0 \to \pi$)
- overall average density of cracks (number of cracks per unit area)
and so forth. Calculating and plotting and histogramming such data is not the problem; extracting the raw data is.
The first step is to create a mathematical graph of the image data. Define the above image as craquelureImage
, and then compute:
paintingGraph= MorphologicalGraph@
ColorReplace[craquelureImage, {White -> Black, Black -> White}]
This gives a graph with several thousand vertices:
I suspect there is a problem here, though, because when I highlight the vertices that have degree $2$, there are only a handful, despite the appearance of long "paths."
One can inspect the connectivity (edges), vertices, their location, etc., using common software functions, just as with any traditional graph.
But this graph is not quite appropriate for full analysis. It contains paths (linear sequences of edges) that are really just a single edge. Thus we need to perform what graph theorists call "path contraction"--replace each such linear path with a single edge.
In a separate question on this site, @kglr wrote very clever code that performs such path contraction, and retains the spatial location of the un-contracted vertices. That code works well on graphs generated by traditional code.
However, for some unknown reason it does not work on paintingGraph
, defined above. It somehow scrambles the locations of vertices or mis-assigns edges. I have examined the VertexList
, EdgeList
, and so on of the underlying graph, and cannot determine why it does not work: I get the layout to be completely mixed up:
Using @kglr's code I get:
Hence my first problem:
Problem 1: How to make the path contraction code work on paintingGraph
. I can only presume there is something special about a graph created by MorphologicalGraph
that isn't obvious.
Assuming that problem is solved, and the spatial locations of the vertices are proper...
Problem 2: How does one extract from the graph the spatial lengths and orientations of each crack segment?
For reasons of material physics, cracks (in paintings, in dry mud, ...) will always meet at vertices of degree three. That is, they appear like Ts (at some orientation). Again for fundamental physical reasons involving sequential stress relief in the drying paint, the angles of the Ts are almost exactly $90^\circ$. Can we demonstrate that? We can easily extract vertices in the graph of degree $3$. Specifically, at these degree-three vertices....
Problem 3: How do we calculate the relative angle of the intersection at each T?
vCoords = AssociationThread[VertexList[#], GraphEmbedding[#]] &;
and changingVertexCoordinates -> {v_ :> GraphEmbedding[g][[v]]}
toVertexCoordinates -> {v_ :> vCoords[g]@v}
? $\endgroup$