I have such images

I want to draw a red line in every line content like this:

If I accumulate the pixel value for every line, I can get the red curve line:

black = ImageRotate[ColorNegate[Import["https://i.stack.imgur.com/skpSZ.png"]]];
data = Plus @@ ImageData[black];
Show[black, ListLinePlot[data, PlotStyle -> Red]]

I think the position of the yellow arrow is what I want to find... But how to find it? Of course, I know the number of the line must be 10 in every column. Because the digits are always from 0 to 9. And I have another such images to do such thing..





Not as succinct as I'd hoped, but here it is:

highlight[img_] := Module[
  {binary, pixelsPerRow, clusters, diffs, occupied, empty, intervals, 
   means, imgw, imgh},
  padded = ImagePad[img, 1];
  binary = Binarize[ColorNegate[padded]];
  pixelsPerRow = Total[ImageData[binary], {2}];
  clusters = FindClusters[
    pixelsPerRow -> Range@Length[pixelsPerRow], 5,
    Method -> "KMeans"
  diffs = Differences[First[clusters]];
  occupied = DeleteCases[diffs, 1];
  empty = SequenceCases[diffs, {seq : 1 ..} :> Length[{seq}]];
  intervals = Partition[Accumulate@Riffle[empty, occupied], 2];
  means = Pick[Mean /@ intervals, EuclideanDistance[##] > 15 & @@@ intervals];
  {imgw, imgh} = ImageDimensions[img];
  HighlightImage[img, InfiniteLine[{0, imgh - # + 1}, {1, 0}] & /@ means]

The basic idea is that rows where there is no text always have the same, or almost the same, number of white pixels. They also have fewer pixels than any other rows. This means that if we cluster the pixel counts per row then the first cluster will consist of the empty rows. We can then use those row numbers to figure out where to put the red lines.

There is a magic number, 15, in this algorithm. Without any correction, this algorithm would have drawn red lines through horizontal white lines because they aren't empty. What the number 15 denotes is that if non-empty sequences of rows are shorter than fifteen rows then they shouldn't be highlighted with red lines.


img1 = Import["https://i.stack.imgur.com/aJbkm.png"];
img2 = Import["https://i.stack.imgur.com/hetHU.png"];
img3 = Import["https://i.stack.imgur.com/7CU49.png"];

GraphicsColumn[highlight /@ {img1, img2, img3}, ImageSize -> 400]


  • $\begingroup$ How about this? $\endgroup$ – yode Oct 14 '18 at 6:19
  • $\begingroup$ @yode Doesn't it work, or what? $\endgroup$ – C. E. Oct 14 '18 at 6:24
  • $\begingroup$ Yes, I will get this result $\endgroup$ – yode Oct 14 '18 at 6:26
  • $\begingroup$ @yode ok, I see. It was easily fixed by padding the image. I've updated the function. $\endgroup$ – C. E. Oct 14 '18 at 6:46
  • $\begingroup$ Then img2 will have an extra red line... $\endgroup$ – yode Oct 14 '18 at 7:10

Just a start to try,

img = Import["https://i.stack.imgur.com/hoCAX.png"]

whiteCount = Total /@ ImageData[Binarize@img];
median = Median[whiteCount];
tmp = SplitBy[whiteCount, Sign[# - median] &];
pos = Pick[Accumulate[Length /@ tmp] - 1/2 Length /@ tmp, 
   Length[#] > 5 && #[[1]] < median & /@ tmp];
dims = ImageDimensions[img];
 Line[{{1, dims[[2]] - #}, {dims[[1]], dims[[2]] - #}} & /@ pos]]

But this code cannot success to use in other images.. Because I have use the median as a threshold here...


Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.