2009-09-22 27 views
8

Tôi đang tìm một thuật toán để cắt bớt các đoạn đường ngắn từ đầu ra của máy dò cạnh. Như có thể thấy trong hình ảnh (và liên kết) bên dưới, có một số cạnh nhỏ được phát hiện không phải là các dòng "dài". Lý tưởng nhất là tôi chỉ muốn 4 mặt của tứ giác xuất hiện sau khi xử lý, nhưng nếu có một vài dòng đi lạc, nó sẽ không phải là một vấn đề lớn ... Bất cứ đề nghị?Cắt tỉa các đoạn đường ngắn từ đầu ra của đầu dò cạnh?

Example

Image Link

+0

Bạn chỉ tìm kiếm các hình chữ nhật? – tom10

Trả lời

5

Trước khi tìm ra mép trước quá trình hình ảnh với một mở hoặc gần hoạt động (hoặc cả hai), có nghĩa là, xói mòn Tiếp theo giãn ra, hoặc giãn ra Tiếp theo xói mòn. điều này sẽ loại bỏ các đối tượng nhỏ hơn nhưng để lại những vật thể lớn hơn gần như nhau.

Tôi đã tìm kiếm các ví dụ trực tuyến và tốt nhất tôi có thể tìm thấy ở trang 41 của this PDF.

+0

Nhìn vào hình ảnh ví dụ. Đường viền cạnh của hình chữ nhật chỉ mỏng 1 pixel! Nếu bạn xói mòn đầu tiên, bạn sẽ hoàn toàn mất hình chữ nhật cũng như các cạnh nhỏ. Nếu bạn làm giãn trước, bạn có thể đóng vài khoảng trống trong hình chữ nhật lớn của mình, nhưng đó là một vấn đề khác, và không thực sự giúp bạn loại bỏ các cạnh nhỏ. –

+0

@Levy - Không, như tôi đã nêu rõ trong câu trả lời của tôi, hình ảnh phải được đóng TRƯỚC KHI TÌM KIẾM EDGES.Tất nhiên điều này không nên được áp dụng cho các cạnh (nhưng đối với các đối tượng từ đó các cạnh được tính toán). – tom10

+0

@ tom10 - cảm ơn lời khuyên, tôi chuyển sang một hoạt động mở (thay thế một bộ lọc Gaussian) và tôi nhận được đầu ra phát hiện cạnh Canny tốt hơn nhiều (kết quả hoạt động gần trong hiệu suất tốt hơn). Tôi đã nghĩ về việc sử dụng xói mòn/giãn nở, nhưng tôi đã nghĩ đến việc sử dụng sau khi phát hiện cạnh, mà không làm việc với các đường mỏng. – user21293

4

tôi nghi ngờ rằng điều này có thể được thực hiện với một hoạt động địa phương đơn giản. Nhìn vào hình chữ nhật bạn muốn giữ - có một số khoảng trống, do đó thực hiện một hoạt động cục bộ để loại bỏ các đoạn đường ngắn có thể sẽ làm giảm đáng kể chất lượng của đầu ra mong muốn.

Do đó tôi sẽ cố gắng phát hiện hình chữ nhật là nội dung quan trọng bằng cách đóng các khoảng trống, lắp đa giác hoặc thứ gì đó tương tự, và sau đó trong bước thứ hai sẽ loại bỏ nội dung không quan trọng còn lại. Có thể là Hough transform có thể hữu ích.

CẬP NHẬT

tôi chỉ sử dụng sample application này sử dụng một Kernel Hough transform với hình ảnh mẫu của bạn và có bốn dây chuyền đẹp phù hợp hình chữ nhật của bạn.

+1

+1 để đề xuất biến đổi Hough. Chỉ cần tìm bốn đỉnh mạnh nhất trong không gian biến đổi, và đó là tứ giác của bạn. – erickson

2

Biến đổi Hough có thể là một hoạt động rất tốn kém.

Một thay thế có thể làm việc tốt trong trường hợp của bạn như sau:

  1. chạy 2 hoạt động hình thái toán học gọi là một hình ảnh gần gũi (http://homepages.inf.ed.ac.uk/rbf/HIPR2/close.htm) với một đường ngang và dọc (có chiều dài được xác định từ thử nghiệm) phần tử cấu trúc tương ứng. Điểm này là để đóng tất cả các khoảng trống trong hình chữ nhật lớn.

  2. chạy phân tích thành phần được kết nối. Nếu bạn đã thực hiện hình thái một cách hiệu quả, hình chữ nhật lớn sẽ xuất hiện dưới dạng một thành phần được kết nối. Sau đó nó chỉ tiếp tục lặp qua tất cả các thành phần được kết nối và chọn ra ứng cử viên có khả năng nhất nên là hình chữ nhật lớn.

+0

Đây là cách tôi sẽ làm. Nếu mục tiêu là tìm vị trí của hình chữ nhật lớn, hãy chọn thành phần lớn nhất. Nếu mục tiêu chỉ là để loại bỏ các cạnh ngắn (tiếng ồn) sau đó loại bỏ tất cả các thành phần đủ nhỏ, hoặc tất cả, nhưng lớn nhất. –

2

Có lẽ việc tìm kiếm các thành phần kết nối, sau đó loại bỏ các thành phần có ít hơn X pixel (theo kinh nghiệm quyết tâm), tiếp theo là sự giãn nở dọc theo các đường ngang/dọc để kết nối lại những khoảng trống trong hình chữ nhật

1

Có thể thực hiện theo hai kỹ thuật chính:

  1. Thao tác dựa trên vector: ánh xạ các hòn đảo pixel của bạn thành các cụm (blob, voronoi khu, bất kỳ). Sau đó áp dụng một số chẩn đoán để sửa chữa các phân đoạn, như thuật toán xấp xỉ chuỗi Teh-Chin, và thực hiện cắt tỉa của bạn khi các phần tử vectơ (bắt đầu, điểm cuối, chiều dài, hướng và vv).

  2. Đặt hoạt động dựa trên: cụm dữ liệu của bạn (như trên). Đối với mỗi cụm, tính toán các thành phần chính và phát hiện đường từ vòng tròn hoặc bất kỳ hình dạng nào khác bằng cách tìm các cụm chỉ hiển thị 1 giá trị riêng (hoặc 2 nếu bạn tìm phân đoạn "béo", có thể giống với dấu ba chấm). Kiểm tra các eigenvectors liên kết với các giá trị riêng để có thông tin về định hướng của các đốm màu và thực hiện lựa chọn của bạn.

Cả hai cách có thể dễ dàng được khám phá với OpenCV (trước đây thực sự thuộc danh mục thuật toán "Đường phân tích").

4

Trong trường hợp ai đó thực hiện bước này, OpenCV 2.x sẽ đưa ra một ví dụ có tên là squares.cpp về cơ bản sẽ làm móng tay tác vụ này.

tôi đã thực hiện một sửa đổi nhỏ để các ứng dụng để cải thiện việc phát hiện các tứ giác

enter image description here

:

#include "highgui.h" 
#include "cv.h" 

#include <iostream> 
#include <math.h> 
#include <string.h> 

using namespace cv; 
using namespace std; 

void help() 
{ 
     cout << 
     "\nA program using pyramid scaling, Canny, contours, contour simpification and\n" 
     "memory storage (it's got it all folks) to find\n" 
     "squares in a list of images pic1-6.png\n" 
     "Returns sequence of squares detected on the image.\n" 
     "the sequence is stored in the specified memory storage\n" 
     "Call:\n" 
     "./squares\n" 
    "Using OpenCV version %s\n" << CV_VERSION << "\n" << endl; 
} 

int thresh = 70, N = 2; 
const char* wndname = "Square Detection Demonized"; 

// helper function: 
// finds a cosine of angle between vectors 
// from pt0->pt1 and from pt0->pt2 
double angle(Point pt1, Point pt2, Point pt0) 
{ 
    double dx1 = pt1.x - pt0.x; 
    double dy1 = pt1.y - pt0.y; 
    double dx2 = pt2.x - pt0.x; 
    double dy2 = pt2.y - pt0.y; 
    return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); 
} 

// returns sequence of squares detected on the image. 
// the sequence is stored in the specified memory storage 
void findSquares(const Mat& image, vector<vector<Point> >& squares) 
{ 
    squares.clear(); 

    Mat pyr, timg, gray0(image.size(), CV_8U), gray; 

    // karlphillip: dilate the image so this technique can detect the white square, 
    Mat out(image); 
    dilate(out, out, Mat(), Point(-1,-1)); 
    // then blur it so that the ocean/sea become one big segment to avoid detecting them as 2 big squares. 
    medianBlur(out, out, 3); 

    // down-scale and upscale the image to filter out the noise 
    pyrDown(out, pyr, Size(out.cols/2, out.rows/2)); 
    pyrUp(pyr, timg, out.size()); 
    vector<vector<Point> > contours; 

    // find squares only in the first color plane 
    for(int c = 0; c < 1; c++) // was: c < 3 
    { 
     int ch[] = {c, 0}; 
     mixChannels(&timg, 1, &gray0, 1, ch, 1); 

     // try several threshold levels 
     for(int l = 0; l < N; l++) 
     { 
      // hack: use Canny instead of zero threshold level. 
      // Canny helps to catch squares with gradient shading 
      if(l == 0) 
      { 
       // apply Canny. Take the upper threshold from slider 
       // and set the lower to 0 (which forces edges merging) 
       Canny(gray0, gray, 0, thresh, 5); 
       // dilate canny output to remove potential 
       // holes between edge segments 
       dilate(gray, gray, Mat(), Point(-1,-1)); 
      } 
      else 
      { 
       // apply threshold if l!=0: 
       //  tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 
       gray = gray0 >= (l+1)*255/N; 
      } 

      // find contours and store them all as a list 
      findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); 

      vector<Point> approx; 

      // test each contour 
      for(size_t i = 0; i < contours.size(); i++) 
      { 
       // approximate contour with accuracy proportional 
       // to the contour perimeter 
       approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true); 

       // square contours should have 4 vertices after approximation 
       // relatively large area (to filter out noisy contours) 
       // and be convex. 
       // Note: absolute value of an area is used because 
       // area may be positive or negative - in accordance with the 
       // contour orientation 
       if(approx.size() == 4 && 
        fabs(contourArea(Mat(approx))) > 1000 && 
        isContourConvex(Mat(approx))) 
       { 
        double maxCosine = 0; 

        for(int j = 2; j < 5; j++) 
        { 
         // find the maximum cosine of the angle between joint edges 
         double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1])); 
         maxCosine = MAX(maxCosine, cosine); 
        } 

        // if cosines of all angles are small 
        // (all angles are ~90 degree) then write quandrange 
        // vertices to resultant sequence 
        if(maxCosine < 0.3) 
         squares.push_back(approx); 
       } 
      } 
     } 
    } 
} 


// the function draws all the squares in the image 
void drawSquares(Mat& image, const vector<vector<Point> >& squares) 
{ 
    for(size_t i = 1; i < squares.size(); i++) 
    { 
     const Point* p = &squares[i][0]; 
     int n = (int)squares[i].size(); 
     polylines(image, &p, &n, 1, true, Scalar(0,255,0), 3, CV_AA); 
    } 

    imshow(wndname, image); 
} 


int main(int argc, char** argv) 
{ 
    if (argc < 2) 
    { 
     cout << "Usage: ./program <file>" << endl; 
     return -1; 
    } 

    static const char* names[] = { argv[1], 0 }; 

    help(); 
    namedWindow(wndname, 1); 
    vector<vector<Point> > squares; 

    for(int i = 0; names[i] != 0; i++) 
    { 
     Mat image = imread(names[i], 1); 
     if(image.empty()) 
     { 
      cout << "Couldn't load " << names[i] << endl; 
      continue; 
     } 

     findSquares(image, squares); 
     drawSquares(image, squares); 
     imwrite("out.jpg", image); 

     int c = waitKey(); 
     if((char)c == 27) 
      break; 
    } 

    return 0; 
} 
0

Dưới đây là một giải pháp lọc hình thái đơn giản sau các dòng của @ Tom10:

Giải pháp trong matlab:

se1 = strel('line',5,180);   % linear horizontal structuring element 
se2 = strel('line',5,90);    % linear vertical structuring element 
I = rgb2gray(imread('test.jpg'))>80; % threshold (since i had a grayscale version of the image) 
Idil = imdilate(imdilate(I,se1),se2); % dilate contours so that they connect 
Idil_area = bwareaopen(Idil,1200); % area filter them to remove the small components 

Ý tưởng là về cơ bản kết nối các đường nét ngang để làm cho một bộ phận lớn và lọc bởi một bộ lọc mở khu vực sau này để có được những hình chữ nhật.

Kết quả:

Dilating directionally the contours (90 and 180)

Area opening using bwareaopen, This may need some tuning but otherwise its simple and robust filter

Các vấn đề liên quan