Skip to main content

Is there a way to convert an image into a Graph?


I'm trying to convert an image with several overlapping dots into a Graph. The goal is to be able to derive the Kirchhoff matrix for the randomly created "network of resistors" a.k.a dots with the function KirchhoffMatrix[]. Any thoughts or ideas?


Here's the image I'm trying to extract the graph from: enter image description here


Haha, sorry I suppose I have been pretty vague with this question. I am currently researching percolation simulations. The dots in the image represent metallic particles. The dots the overlap represent metallic particles connected via a resistor. We randomize the dots on the screen, group the ones that are touching with MorphologicalComponents[] // Colorize , and then we Isolate the 'BackBone Cluster' a.k.a the largest component with DeleteSmallComponents[]. We check to see if the cluster spans all the way from the top to the bottom and if it does, the sample percolated. We are then trying to find the conductance of the sample from the BackBone cluster. We have been using the Finite Element Method for this, which requires the KirchhoffMatrix[].


Your answer, Vitaliy Kaurov, looks like it will very thoroughly answer my question. I'm going to test things before I accept it but thank you for putting so much time into it!!



Answer



==== Method 1 ===


Here is a way to get a graph from an image. MorphologicalGraph can get you started.


img = Import["http://i.stack.imgur.com/9HXZ5.png"];

g = MorphologicalGraph[img]

enter image description here


And here is your KirchhoffMatrix of the graph. Please note that MatrixPlot averages values for the best visual representation, - actual plot would be too detailed to be a good representation.


m = KirchhoffMatrix[g]; MatrixPlot[m]

enter image description here


A good guess is that your graph, due to its random nature, not all entirely connected. This maybe important in your case, because with no connection you have no conductance. Here is the way to find all connected components, highlight them and compute their own KirchhoffMatrix.


cc = ConnectedComponents[g];
index = Length /@ ConnectedComponents[g];

ctrl = Sort[MapThread[Rule, {cc, index}], #1[[2]] > #2[[2]] &];
Manipulate[
GraphicsRow[{HighlightGraph[g, Subgraph[g, n],
GraphStyle -> "LargeNetwork"],
MatrixPlot[KirchhoffMatrix[Subgraph[g, n]],
ColorFunction -> "DarkBands"]},
ImageSize -> 600], {{n, ctrl[[1, 1]], "Vertex number"}, ctrl},
FrameMargins -> 0]

enter image description here



Let's do some math now. Here is a famous property:



The number of times 0 appears as an eigenvalue in the KirchhoffMatrix is the number of connected components in the graph.



The number of connected comments (separate sub-graphs) is found as


 ConnectedComponents[g] // Length


159




And as you can see it is equal to the number of zero eigenvalues :


 ev = Chop[Eigenvalues[N@m]]; Count[ev, 0]


159



Isn't it great to see programming and math matching up perfectly?


Here is the way to list all connected sub-graphs separately. Mouse-over will give KirchhoffMatrix.


Tooltip[#, 
MatrixPlot[KirchhoffMatrix[Subgraph[g, #]],

ColorFunction -> "DarkBands",
ImageSize -> 200]] & /@ (Subgraph[g, #, ImageSize -> 80,
VertexSize -> 0] & /@ ctrl[[All, 1]])

enter image description here


Note some extra image processing before MorphologicalGraph would result in different graphs. For example finding first SkeletonTransform or DistanceTransform would change the game:


GraphicsRow[{DistanceTransform[Dilation[img, 3]] // ImageAdjust,
SkeletonTransform[img]}, ImageSize -> 600]

enter image description here



So if you now apply MorphologicalGraph function to these images you of course will get different graphs as result. This maybe useful because there is no exact guarantee that MorphologicalGraph will give you always what you need in all cases, especially with low-res images. Below is an example of possible issue - with real question being where exactly is the boundary between these two cases?


Grid@{{r = .6; 
img = ColorNegate@
Graphics[{Disk[{0, 0}, r], Disk[{.5, .9}, r], Disk[{1, 0}, r]}],
MorphologicalGraph[img, VertexSize -> .1]},
{r = 1.2;
img = ColorNegate@
Graphics[{Disk[{0, 0}, r], Disk[{.5, .9}, r], Disk[{1, 0}, r]}],
MorphologicalGraph[img, VertexSize -> .1]}}


enter image description here


So this is just the word of caution - you may need some image pre-processing before you get your MorphologicalGraph. Or you may use something else - see method below.


==== Method 2 ===


This is just an outline, not complete solution:


1) This question and answers by @Verbeia and @Szabolcs and Fred Simons on MathGroup are a starting point


2) They work only if you know center and radius of every point in your image - which you do NOT


3) You can get center and radius of every point by using for example ComponentMeasurements function. Then you can apply methods by @Verbeia and @Szabolcs and Fred Simons on MathGroup.



  • Example 1: This Wolfram Blog gives code for finding what you need even in the case of overlapping particles, like this (image - courtesy of Wolfram Research):



enter image description here



enter image description here


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

equation solving - Invert and fit implicitly defined curve

I need to fit an implicitly defined curve. I thought I could get some data out of Solve , and then using FindFit . Therefore, I would like to find the relation the parametric curve defined by $F(x,y)=0$: Solve[-(1/2) + 1/2 (0.41202 BesselK[0, 0.1 Sqrt[x^2 + y^2]] + (0.101483 x BesselK[1, 0.1 Sqrt[x^2 + y^2]])/Sqrt[x^2 + y^2]) == 0, y] But I can't get an output: Solve was unable to solve the system with inexact coefficients or the system obtained by direct rationalization of inexact numbers present in the system. Since many of the methods used by Solve require exact input, providing Solve with an exact version of the system may help. >> Edit: In particular, I would like to fit the data coming from the curve with the expression of another curve, and not with a function $f(x)$. In particular, since this clearly looks like a cardioid , I would like it to fit to something like it. What other strategies could I try?

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...