batlike
batlike

Reputation: 708

Fitting lines along bright pixels

I am trying to detect short "filaments" in images by fitting lines across the longest axis of bright pixel clusters. Is there a way to do this using tools from available computer vision libraries (ie. OpenCV)?

Intended result

Upvotes: 0

Views: 337

Answers (1)

Miki
Miki

Reputation: 41765

You can:

  1. find each connected component, with findContours
  2. find the minimum oriented bounding box of each connected component, with minAreaRect
  3. draw the line (parallel to longest side) passing trough the center.

Result:

enter image description here

Since you mentioned:

I'd like to reduce each "blob" to an object with the attributes 1) length 2) centroid 3) angle

I wrapped a few functionalities into the class Blob. Here's the code:

#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;

struct Blob
{
    float _length;
    Point2f _centroid;
    float _angle;

    Blob(float length, const Point2f& centroid, float angle) :
        _length(length), _centroid(centroid), _angle(angle) {};

    Blob(const Blob& other) :
        _length(other._length), _centroid(other._centroid), _angle(other._angle) {};

    Blob(const RotatedRect& r)
    {
        _centroid = r.center;
        _angle = r.angle*CV_PI / 180.;
        _length = r.size.height;
        if (r.size.width >= r.size.height)
        {
            _angle = (CV_PI / 2.0) + _angle;
            _length = r.size.width;
        }
    }

    void draw(const Mat3b& img)
    {
        // Draw line
        Point2f p1, p2;
        float b = (float)cos(_angle)*0.5f;
        float a = (float)sin(_angle)*0.5f;
        p1.x = _centroid.x - a*_length;
        p1.y = _centroid.y + b*_length;
        p2.x = _centroid.x + a*_length;
        p2.y = _centroid.y - b*_length;

        line(img, p1, p2, Scalar(0,0,255));
    };
};

int main()
{
    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    // Apply a threshold to remove JPEG artifacts
    Mat1b img2 = img > 200;

    // Prepare output image
    Mat3b result;
    cvtColor(img, result, COLOR_GRAY2BGR);

    // Apply a small border to take care of blobs on image boundary
    copyMakeBorder(img2, img2, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(0));

    // Find connected components
    vector<vector<Point>> contours;
    findContours(img2.clone(), contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

    // The vector of blobs
    vector<Blob> blobs;

    for (int i = 0; i < contours.size(); ++i)
    {
        // Account for border
        for (int j = 0; j < contours[i].size(); ++j)
        {
            contours[i][j] -= Point(1,1);
        }

        // Find minimum oriented bounding box
        RotatedRect r = minAreaRect(contours[i]);

        Blob b(r);
        b.draw(result);

        // Append to blobs
        blobs.push_back(b);
    }

    imshow("Result", result);
    waitKey();

    return 0;
}

Upvotes: 2

Related Questions