Ask Your Question

How to determine length of spline (e.g. snake)

asked 2017-09-14 14:47:15 -0500

DougL gravatar image

updated 2017-12-10 03:30:48 -0500

LBerger gravatar image


Hello, I'm trying to automatically determine length of a spline - well, in this case it's the length of a snake. I thought I could modify existing code and then divide the contour into segments but I'm not having much luck.

The black rectangle in the photo is 12" x 2". The snake is more than 5' long.

Can someone point me in the right direction?

Command line used:

python --image images/img_1718_useforcalib_w2.5.jpg --width 2.5


# python --image images/example_01.png --width 0.955
# python --image images/example_02.png --width 0.955
# python --image images/example_03.png --width 3.5

# import the necessary packages
from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2

def midpoint(ptA, ptB):
    return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
    help="path to the input image")
ap.add_argument("-w", "--width", type=float, required=True,
    help="width of the left-most object in the image (in inches)")
args = vars(ap.parse_args())

# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)

# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)

# find contours in the edge map
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cnts = cnts[0] if imutils.is_cv2() else cnts[1]

# sort the contours from left-to-right and initialize the
# 'pixels per metric' calibration variable
(cnts, _) = contours.sort_contours(cnts)
pixelsPerMetric = None

# loop over the contours individually
for c in cnts:
    # if the contour is not sufficiently large, ignore it
    if cv2.contourArea(c) < 100:

    # compute the rotated bounding box of the contour
    orig = image.copy()
    box = cv2.minAreaRect(c)
    box = if imutils.is_cv2() else cv2.boxPoints(box)
    box = np.array(box, dtype="int")

    # order the points in the contour such that they appear
    # in top-left, top-right, bottom-right, and bottom-left
    # order, then draw the outline of the rotated bounding
    # box
    box = perspective.order_points(box)
    cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)

    # loop over the original points and draw them
    for (x, y) in box:, (int(x), int(y)), 5, (0, 0, 255), -1)

    # unpack the ordered bounding box, len compute the midpoint
    # between the top-left and top-right coordinates, followed by
    # the midpoint between bottom-left and bottom-right coordinates
    (tl, tr, br, bl) = box
    (tltrX, tltrY) = midpoint(tl, tr)
    (blbrX, blbrY) = midpoint(bl, br)

    # compute the midpoint between the top-left and top-right points,
    # followed by the midpoint between the top-righ and bottom-right
    (tlblX, tlblY) = midpoint(tl, bl ...
edit retag flag offensive close merge delete

3 answers

Sort by ยป oldest newest most voted

answered 2017-09-14 15:15:25 -0500

LBerger gravatar image

I don't know python but I tried this :

  1. invert image
  2. threshold image
  3. apply thinning algoirthm
  4. look for connected components
  5. snake is index surface is 513=length and and black rectangle is index 2 length is 79 (there is an error here because skeleton is not rectangle height)

There is no calibration so width of pixel far from center is not equal to pixel at center

image description

edit flag offensive delete link more


This solution doesn't use Contours, correct? What tool are you using - is that a OpenCV GUI that I'm not aware of?

DougL gravatar imageDougL ( 2017-09-14 16:02:37 -0500 )edit

No contour only connected components and thinning algorithm

LBerger gravatar imageLBerger ( 2017-09-15 01:16:26 -0500 )edit

@LBerger, interesting spreadsheet ! what's that ?

berak gravatar imageberak ( 2017-09-15 02:01:05 -0500 )edit

it's just wxopencv my old program...

LBerger gravatar imageLBerger ( 2017-09-15 02:59:35 -0500 )edit

Thank you @LBerger

DougL gravatar imageDougL ( 2017-09-15 07:19:22 -0500 )edit

@LBerger - I've tried 3 forms of thresholding but don't get the same results as you. Can you tell me what thresholding you're using - perhaps the exact commands? I'm a noob here.

DougL gravatar imageDougL ( 2017-09-15 08:36:26 -0500 )edit

manual threshold = 180. If you want to use OTSU you need to crop image

LBerger gravatar imageLBerger ( 2017-09-15 11:03:29 -0500 )edit

It's been a while and I still haven't figured this out. Any way you could share your spreadsheet with me - maybe it would help me get Python code done? Thanks!

DougL gravatar imageDougL ( 2017-12-09 09:45:30 -0500 )edit

>>> help(cv2.ximgproc.thinning)

berak gravatar imageberak ( 2017-12-09 09:57:09 -0500 )edit

I can't tell you how many hours I tried to find a function within OpenCV to no avail. Found several in other packages. Thank you!

DougL gravatar imageDougL ( 2017-12-09 13:50:37 -0500 )edit

answered 2017-12-10 08:10:01 -0500

supra56 gravatar image

@DougL. python3 --image images/snake.jpg --width 2.5. Uncertainly, measurement isn't accurately. image description But i set another is 1.019685. image description

edit flag offensive delete link more


where is

jsxyhelu gravatar imagejsxyhelu ( 2018-07-18 09:24:42 -0500 )edit

answered 2017-12-12 10:22:22 -0500

sjhalayka gravatar image

updated 2017-12-12 10:36:05 -0500

I got the C++ code for LBerger's algorithm! Hopefully it's not hard to convert it to Python. Does Python even support the opencv contrib modules?

Here's the input image: image description

Here's the code:

#include <opencv2/opencv.hpp>
#include <opencv2/ximgproc.hpp>
using namespace cv;
using namespace cv::ximgproc;
#pragma comment(lib, "opencv_world331.lib")

#include <iostream>
#include <map>
using namespace std;

int main(void)
    Mat frame = imread("snake.png");

    if (frame.empty())
        cout << "Error loading image file" << endl;
        return -1;

    cvtColor(frame, frame, CV_RGB2GRAY);

    bitwise_not(frame, frame);

    threshold(frame, frame, 190, 255, CV_THRESH_BINARY);

    thinning(frame, frame);

    Mat labels;
    connectedComponents(frame, labels, 8);

    map<size_t, size_t> counts;

    for (int j = 0; j < labels.rows; j++)
        for (int i = 0; i < labels.cols; i++)
            counts[<int>(j, i)]++;

            if (<int>(j, i) == 2)
      <unsigned char>(j, i) = 255;
            else if (<int>(j, i) == 1)
      <unsigned char>(j, i) = 127;
            else if (<int>(j, i) == 0)
      <unsigned char>(j, i) = 0;

    cout << counts.size() << endl;

    cout << counts[1] << endl; // The snake is this long in pixels.

    imshow("Frame", frame);


    return 0;
edit flag offensive delete link more
Login/Signup to Answer

Question Tools

1 follower


Asked: 2017-09-14 14:47:15 -0500

Seen: 615 times

Last updated: Dec 12 '17