This is almost completely automated except:
I have to specificy where in the image the axes are
I can't quite get TextRecognize
to work on the x-axis ticks.
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;
output = AssociationThread[xTicks -> sorted]
And plotting the result looks pretty similar to the original image:
BarChart[output, PlotRange -> {All, {-0.04, 0.3}},
ChartLabels -> Automatic]