OpenCV Q&A Forum - RSS feedhttp://answers.opencv.org/questions/OpenCV answersenCopyright <a href="http://www.opencv.org">OpenCV foundation</a>, 2012-2018.Wed, 01 Feb 2017 00:24:13 -0600What is a good thinning algorithm for getting the "skeleton" of characters for OCR?http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/Hi guys I have a few thousand training examples for my neural network that looks like:
![image description](https://dl.dropbox.com/u/7224756/one.jpg)
The thickness does vary in my training set. The accuracy of the neural network on the test set isnt bad, as its around 97% but I have problems when the characters are super small, with a high thickness. I want to normalize the characters to have a standard thickness if possible using a thinning algorithm. I have found many papers that talk about them, but never explain in detail how they work. I was wondering if anyone knew a nice way to do this in OpenCV? I would be very greatful! Thanks.Tue, 16 Oct 2012 20:18:38 -0500http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/Answer by aiming_high for <p>Hi guys I have a few thousand training examples for my neural network that looks like:</p>
<p><img alt="image description" src="https://dl.dropbox.com/u/7224756/one.jpg"></p>
<p>The thickness does vary in my training set. The accuracy of the neural network on the test set isnt bad, as its around 97% but I have problems when the characters are super small, with a high thickness. I want to normalize the characters to have a standard thickness if possible using a thinning algorithm. I have found many papers that talk about them, but never explain in detail how they work. I was wondering if anyone knew a nice way to do this in OpenCV? I would be very greatful! Thanks.</p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=11697#post-id-11697if your data is noisy you can try Chatbri and Kameyama's framework for thinning noisy images. They provide a java implementation:
http://adapt.cs.tsukuba.ac.jp/~chatbri/web/publications.htmlMon, 15 Apr 2013 22:01:11 -0500http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=11697#post-id-11697Answer by ZachTM for <p>Hi guys I have a few thousand training examples for my neural network that looks like:</p>
<p><img alt="image description" src="https://dl.dropbox.com/u/7224756/one.jpg"></p>
<p>The thickness does vary in my training set. The accuracy of the neural network on the test set isnt bad, as its around 97% but I have problems when the characters are super small, with a high thickness. I want to normalize the characters to have a standard thickness if possible using a thinning algorithm. I have found many papers that talk about them, but never explain in detail how they work. I was wondering if anyone knew a nice way to do this in OpenCV? I would be very greatful! Thanks.</p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=3289#post-id-3289I did some more research and discovered
[this article](http://www-prima.inrialpes.fr/perso/Tran/Draft/gateway.cfm.pdf)
I found that somone implemented this algorithim in opencv [here](http://opencv-users.1802565.n2.nabble.com/Morphological-thinning-operation-from-Z-Guo-and-R-W-Hall-quot-Parallel-Thinn-td4225544.html)
I then converted the code to the C++ opencv using Mats.
void ThinSubiteration1(Mat & pSrc, Mat & pDst) {
int rows = pSrc.rows;
int cols = pSrc.cols;
pSrc.copyTo(pDst);
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(pSrc.at<float>(i, j) == 1.0f) {
/// get 8 neighbors
/// calculate C(p)
int neighbor0 = (int) pSrc.at<float>( i-1, j-1);
int neighbor1 = (int) pSrc.at<float>( i-1, j);
int neighbor2 = (int) pSrc.at<float>( i-1, j+1);
int neighbor3 = (int) pSrc.at<float>( i, j+1);
int neighbor4 = (int) pSrc.at<float>( i+1, j+1);
int neighbor5 = (int) pSrc.at<float>( i+1, j);
int neighbor6 = (int) pSrc.at<float>( i+1, j-1);
int neighbor7 = (int) pSrc.at<float>( i, j-1);
int C = int(~neighbor1 & ( neighbor2 | neighbor3)) +
int(~neighbor3 & ( neighbor4 | neighbor5)) +
int(~neighbor5 & ( neighbor6 | neighbor7)) +
int(~neighbor7 & ( neighbor0 | neighbor1));
if(C == 1) {
/// calculate N
int N1 = int(neighbor0 | neighbor1) +
int(neighbor2 | neighbor3) +
int(neighbor4 | neighbor5) +
int(neighbor6 | neighbor7);
int N2 = int(neighbor1 | neighbor2) +
int(neighbor3 | neighbor4) +
int(neighbor5 | neighbor6) +
int(neighbor7 | neighbor0);
int N = min(N1,N2);
if ((N == 2) || (N == 3)) {
/// calculate criteria 3
int c3 = ( neighbor1 | neighbor2 | ~neighbor4) & neighbor3;
if(c3 == 0) {
pDst.at<float>( i, j) = 0.0f;
}
}
}
}
}
}
}
void ThinSubiteration2(Mat & pSrc, Mat & pDst) {
int rows = pSrc.rows;
int cols = pSrc.cols;
pSrc.copyTo( pDst);
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if (pSrc.at<float>( i, j) == 1.0f) {
/// get 8 neighbors
/// calculate C(p)
int neighbor0 = (int) pSrc.at<float>( i-1, j-1);
int neighbor1 = (int) pSrc.at<float>( i-1, j);
int neighbor2 = (int) pSrc.at<float>( i-1, j+1);
int neighbor3 = (int) pSrc.at<float>( i, j+1);
int neighbor4 = (int) pSrc.at<float>( i+1, j+1);
int neighbor5 = (int) pSrc.at<float>( i+1, j);
int neighbor6 = (int) pSrc.at<float>( i+1, j-1);
int neighbor7 = (int) pSrc.at<float>( i, j-1);
int C = int(~neighbor1 & ( neighbor2 | neighbor3)) +
int(~neighbor3 & ( neighbor4 | neighbor5)) +
int(~neighbor5 & ( neighbor6 | neighbor7)) +
int(~neighbor7 & ( neighbor0 | neighbor1));
if(C == 1) {
/// calculate N
int N1 = int(neighbor0 | neighbor1) +
int(neighbor2 | neighbor3) +
int(neighbor4 | neighbor5) +
int(neighbor6 | neighbor7);
int N2 = int(neighbor1 | neighbor2) +
int(neighbor3 | neighbor4) +
int(neighbor5 | neighbor6) +
int(neighbor7 | neighbor0);
int N = min(N1,N2);
if((N == 2) || (N == 3)) {
int E = (neighbor5 | neighbor6 | ~neighbor0) & neighbor7;
if(E == 0) {
pDst.at<float>(i, j) = 0.0f;
}
}
}
}
}
}
}
void HandOCR::normalizeLetter(Mat & inputarray, Mat & outputarray) {
bool bDone = false;
int rows = inputarray.rows;
int cols = inputarray.cols;
inputarray.convertTo(inputarray,CV_32FC1);
inputarray.copyTo(outputarray);
outputarray.convertTo(outputarray,CV_32FC1);
/// pad source
Mat p_enlarged_src = Mat(rows + 2, cols + 2, CV_32FC1);
for(int i = 0; i < (rows+2); i++) {
p_enlarged_src.at<float>(i, 0) = 0.0f;
p_enlarged_src.at<float>( i, cols+1) = 0.0f;
}
for(int j = 0; j < (cols+2); j++) {
p_enlarged_src.at<float>(0, j) = 0.0f;
p_enlarged_src.at<float>(rows+1, j) = 0.0f;
}
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if (inputarray.at<float>(i, j) >= 20.0f) {
p_enlarged_src.at<float>( i+1, j+1) = 1.0f;
}
else
p_enlarged_src.at<float>( i+1, j+1) = 0.0f;
}
}
/// start to thin
Mat p_thinMat1 = Mat::zeros(rows + 2, cols + 2, CV_32FC1);
Mat p_thinMat2 = Mat::zeros(rows + 2, cols + 2, CV_32FC1);
Mat p_cmp = Mat::zeros(rows + 2, cols + 2, CV_8UC1);
while (bDone != true) {
/// sub-iteration 1
ThinSubiteration1(p_enlarged_src, p_thinMat1);
/// sub-iteration 2
ThinSubiteration2(p_thinMat1, p_thinMat2);
/// compare
compare(p_enlarged_src, p_thinMat2, p_cmp, CV_CMP_EQ);
/// check
int num_non_zero = countNonZero(p_cmp);
if(num_non_zero == (rows + 2) * (cols + 2)) {
bDone = true;
}
/// copy
p_thinMat2.copyTo(p_enlarged_src);
}
// copy result
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
outputarray.at<float>( i, j) = p_enlarged_src.at<float>( i+1, j+1);
}
}
}
And it works great! Highly recommended to anyone who wants to get the skeleton of a letter for something like OCR normalization.
Oh and using my initial example here is the result:
![image description](https://dl.dropbox.com/u/7224756/ImageAfter_screenshot_18.10.2012.png)Thu, 18 Oct 2012 12:56:27 -0500http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=3289#post-id-3289Comment by Milanista for <div class="snippet"><p>I did some more research and discovered
<a href="http://www-prima.inrialpes.fr/perso/Tran/Draft/gateway.cfm.pdf">this article</a></p>
<p>I found that somone implemented this algorithim in opencv <a href="http://opencv-users.1802565.n2.nabble.com/Morphological-thinning-operation-from-Z-Guo-and-R-W-Hall-quot-Parallel-Thinn-td4225544.html">here</a></p>
<p>I then converted the code to the C++ opencv using Mats. </p>
<pre><code> void ThinSubiteration1(Mat & pSrc, Mat & pDst) {
int rows = pSrc.rows;
int cols = pSrc.cols;
pSrc.copyTo(pDst);
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(pSrc.at<float>(i, j) == 1.0f) {
/// get 8 neighbors
/// calculate C(p)
int neighbor0 = (int) pSrc.at<float>( i-1, j-1);
int neighbor1 = (int) pSrc.at<float>( i-1, j);
int neighbor2 = (int) pSrc.at<float>( i-1, j+1);
int neighbor3 = (int) pSrc.at<float>( i, j+1);
int neighbor4 = (int) pSrc.at<float>( i+1, j+1);
int neighbor5 = (int) pSrc.at<float>( i+1, j);
int neighbor6 = (int) pSrc.at<float>( i+1, j-1);
int neighbor7 = (int) pSrc.at<float>( i, j-1);
int C = int(~neighbor1 & ( neighbor2 | neighbor3)) +
int(~neighbor3 & ( neighbor4 | neighbor5)) +
int(~neighbor5 & ( neighbor6 | neighbor7)) +
int(~neighbor7 & ( neighbor0 | neighbor1));
if(C == 1) {
/// calculate N
int N1 = int(neighbor0 | neighbor1) +
int(neighbor2 | neighbor3) +
int(neighbor4 | neighbor5) +
int(neighbor6 | neighbor7);
int N2 = int(neighbor1 | neighbor2) +
int(neighbor3 | neighbor4) +
int(neighbor5 | neighbor6) +
int(neighbor7 | neighbor0);
int N = min(N1,N2);
if ((N == 2) || (N == 3)) {
/// calculate criteria 3
int c3 = ( neighbor1 | neighbor2 | ~neighbor4) & neighbor3;
if(c3 == 0) {
pDst.at<float>( i, j) = 0.0f;
}
}
}
}
}
}
}
void ThinSubiteration2(Mat & pSrc, Mat & pDst) {
int rows = pSrc.rows;
int cols = pSrc.cols;
pSrc.copyTo( pDst);
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if (pSrc.at<float>( i, j) == 1.0f) {
/// get 8 neighbors
/// calculate C(p)
int neighbor0 = (int) pSrc.at<float>( i-1, j-1);
int neighbor1 = (int) pSrc.at<float>( i-1, j);
int neighbor2 = (int) pSrc.at<float>( i-1, j+1);
int neighbor3 = (int) pSrc.at<float>( i, j+1);
int neighbor4 = (int) pSrc.at<float>( i+1, j+1);
int neighbor5 = (int) pSrc.at<float>( i+1, j);
int neighbor6 = (int) pSrc.at<float>( i+1, j-1);
int neighbor7 = (int) pSrc.at<float>( i, j-1);
int C = int(~neighbor1 & ( neighbor2 | neighbor3)) +
int(~neighbor3 & ( neighbor4 | neighbor5)) +
int(~neighbor5 & ( neighbor6 | neighbor7)) +
int(~neighbor7 & ( neighbor0 | neighbor1));
if(C == 1) {
/// calculate N
int N1 = int(neighbor0 | neighbor1) +
int(neighbor2 | neighbor3) +
int(neighbor4 | neighbor5) +
int(neighbor6 | neighbor7);
int N2 = int(neighbor1 | neighbor2) +
int(neighbor3 | neighbor4) +
int(neighbor5 | neighbor6) +
int(neighbor7 | neighbor0);
int N = min(N1,N2);
if((N == 2) || (N == 3)) {
int E = (neighbor5 | neighbor6 | ~neighbor0) & neighbor7;
if(E == 0) {
pDst.at<float>(i, j) = 0.0f;
}
}
}
}
}
}
}
void HandOCR::normalizeLetter(Mat & inputarray, Mat & outputarray) {
bool bDone = false;
int rows = inputarray.rows;
int cols = inputarray.cols;
inputarray.convertTo(inputarray,CV_32FC1);
inputarray.copyTo(outputarray);
outputarray.convertTo(outputarray,CV_32FC1);
/// pad source
Mat p_enlarged_src = Mat(rows + 2, cols + 2, CV_32FC1);
for(int i = 0; i < (rows+2); i++) {
p_enlarged_src.at<float>(i, 0) = 0.0f;
p_enlarged_src.at<float>( i, cols+1) = 0.0f;
}
for(int j = 0; j < (cols+2); j++) {
p_enlarged_src.at<float>(0, j) = 0.0f;
p_enlarged_src.at<float>(rows+1, j ...</code></pre><span class="expander"> <a>(more)</a></span></div>http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=12687#post-id-12687This is great for fingerprint! Thanks much!Tue, 30 Apr 2013 03:21:57 -0500http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=12687#post-id-12687Answer by RLD for <p>Hi guys I have a few thousand training examples for my neural network that looks like:</p>
<p><img alt="image description" src="https://dl.dropbox.com/u/7224756/one.jpg"></p>
<p>The thickness does vary in my training set. The accuracy of the neural network on the test set isnt bad, as its around 97% but I have problems when the characters are super small, with a high thickness. I want to normalize the characters to have a standard thickness if possible using a thinning algorithm. I have found many papers that talk about them, but never explain in detail how they work. I was wondering if anyone knew a nice way to do this in OpenCV? I would be very greatful! Thanks.</p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=8072#post-id-8072Which method is more efficient?
Ans: [Nash's implementation](http://opencv-code.com/quick-tips/implementation-of-thinning-algorithm-in-opencv/) of Zhang-Suen algorithm produces good result. Though there isn't expected result upon thinning/skeletonising thick **A, V, K, k, M, N, X, Y, y, Z, z, 2, 5** etc. There is little problem. Required close look.Mon, 25 Feb 2013 18:50:49 -0600http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=8072#post-id-8072Comment by Heshan Sandeepa for <p>Which method is more efficient?
Ans: <a href="http://opencv-code.com/quick-tips/implementation-of-thinning-algorithm-in-opencv/">Nash's implementation</a> of Zhang-Suen algorithm produces good result. Though there isn't expected result upon thinning/skeletonising thick <strong>A, V, K, k, M, N, X, Y, y, Z, z, 2, 5</strong> etc. There is little problem. Required close look.</p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=16637#post-id-16637this algorithm is excellent, thank youSat, 13 Jul 2013 00:53:11 -0500http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=16637#post-id-16637Answer by bsdnoobz for <p>Hi guys I have a few thousand training examples for my neural network that looks like:</p>
<p><img alt="image description" src="https://dl.dropbox.com/u/7224756/one.jpg"></p>
<p>The thickness does vary in my training set. The accuracy of the neural network on the test set isnt bad, as its around 97% but I have problems when the characters are super small, with a high thickness. I want to normalize the characters to have a standard thickness if possible using a thinning algorithm. I have found many papers that talk about them, but never explain in detail how they work. I was wondering if anyone knew a nice way to do this in OpenCV? I would be very greatful! Thanks.</p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=6017#post-id-6017I've implemented the [Zhang-Suen](http://opencv-code.com/quick-tips/implementation-of-thinning-algorithm-in-opencv/) and [Guo-Hall](http://opencv-code.com/quick-tips/implementation-of-guo-hall-thinning-algorithm/) thinning algorithms in my blog. Using your image, the result for Zhang-Suen algorithm is on the left and for Guo-Hall algorithm is on the right.
![image description](/upfiles/13581401379947773.jpg)
![image description](/upfiles/13581401542890768.jpg)Sun, 13 Jan 2013 23:11:44 -0600http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=6017#post-id-6017Comment by Mostafa Sataki for <p>I've implemented the <a href="http://opencv-code.com/quick-tips/implementation-of-thinning-algorithm-in-opencv/">Zhang-Suen</a> and <a href="http://opencv-code.com/quick-tips/implementation-of-guo-hall-thinning-algorithm/">Guo-Hall</a> thinning algorithms in my blog. Using your image, the result for Zhang-Suen algorithm is on the left and for Guo-Hall algorithm is on the right.</p>
<p><img alt="image description" src="/upfiles/13581401379947773.jpg">
<img alt="image description" src="/upfiles/13581401542890768.jpg"></p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=6022#post-id-6022Which method is more efficient?Mon, 14 Jan 2013 00:23:46 -0600http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=6022#post-id-6022Comment by Milanista for <p>I've implemented the <a href="http://opencv-code.com/quick-tips/implementation-of-thinning-algorithm-in-opencv/">Zhang-Suen</a> and <a href="http://opencv-code.com/quick-tips/implementation-of-guo-hall-thinning-algorithm/">Guo-Hall</a> thinning algorithms in my blog. Using your image, the result for Zhang-Suen algorithm is on the left and for Guo-Hall algorithm is on the right.</p>
<p><img alt="image description" src="/upfiles/13581401379947773.jpg">
<img alt="image description" src="/upfiles/13581401542890768.jpg"></p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=12685#post-id-12685Hello @bsdnoobz. Im using your Zhang-Suen algorith for thinning fingerprints, But I have a problem: I'm looking for minutae int the skeleton with crossing-number method - I check the pixel values in 3x3 blocks. My problem is that with this in some places the width of line is 2 pixels. Whats the problem?Tue, 30 Apr 2013 03:02:33 -0500http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=12685#post-id-12685Comment by M Mahdi Chamseddine for <p>I've implemented the <a href="http://opencv-code.com/quick-tips/implementation-of-thinning-algorithm-in-opencv/">Zhang-Suen</a> and <a href="http://opencv-code.com/quick-tips/implementation-of-guo-hall-thinning-algorithm/">Guo-Hall</a> thinning algorithms in my blog. Using your image, the result for Zhang-Suen algorithm is on the left and for Guo-Hall algorithm is on the right.</p>
<p><img alt="image description" src="/upfiles/13581401379947773.jpg">
<img alt="image description" src="/upfiles/13581401542890768.jpg"></p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=124561#post-id-124561Hey @bsdnoobz, your blog's domain name expired and the links are inaccessible anymore. Could you please provide alternative links.Wed, 01 Feb 2017 00:24:13 -0600http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?comment=124561#post-id-124561Answer by aiming_high for <p>Hi guys I have a few thousand training examples for my neural network that looks like:</p>
<p><img alt="image description" src="https://dl.dropbox.com/u/7224756/one.jpg"></p>
<p>The thickness does vary in my training set. The accuracy of the neural network on the test set isnt bad, as its around 97% but I have problems when the characters are super small, with a high thickness. I want to normalize the characters to have a standard thickness if possible using a thinning algorithm. I have found many papers that talk about them, but never explain in detail how they work. I was wondering if anyone knew a nice way to do this in OpenCV? I would be very greatful! Thanks.</p>
http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=11698#post-id-11698if your data is noisy you can try Chatbri and Kameyama's framework for thinning noisy images. They provide a java implementation:
http://adapt.cs.tsukuba.ac.jp/~chatbri/web/publications.htmlMon, 15 Apr 2013 22:01:59 -0500http://answers.opencv.org/question/3207/what-is-a-good-thinning-algorithm-for-getting-the-skeleton-of-characters-for-ocr/?answer=11698#post-id-11698