[New] Consistent usage of "similarity" and "confidence"#138
Conversation
86f891e to
b51f8d8
Compare
|
While testing the changes to |
b51f8d8 to
7668069
Compare
|
Hey @fernando79513, can you please take a look at this PR? The idea looks good to me but you worked on those files in the first place and you might have a different view on it. |
The only reason we kept this is because rapidOCR is noticeably slower than tesseract (around 2x), specially on big images. We may want to keep it in case we need faster detection times. Nevertheless, maintaining both introduces some complexity, so I don't have a strong opinion about keeping Tesseract. Any comments on that @p-gentili? |
fernando79513
left a comment
There was a problem hiding this comment.
Thanks a lot for the work here.
I usually prefer having confidence and similarities with values from 0-1 instead of 0-100. We didn't change that in the beginning because we couldn't modify the RPA library, but I guess it should be possible now that it's vendorized. I leave it up to you though.
Although I believe tesseract just doesn't work well in our use cases, this change doesn't really justify introducing a breaking change. The patch doesn't look that complex, and it would be similar to the amount of work required for any other OCR engine we might introduce. Nevertheless, we can discuss separately about the removal, but I would keep this PR as is. |
@p-gentili said in #100 (comment) that he prefers 0-100, so that's what I implemented here. I don't have an opinion on that so I'll let you two discuss it. |
7668069 to
3faead5
Compare
|
Rebased on main and resolved the conflicts. |
1accf29 to
a04652e
Compare
|
I noticed that a call to I'll remove the |
a04652e to
556ec5b
Compare
lol, let's fight @fernando79513 ! I like integers, that's all. If in this context [0,1] makes more sense that's fine by me. |
fernando79513
left a comment
There was a problem hiding this comment.
Just a minor comment, but I think it would make more sense to have the logs like this.
Let me know what you think. It's not really impactful for the code execution.
| elif similarity >= self.SIMILARITY_LOG_THRESHOLD: | ||
| logger.debug( | ||
| f"Rejected match for text '{match_text}' " | ||
| f"with similarity {similarity} " | ||
| f"and confidence {item.confidence}: '{item.text}'" | ||
| ) |
There was a problem hiding this comment.
If you are having a similarity threshold, why not having also a confidence threshold?
Also, why is the similarity log threshold the same as the similarity threshold?
I think it would be more useful to log the cases in which both confidence and similarity are close to the threshold values:
| elif similarity >= self.SIMILARITY_LOG_THRESHOLD: | |
| logger.debug( | |
| f"Rejected match for text '{match_text}' " | |
| f"with similarity {similarity} " | |
| f"and confidence {item.confidence}: '{item.text}'" | |
| ) | |
| elif ( | |
| similarity >= self.SIMILARITY_LOG_THRESHOLD | |
| and item.confidence >= self.CONFIDENCE_LOG_THRESHOLD | |
| ): | |
| logger.debug( | |
| f"Rejected match for text '{match_text}' " | |
| f"with similarity {similarity} " | |
| f"and confidence {item.confidence}: '{item.text}'" | |
| ) |
I'd suggest:
- SIMILARITY_LOG_THRESHOLD = 70.0
- CONFIDENCE_LOG_THRESHOLD = 60.0
Another option would be having just a "LOG_THRESHOLD" and compare:
elif (
similarity >= similarity_threshold - self.LOG_THRESHOLD
and item.confidence >= confidence_threshold - self.LOG_THRESHOLD
):
There was a problem hiding this comment.
If you are having a similarity threshold, why not having also a confidence threshold?
I don't think that would be useful. The confidence score measures how confident the OCR engine is that the string it returned is the actual string that's on the image. Let's say we want to find the string "foo" in an image, but the image only contains "bar". The OCR engine returns {"text": "bar", "confidence": "99"}, i.e. it's confident that the image really contains "bar". We calculate the similarity to "foo" which results in a similarity score of 10. Logging the message Rejected match for text 'foo' with similarity 10 and confidence 99: 'bar' is not very helpful. Does that make sense?
Also, why is the similarity log threshold the same as the similarity threshold?
Because I only found it useful when setting a higher threshold (which is made possible by #100). The log message is only useful to spot false negatives (i.e. a match was rejected even though it should have been accepted). We found the default threshold to be so low that it frequently causes false positive matches, so I doubt that there will be a lot of false negatives with an even lower similarity score.
There was a problem hiding this comment.
I don't think that would be useful
But in that case you won't log anything. Both have to be close to the threshold to log anything.
Imagine you text is blurry, but the OCR still manages to get your word right. You try to look for foo, and you get: {"text": "foo", "confidence": "65"}. The OCR was close to get it, but the confidence was just below the threshold. I think that information could be useful.
If the OCR returns {"text": "bar", "confidence": "99"}, similarity will be 0 and it won't log anything
Because I only found it useful
I don't see the point in having it exactly the same. If you are looking for "fooo" and OCR gets "foo0", you may not want to match it, because the similarity is 75, but it's close enough to your threshold that you may want it to be logged.
If you have the exact same value, you are basically filtering for similarity to see when the confidence is too low but the similarity is high.
I think having a log threshold right below your real threshold will be useful to log only the cases that are close, so you can adjust your thresholds accordingly.
There was a problem hiding this comment.
But in that case you won't log anything. Both have to be close to the threshold to log anything. Imagine you text is blurry, but the OCR still manages to get your word right. You try to look for foo, and you get:
{"text": "foo", "confidence": "65"}. The OCR was close to get it, but the confidence was just below the threshold. I think that information could be useful.
You already get that message with my proposal though, because the similarity between "foo" and "foo" is 100, so it's above the similarity log threshold. In the diff you suggested, you just add an additional condition for logging the rejected matches, i.e. that the confidence is also high. That means when the text is blurry but the OCR engine still recognizes the text we're looking for, e.g. {"text": "foo", "confidence": "50"}, it will reject the match and won't even log about it.
I don't see the point in having it exactly the same. If you are looking for "fooo" and OCR gets "foo0", you may not want to match it, because the similarity is 75, but it's close enough to your threshold that you may want it to be logged. If you have the exact same value, you are basically filtering for similarity to see when the confidence is too low but the similarity is high.
That is a case we want to log IMO. In the case from above, when the engine is unsure that it recognized the exact text and returns {"text": "foo", "confidence": "50"}, we do want to log the rejected match even if the confidence score is very low.
I think having a log threshold right below your real threshold will be useful to log only the cases that are close, so you can adjust your thresholds accordingly.
Agreed, that's why in our tests we are using a similarity threshold of 92 and a log threshold of 80. My point is just that the default similarity threshold is already too low and causes too many false positives. I don't think we can safely change that, because it would break existing tests. So the proposed log threshold of 80 would mainly be useful when you set a higher similarity threshold (except for the case discussed above, where the match was rejected because of low confidence, in that case the log threshold of 80 would also be useful without setting a higher similarity threshold). Anyway, if we also make the log threshold configurable, which we should probably do anyway, I'm also fine with using a lower default there.
There was a problem hiding this comment.
You already get that message with my proposal though.
You are 100% right there. Sorry for my confusion. The condition I suggested was even more restrictive.
If you think we don't need a lower bound to log the confidence results, I think that's okay. The lower bound for outputting a result (text_score) is already set at 0.5 in the rapidocr config.yaml, so I don't think it's going to introduce too much noise.
I didn't get at first why we would want to have a logging mechanism that just skips logging when using default values (for the similarity threshold). If you are only planning on increasing it, it makes sense because you will log the results you would get with the default values.
Nevertheless, if you were going to reduce the similarity threshold, you wouldn't log anything; that's why I liked the "LOG_THRESHOLD" approach.
Maybe the threshold is already too permissive, and we don't have to bother about it...
I leave it up to you if you want to have an absolute threshold for logs or a relative one.
There was a problem hiding this comment.
You are 100% right there. Sorry for my confusion.
No worries, I also found it challenging to understand the actual effect of the different approaches in practice and actually tried out the approaches to see the actual results.
If you are only planning on increasing it, it makes sense because you will log the results you would get with the default values.
Exactly, that was the idea.
Maybe the threshold is already too permissive, and we don't have to bother about it...
That's the case in my experience
556ec5b to
2ca4284
Compare
|
rebased on main and signed my commits which were missing signatures |
|
Do you have a strict "only squash and merge" policy? I tried to make the commits self-contained and write useful commit messages, would be a shame to lose those. |
We do, because versioning is computed automatically, but I can make it work for you. I just need you to rename all those commits removing any sort of prefix. |
We used the terms "confidence" and "coincidence", which are ambiguous
and used inconsistently:
* When using tesseract, only "confidence" is used, and it refers to the
similarity between the text that it tries to find and the text that
was returned by OCR.
* When using RapidOCR:
* "confidence" refers to the confidence score returned by RapidOCR,
which is the model's estimated probability that the returned text is
actually what is visible in the image.
* "coincidence" refers to the same which "confidence" refers to in
tesseract: the similarity between the text that we try to find and
the text that was returned by OCR
* In the matches dictionaries returned by find_text, the confidence item
always refers to the text similarity, even when using RapidOCR, where
the coincidence argument is used for that.
* For RapidOCR, the "confidence" value is a fraction (between 0 and 1),
for the other values we use percentage (between 0 and 100).
We now consistently use:
* "similarity" for text similarity
* "confidence" for the OCR confidence score
* Percentage (0 to 100) for all similarity and confidence values.
The module only returned similarity but tesseract does actually also return confidence values, we just have to use it.
When setting a too high similarity threshold, we can end up rejecting matches which are the ones we want to match. Logging those matches which were rejected even though they have a relatively high similarity can help debug those cases.
Including the image in the HTML log allows debugging cases in which text was found on a screenshot that shouldn't contain the text.
It's now already logged in find_text which is called via match_text.
2ca4284 to
fe4a7f7
Compare
Thank you!
Done! |
Description
This PR contains multiple improvements to the OCR modules used by yarf:
yarf/vendor/RPA/recognition/ocr.pyreturn actual confidencefind_textSee commit messages for details.
Tests
The existing tests were updated and run via
uv run pytest.