Profile Features¶

By this time, yous should have an image that looks like this

Binary Image

but in all reality, your prototype probably looks something like this

Binary Image Bad

Nosotros recommend you utilize "findcontours" for your border detector. Information technology is a rather sometime algorithm, but it is very effective as well as fast. Here is code that calls findcontours and draws them onto an image. Make certain that the paradigm yous are cartoon to is RGB and not GrayScale if you desire to depict things in colour.

                                    List                  <                  MatOfPoint                  >                  contours                  =                  new                  ArrayList                  <                  MatOfPoint                  >();                  Mat                  hierarchy                  =                  new                  Mat                  ();                  Imgproc                  .                  findContours                  (                  img                  ,                  contours                  ,                  hierarchy                  ,                  Imgproc                  .                  RETR_EXTERNAL                  ,                  Imgproc                  .                  CHAIN_APPROX_NONE                  );                  for                  (                  int                  i                  =                  0                  ;                  i                  <                  contours                  .                  size                  ();                  i                  ++)                  {                  //...contour code here...                  }                
                                    vector                  <                  vector                  <                  Indicate                  >                  >                  contours                  ;                  vector                  <                  Vec4i                  >                  hierarchy                  ;                  findContours                  (                  img                  ,                  contours                  ,                  hierarchy                  ,                  CV_RETR_EXTERNAL                  ,                  CV_CHAIN_APPROX_NONE                  ,                  Point                  (                  0                  ,                  0                  ));                  for                  (                  size_t                  i                  =                  0                  ;                  i                  <                  contours                  .                  size                  ();                  i                  ++                  )                  {                  drawContours                  (                  depict                  ,                  contours                  ,                  i                  ,                  Scalar                  (                  255                  ,                  0                  ,                  255                  ),                  three                  ,                  8                  ,                  hierarchy                  ,                  0                  ,                  Point                  ()                  );                  }                
                                    img2                  ,                  contours                  ,                  hierarchy                  =                  cv2                  .                  findContours                  (                  img                  ,                  cv2                  .                  RETR_TREE                  ,                  cv2                  .                  CHAIN_APPROX_NONE                  )                  for                  contour                  in                  contours                  :                  cv2                  .                  drawContours                  (                  draw                  ,                  [                  profile                  ],                  0                  ,                  (                  255                  ,                  0                  ,                  255                  ),                  iii                  )                

Binary Image Bad

CHAIN_APPROX_NONE is a flag that tells findContours to shop every contour point - including contours within other contours, or multiple forth the same line. If you don't have the strongest of computers (like a Pi), I recommend using CHAIN_APPROX_SIMPLE because it compresses horizontal, vertical, and diagonal segments and leaves only their terminate points.

Moving forward, this is where you lot must go creative. If y'all have contours that are not wanted, you lot must come up with tests that volition pass on the contours that you lot want, merely fail on the ones you lot practice non wish to proceed.

An implementation note: The following tests are meant to be implemented in a for loop, where the developer is looping through every profile.

                                    for                  (                  int                  i                  =                  0                  ;                  i                  <                  contours                  .                  size                  ();                  i                  ++                  )                  {                  //contours.get(i)....                  }                
                                    vector                  <                  vector                  <                  Point                  >                  >                  contours                  ;                  for                  (                  size_t                  i                  =                  0                  ;                  i                  <                  contours                  .                  size                  ();                  i                  ++                  )                  {                  //contours[i] ....                  }                
                                    for                  contour                  in                  contours                  :                  # do stuff with profile....                

Contour Area¶

A quick and easy manner to filter out pocket-sized contours is to check their area. Found a minimum and maximum area, which volition probably have to be plant empirically, and check to see if the profile's area falls within that range.

                                        double                    contourArea                    =                    Imgproc                    .                    contourArea                    (                    contour                    );                    if                    (                    contourArea                    <                    min_area                    ||                    contourArea                    >                    maxArea                    )                    {                    go along                    ;                    }                  
                                        float                    contourArea                    =                    contourArea                    (                    contours                    [                    i                    ]);                    if                    (                    contourArea                    >                    maxArea                    ||                    contourArea                    <                    minArea                    )                    {                    proceed                    ;                    }                  
                                        contourArea                    =                    cv2                    .                    contourArea                    (                    contour                    )                    if                    contourArea                    <                    minArea                    or                    contourArea                    >                    maxArea                    :                    continue                  

For code readability, I prefer to do that go on approach when looping through contours, instead of nested every contour examination. It makes the code more readable, as well makes the programmer have to worry about counting an absurd amount of curly brackets.

Aspect Ratio¶

Aspect Ratio refers to the ratio between the contour'due south width / contour's height. To do this, one could detect the extreme points, but information technology is easier to apply a bounding rectangle and and so compute the ratio of the divisional rectangle.

                                        Rect                    boundRect                    =                    Imgproc                    .                    boundingRect                    (                    contour                    );                    float                    ratio                    =                    (                    float                    )                    boundRect                    .                    width                    /                    boundRect                    .                    height                  
                                        Rect                    boundRect                    =                    boundingRect                    (                    contours                    [                    i                    ]);                    bladder                    ratio                    =                    (                    float                    )                    boundRect                    .                    width                    /                    boundRect                    .                    height                  
                                        x                    ,                    y                    ,                    w                    ,                    h                    =                    cv2                    .                    boundingRect                    (                    contour                    )                    ratio                    =                    float                    (                    west                    )                    /                    h                  

Remember to cast to float, otherwise integer sectionalization will occur and you won't go precise ratios.

Solidity¶

The terminal examination nosotros will cover is solidity. That is, the ratio between the contour expanse and the bounding rectangle expanse. This is useful to determine the "rectangle-ness" of the profile. The more than closely the bounding rectangle fits the contours, the closer this ratio will exist to i.

                                        Rect                    boundRect                    =                    Imgproc                    .                    boundingRect                    (                    contour                    );                    float                    ratio                    =                    Imgproc                    .                    contourArea                    (                    contour                    )/(                    boundRect                    .                    width                    *                    boundRect                    .                    top                    )                  
                                        Rect                    boundRect                    =                    boundingRect                    (                    contours                    [                    i                    ]);                    bladder                    ratio                    =                    contourArea                    (                    contours                    [                    i                    ])                    /                    (                    boundRect                    .                    width                    *                    boundRect                    .                    height                    );                  
                                        x                    ,                    y                    ,                    w                    ,                    h                    =                    cv2                    .                    boundingRect                    (                    contour                    )                    ratio                    =                    cv2                    .                    contourArea                    (                    contour                    )                    /                    (                    w                    *                    h                    )                  

Finding the middle¶

In typical FRC mode, y'all want your robot to line upwardly with the center of the target (contour). In order to do this, one must first detect the center. Here is how to discover the center using bounding rectangles. CenterX is the centre x coordinate and CenterY is middle y coordinate.

                                        Rect                    boundRect                    =                    Imgproc                    .                    boundingRect                    (                    profile                    );                    double                    centerX                    =                    boundRect                    .                    x                    +                    (                    boundRect                    .                    width                    /                    2                    )                    double                    centerY                    =                    boundRect                    .                    y                    +                    (                    boundRect                    .                    height                    /                    2                    )                  
                                        Rect                    boundRect                    =                    boundingRect                    (                    contours                    [                    i                    ]);                    double                    centerX                    =                    boundRect                    .                    x                    +                    (                    boundRect                    .                    width                    /                    ii                    )                    double                    centerY                    =                    boundRect                    .                    y                    +                    (                    boundRect                    .                    height                    /                    2                    )                  
                                        x                    ,                    y                    ,                    w                    ,                    h                    =                    cv2                    .                    boundingRect                    (                    contour                    )                    double                    centerX                    =                    boundRect                    .                    x                    +                    (                    boundRect                    .                    w                    /                    two                    )                    double                    centerY                    =                    boundRect                    .                    y                    +                    (                    boundRect                    .                    h                    /                    2                    )                  

While yous could simply apply a divisional rectangle and so observe the center of that, there is a more precise way: Northward-th order moments. Mathematically, a moment is defined every bit \(\mu _{n}=\int _{-\infty }^{\infty }(ten-c)^{n}\,f(x)\,dx\). For a 2nd continuous role f(x,y) the moment of society (p + q) is divers equally \(M_{{pq}}=\int \limits _{{-\infty }}^{{\infty }}\int \limits _{{-\infty }}^{{\infty }}x^{p}y^{q}f(ten,y)\,dx\,dy\). The area of a contour is the zeroth moment, and moments can exist used to find the centroid of a contour. The centroid is the center of mass of an object. The centroid point is also the center of gravity. Using moments, the centroid, C is defined as \(C_x = M_{x} / M_{00}\) and \(C_y =M_{01} /M_{00}\).

                                        Moments                    k                    =                    Imgproc                    .                    moments                    (                    contour                    );                    double                    centerX                    =                    m                    .                    get_m10                    ()                    /                    yard                    .                    get_m00                    ();                    double                    centerY                    =                    thousand                    .                    get_m01                    ()                    /                    m                    .                    get_m00                    ();                  
                                        cv                    ::                    Moments                    moment                    =                    cv                    ::                    moments                    (                    contours                    [                    i                    ]);                    cv                    ::                    Indicate                    center                    =                    cv                    ::                    Point2f                    (                    moment                    .                    m10                    /                    moment                    .                    m00                    ,                    moment                    .                    m01                    /                    moment                    .                    m00                    );                  
                                        moments                    =                    cv2                    .                    moments                    (                    contour                    )                    centerX                    =                    int                    (                    moments                    [                    'm10'                    ]                    /                    moments                    [                    'm00'                    ])                    centerY                    =                    int                    (                    moments                    [                    'm01'                    ]                    /                    moments                    [                    'm00'                    ])                  

Notation that in previous years for FRC, in that location hasn't been a vision challenge where the vision assistance tape wasn't symmetrical. But in hereafter if the tape isn't symmetrical, you would need to consider whether the centroid is what yous desire.

Drawing¶

A very useful function is drawing the contours on your images. While you can draw every profile using the -ane flag, instead of the contour index, information technology is recommended you only draw the contours that pass all your tests. This allows for fast and effective debugging.

                                        for                    (                    int                    i                    =                    0                    ;                    i                    <                    contours                    .                    size                    ();                    i                    ++)                    {                    //Tests....                    Imgproc                    .                    drawContours                    (                    contourImg                    ,                    contours                    ,                    i                    ,                    new                    Scalar                    (                    255                    ,                    255                    ,                    255                    ),                    -                    1                    );                    }                  
                                        for                    (                    size_t                    i                    =                    0                    ;                    i                    <                    contours                    .                    size                    ();                    i                    ++                    )                    {                    //Tests...                    drawContours                    (                    draw                    ,                    contours                    ,                    i                    ,                    Scalar                    (                    255                    ,                    0                    ,                    255                    ),                    iii                    ,                    8                    ,                    hierarchy                    ,                    0                    ,                    Point                    ()                    );                    }                  
                                        for                    profile                    in                    contours                    :                    #Tests...                    cv2                    .                    drawContours                    (                    describe                    ,                    [                    contour                    ],                    0                    ,                    (                    255                    ,                    0                    ,                    255                    ),                    3                    )                  

Putting it all together¶

While other contour tests can be done, such every bit approximating a polygon around a contour, this a a good basis that should permit you to solve just near every FRC computer vision claiming. Hither is a quick rundown of the tests needed to successfully solve the vision challenges over the years:

Year Tests
2012 Area, Solidity
2013 Expanse, Solidity, Aspect Ratio
2014 Expanse, Solidity
2015 Area, Solidity
2016 Area, Solidity
2017 Area, Solidity

Equally you tin can see, nearly every FRC vision challenge can exist solved with a simple expanse and solidity test.

Hither is an case of what a last prototype for 2017 with contours drawn on the prototype might look like.

Contour Final