# Extract table from image

I have the image of a matrix (download it to see it bigger):

Is there a way to convert this into a Mathematica numerical matrix, using Mathematica?

• Do you know the bounds of the values of your matrix? Are they all 0/1/-1/2? – rm -rf Jan 13 '14 at 20:58
• @rm-rf Yes. You can assume that the values are all in the set {-1, 0, 1, 2}. – becko Jan 13 '14 at 21:00
• ImageCorrelate is probably your friend here. – rm -rf Jan 13 '14 at 21:02
• Various methods are described here here. If you know the dimension of this matrix as well as how the columns are spatially distributed it might help, because a useful first step is probably to partition the image (and then use ImageCorrelate like rm -rf said). – C. E. Jan 13 '14 at 21:12
• @anon You can also assume that you know the dimensions of the matrix. In this example, the matrix is 46x34. The columns and rows are clearly delineated in the image. – becko Jan 13 '14 at 21:19

TextRecognize works fine after some tweaks and error corrections:

x = Import["http://i.stack.imgur.com/NQr6I.png"];
res = TextRecognize[Binarize[ImageResize[x, Scaled[5]], 0.7],"SegmentationMode" -> 6];

m = ToExpression /@ StringSplit[#] & /@
StringSplit[
StringReplace[
res, {"O" | "D" | "U" -> "0", "~" | "\"" -> "-", "I" -> "1"}],
"\n"];

m // MatrixForm


I have used the undocumented option "SegmentationMode" -> 6.

• SegmentationMode details here – Murta Jan 14 '14 at 0:52
• @Murta My last line is clickable with the same link :) – ybeltukov Jan 14 '14 at 0:56
• +1 for proving that TextRecognize works. It seems the text needs to be large enough for it to work. – shrx Jan 14 '14 at 9:01
• +1. But I've found one error: m[[28,28]] gives -1. It should be 1. – becko Jan 14 '14 at 14:32

Here is one way:

data = ColorNegate@Import@"http://i.stack.imgur.com/NQr6I.png";

points=ComponentMeasurements[ MorphologicalComponents[Sharpen[Dilation[Binarize@data,1.5],1]] ,"Centroid"][[All,2]];
box=ComponentMeasurements[ MorphologicalComponents[Sharpen[Dilation[Binarize@data,1.5],1]] ,"BoundingBox"][[All,2]];

{posX,posY}=Mean/@Split[#,If[Abs[#1-#2]<5,True,False]&]&/@{Sort@points[[All,1]],Sort@points[[All,2]]}


We can see that grid position worked in this plot:

ListPlot[points,PlotRange->All,GridLines->{posX,posY},PlotStyle->Red]


Now let's do image partition:

imagePartition = ParallelMap[ImageTrim[Binarize@data, #] &, box];


Here is a sample:

imagePartition[[;; 15]]


Now the part that has to be improved, here is one attempt to recognize the numbers.

getNumber[img_]:=Module[{r,comp},
comp=ComponentMeasurements[img,{"PerimeterCount","Holes"}][[All,2]];
r=Which[
Length@#==2,-1
,#[[1,2]]==1,0
,#[[1,1]]<15,1
,True,2
]&[comp];
(*{r,comp,img}*)
r
]


Two elements finds -1, one hole find 0, no hole with perimeter < 15 finds 1 and the rest is 2.

Applying it data partition as:

numberData=Partition[ParallelMap[getNumber,imagePartition],Length@posX]//MatrixForm


We get:

Grid[numberData,Spacings->0,Alignment-> NumberPoint,Dividers->LightGray,BaseStyle->{FontSize-> 11}]


Not perfect, but can be a start point. It's just improve getNumber.

## Update

With some calibration in getNumber and using Binarize intead of Sharpen, now all cases are ok.

• Related to the last part: coming soon. First application example – Rojo Jan 14 '14 at 2:23
• Yes I know! Tks for show that this documentation is already public :) – Murta Jan 14 '14 at 10:06
• +1 But it has some mistakes. numberData[[6, 5]] and numberData[[6, 6]] both give 0, but should be -1 and 1 respectively. – becko Jan 16 '14 at 18:04