I need to get the lines from a photographie of a sheet of paper. While recherching, I came along this, where one answer referred to this blog entry.
Now I am having trouble with this approach. I have implemented it as follows (with the c++-interface of OpenCV:

// the image has to be grayscale
if (image.channels() != 1)
    cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);

// we need to enhance the lighting before we can threshold the photography
cv::equalizeHist(image, image);
// a binary image is needed
cv::threshold(image, image, threshold, 255, cv::THRESH_BINARY);
cv::imwrite("thresholdedImage.jpg", image);

// the resulting skeleton
cv::Mat skeleton(image.size(), CV_8UC1, cv::Scalar(0,0,0));
// needed if in-place processing is not possible
cv::Mat temp(image.size(), CV_8UC1);
// eroded image is saved here
cv::Mat eroded;
// needed for morphological transforms (erodation, dilation)
cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3,3));

bool done = true;
int i = 0;
    // eroding + dilating = opening
    cv::erode(image, eroded, element);
    cv::dilate(eroded, temp, element);
    cv::subtract(image, temp, temp);
    cv::bitwise_or(skeleton, temp, skeleton);

    done = (cv::countNonZero(image) == 0);

For proof of concept, I decide to use a computer-made drawing for testing purposes, to eliminate issues with lighting at this early stage. This can be seen here: Created with GIMP The threshold is correct, but unfortunately the skeleton looks very strange: enter image description here I have drawn the skeleton after 100 iterations, where it looked like this: enter image description here
Do you have any suggestions?

  • 3
    $\begingroup$ That looks like the right skeleton (to the first 100 iterations) of the bright background area. Have you tried inverting the image? $\endgroup$ – Niki Estner Aug 30 '13 at 7:47
  • $\begingroup$ Now I have tried, and it works! May you post this as an answer? $\endgroup$ – BlackMamba Aug 30 '13 at 8:56

You are actually drawing the skeleton of the background (brighter region).


cv::threshold(image, image, threshold, 255, cv::THRESH_BINARY);

in your codes to

cv::threshold(image, image, threshold, 255, cv::THRESH_BINARY_INV); 

You should be able to get the right skeleton.


You could well refer here for a few different implementations, which provide acceptable results.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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