4

Bug introduced in 11.0 and persisting through 11.3


From this answer, I doubt the capability to work on single character. So I give some test to verify this possibility. You can get my test imgs by this code

imgs = Binarize[
    Import[#]] & /@ {"https://i.stack.imgur.com/PvuFe.png", 
   "https://i.stack.imgur.com/bXHyv.png", 
   "https://i.stack.imgur.com/6Uxpo.png"};

Note the TextRecognize[#, "Character"] & /@ imgs will get nothing. We can get a example from the documentation in Examples/Applications, that indicate the appropriate mask maybe can improve the performance to get a character, but I don't very like this method. Because it is hard to build a mask for characters "i","j" like

TextRecognize[#, 
   Masking -> MorphologicalTransform[#, "BoundingBoxes", Infinity], 
   RecognitionPrior -> "Character"] & /@ imgs

{{H,1,O},{m,Y},{d,d}}

Question

  • Any workaround that can improve the recognition quality when TextRecognize work on single character

Or

  • If we want to improve the recognition quality by mask, how to build correct mask?

I desire to make my this answer better by TextRecognize.

yode
  • 26,686
  • 4
  • 62
  • 167
  • I recommend you putting your test images on http://imgur.com/, that those who use Mathematica version lower than 10 could work with them too. – Alexey Popkov Mar 21 '17 at 19:51
  • TextRecognize[#, "Character"] & is not recognized syntax, as indicated by the stream of errors it generates. Furthermore, there is no indication in the documentation of that usage. Why do you suppose that it should work? – MarcoB Mar 21 '17 at 19:55
  • @MarcoB I'm in 11.1 – yode Mar 21 '17 at 20:01

2 Answers2

7

I felt that I miss some simple way to unite closely located components and finally I found it: ImageForestingComponents (thanks to this answer)!

  • It is unfortunate that a link to this function isn't included in the "See Also" drop-down list neither on the Docs page for ComponentMeasurements, nor MorphologicalComponents, nor MorphologicalTransform. That's why I wasn't able to find it quickly...

I'll show how it can be used on the most problematic case with letter "i" which is formed by two not connected clusters of points:

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

image

With horizontal radius 1 and vertical radius 6 we get a segmentation where our letter "i" is counted as a single component:

ImageForestingComponents[i, Automatic, {1, 6}] // Colorize

image

Using ComponentMeasurements we can get the bounding boxes of our characters dropping the background:

c = ComponentMeasurements[ImageForestingComponents[i, Automatic, {1, 6}], 
  "BoundingBox", #"ConvexCoverage" < .9 &]
{2 -> {{66., 125.}, {79., 161.}}, 3 -> {{46., 61.}, {84., 98.}}}
HighlightImage[i, {Yellow, Rectangle @@@ c[[All, 2]]}]

image

TextRecognize accepts a set of Rectangle primitives as a Mask (it is documented under the Examples ► Options ► Masking sub-subsection):

TextRecognize[i, Masking -> Rectangle @@@ c[[All, 2]], RecognitionPrior -> "Character"]
{"i", "O"}

That's all. :^)

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
  • And do you think this is a bug behavior on TextRecognize that cannot correctly recognize single character? – yode Mar 27 '17 at 10:04
  • @yode When you give an improper mask to TextRecognize it is expected that it won't be able to recognize the character correctly. – Alexey Popkov Mar 27 '17 at 10:06
  • I mean that case when we don't give any mask.Just TextRecognize[img, "Character"] – yode Mar 27 '17 at 10:20
  • @yode RecognitionPrior -> "Character" assumes that we have only one character on the image (or on each masked area). And TextRecognize correctly recognizes the character "i" when it is the only char on the image: TextRecognize[ImageTake[i, 80], RecognitionPrior -> "Character"]. – Alexey Popkov Mar 27 '17 at 10:28
  • I do not mean use TextRecognize[i, RecognitionPrior -> "Character"](and I know what you have say in here).I just think TextRecognize[i, "Character"] should give a correct result. – yode Mar 27 '17 at 10:43
  • See this please. – yode Mar 27 '17 at 10:57
  • @yode Sorry, I forgot that "Character" also may be a level specification. The fact that TextRecognize[i, "Character"] and even TextRecognize[i, "Character", RecognitionPrior -> "SparseText"] do not return any result may indicate a bug either on the WRI side or on the side of the developers of Tesseract (upon which TextRecognize is based). You can check the latter on Windows using, for example, gImageReader. – Alexey Popkov Mar 27 '17 at 11:11
  • @yode I've checked gImageReader and it recognizes i without difficulties. Given that I suspect that we indeed have a bug on the WRI side. I think it is worth to ask the tech support about this (also it is interesting which version of Tesseract is included in Mathematica). – Alexey Popkov Mar 27 '17 at 11:52
  • Since this,I will report it to WRI right now.. – yode Mar 27 '17 at 12:20
6

You can use Dilation with rectangular kernel to extend the bounding boxes vertically in order to connect closely related components:

MorphologicalTransform[#, "BoundingBoxes", Infinity] & /@ imgs
Dilation[#, Table[1, {6}, {1}]] & /@ %

screenshot

With this approach

TextRecognize[#, 
   Masking -> Dilation[MorphologicalTransform[#, "BoundingBoxes", Infinity], 
     Table[1, {6}, {1}]], RecognitionPrior -> "Character"] & /@ imgs
{{"i", "O"}, {"m", "Y"}, {"d", "d"}}

Use ImageFilter to connect only closely located bounding boxes:

uniteBoxes[image_, range_: 6] := 
 ImageFilter[If[#[[{1, -1}, 1]] == {1, 1}, 1, #[[Ceiling[Length[#]/2], 1]]] &, 
  image, {range {1, 1}, {0, 0}}]

uniteBoxes@MorphologicalTransform[#, "BoundingBoxes", Infinity] & /@ imgs

TextRecognize[#, 
   Masking -> uniteBoxes[MorphologicalTransform[#, "BoundingBoxes", Infinity]], 
   RecognitionPrior -> "Character"] & /@ imgs

output

{{"i", "O"}, {"m", "Y"}, {"d", "d"}}

More robust approach:

fillGaps[list_, length_: 6] := 
  ReplacePart[list, 
   List /@ Flatten[Range @@@ SequencePosition[list, {1, Repeated[0, {1, length}], 1}]] -> 
    1];
connectVertically[image_, distance_: 6] := 
 Image[Transpose[fillGaps[#, distance] & /@ Transpose[ImageData[image]]]]
connectVertically@MorphologicalTransform[#, "BoundingBoxes", Infinity] & /@ imgs

output


P.S. It is possible that approaches shown in the following threads can be used to speed-up searching for closely located components (knowing the coordinates of their bounding boxes):

Also, DistanceMatrix can be of use here.

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368