# Obtaining the numerical value of barcharts from a plot in an image

I am struggling to obtain the numerical values {x,y} of the barcharts in this plot. Ideally with an automated routine.

Time ago, I developed a routine to do it in the case of points on the plot (eg the typical outcome of a ListPlot command). But it does not work with barcharts. Furthermore, another challenge is that ,here, you do not really see the tickmarks on the x- and y- axis.

• One non-mma solution is to use WebPlotDigitizer , although it makes it more difficult that there are no axes nor any ticks
– ydd
Commented Sep 5, 2023 at 15:17

This is almost completely automated except:

1. I have to specificy where in the image the axes are

2. I can't quite get TextRecognize to work on the x-axis ticks.

3. The zero-height bar at x=0 has to be added manually.

First grab the y-axis centroids

img = Import["https://i.sstatic.net/mcL93.jpg"];
yAxis = ImageTake[img, {1, 422}, {1, 150}]


Then find text in the image. Mma sometimes thinks the E is a euro symbol, and sometimes thinks a dot (.) is a comma (,), so I just replace those in case they happen

numStr =
StringReplace[
TextRecognize[yAxis, RecognitionPrior -> "Column"], {"," -> ".",
"€" -> "E"}];
numStr = StringReplace[numStr, "E" -> "* 10 ^"];
numStr = StringSplit[numStr, "\n\n"];
nums = ToExpression /@ numStr
(*{0.3, 0.2, 0.1, 0.}*)


Then find the "Centroid" of each y-axis label. Blurring the image helps here:

yAxisBin = yAxis // Binarize // ColorNegate;
yAxisBin = Blur[yAxisBin, 2]
tickHeights = (ComponentMeasurements[mp, "Centroid"] // Values)[[All,
2]]
(*{315.338, 230.458, 146.684, 62.1389}*)


Fit a linear model to the y-tick positions and the y-tick values to get the y-value as a function of vertical pixel coordinate. Also grab the fitted slope as it will be useful later:

yAxVal = LinearModelFit[Thread[{tickHeights, nums}], x, x]
slopeY = yAxVal[[1, 2, 2]]

(*FittedModel[-0.0736902+0.00118571 x]*)
(*0.00118571*)


Unfortunately, I could not get TextRecognize to work with the x-axis, so we will have to hardcode those values in:

xTicks = Range[0, 90, 10];


Now grab the actual bars from the image:

bars = ImageTake[img, {250, 300}, {150, 422}] // Binarize //
ColorNegate


Get their bounding boxes, and calculate the pixel height of each bar:

bounds = ComponentMeasurements[bars, "BoundingBox"] // Values;
heights = Last[Flatten[Differences[#]]] & /@ bounds
(*{29., 27., 21., 21., 20., 18., 18., 17., 8.}*)


Note that the bars in ComponentMeasurements are not in left-to-right order, so we need to get the correct ordering for them (by using their horizontal centroid coordinates), and permute the height list to the correct order:

posBars = (ComponentMeasurements[bars, "Centroid"] // Values)[[All,
1]];
barIndexes = Ordering@Ordering[posBars];
sorted = Permute[heights, barIndexes];


The bar at x=0 has height 0 and isn't in the plot so I have to manually add a 0 to the start of sorted. Then we will convert the bar pixel heights to y-axis values by using the fitted slope:

PrependTo[sorted, 0];
sorted *= slopeY;

BarChart[output, PlotRange -> {All, {-0.04, 0.3}},