Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a different color for each side of a polygon?

Tags:

python

opencv

I want to draw an open polygon with say 50 sides. Now I am looking for a way to have each side a different color, depending on an index, eg:

polygon_coordinates = [ [5, 10], [7, 9], [8, 11], [11, 20] ]
color_index = [100, 75, 200]

I am looking for something like

import cv2
for i in range (0, len(polygon_coordinates)-1):
    cv2.polylines([polygon_coordinates[i],polygon_coordinates[i+1]], color=[color_index[i], color_index[i], color_index[i]])

Is there a way for that, ideally without a loop? Thanks for the help

like image 599
ninehundred Avatar asked Sep 27 '22 20:09

ninehundred


1 Answers

This is probably not the answer you are looking for:)

Short answer is not really. You cannot do that in cv2. I also checked about 5 or 6 other libraries and it is the same for them as well (I am sure you have done that too).

Not all is lost though. I have a strong feeling that the polylines in cv2 is implemented by using the line function. From my rusty cpp years, here is what I gathered when I delved into the source code for OpenCV for linux and mac (https://github.com/Itseez/opencv/archive/3.0.0.zip):

In opencv-3.0.0/modules/imgproc/src/drawing.cpp

polylines call PolyLine to do the drawing at the end of the code block

void polylines( Mat& img, const Point* const* pts, const int* npts, int ncontours, bool isClosed,
                const Scalar& color, int thickness, int line_type, int shift )
{
    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    CV_Assert( pts && npts && ncontours >= 0 &&
               0 <= thickness && thickness <= MAX_THICKNESS &&
               0 <= shift && shift <= XY_SHIFT );

    double buf[4];
    scalarToRawData( color, buf, img.type(), 0 );

    for( int i = 0; i < ncontours; i++ )
        PolyLine( img, pts[i], npts[i], isClosed, buf, thickness, line_type, shift );
}

PolyLine calls ThickLine to draw segments by looping.

PolyLine( Mat& img, const Point* v, int count, bool is_closed,
          const void* color, int thickness,
          int line_type, int shift )
{
    if( !v || count <= 0 )
        return;

    int i = is_closed ? count - 1 : 0;
    int flags = 2 + !is_closed;
    Point p0;
    CV_Assert( 0 <= shift && shift <= XY_SHIFT && thickness >= 0 );

    p0 = v[i];
    for( i = !is_closed; i < count; i++ )
    {
        Point p = v[i];
        ThickLine( img, p0, p, color, thickness, line_type, flags, shift );
        p0 = p;
        flags = 2;
    }
}

ThickLine in turn invokes various Line functions to do its implementations, truncated here because it is a long function but just look at what it does when drawing lines of thickness of 1 or less, it calls the Line function

ThickLine( Mat& img, Point p0, Point p1, const void* color,
           int thickness, int line_type, int flags, int shift )
{
    static const double INV_XY_ONE = 1./XY_ONE;

    p0.x <<= XY_SHIFT - shift;
    p0.y <<= XY_SHIFT - shift;
    p1.x <<= XY_SHIFT - shift;
    p1.y <<= XY_SHIFT - shift;

    if( thickness <= 1 )
    {
        if( line_type < CV_AA )
        {
            if( line_type == 1 || line_type == 4 || shift == 0 )
            {
                p0.x = (p0.x + (XY_ONE>>1)) >> XY_SHIFT;
                p0.y = (p0.y + (XY_ONE>>1)) >> XY_SHIFT;
                p1.x = (p1.x + (XY_ONE>>1)) >> XY_SHIFT;
                p1.y = (p1.y + (XY_ONE>>1)) >> XY_SHIFT;
                Line( img, p0, p1, color, line_type );
            }
            else
                Line2( img, p0, p1, color );
        }
        else
            LineAA( img, p0, p1, color );
    }
    ...

And finally Line (and its variations like Line2 etc) just plot points:

Line( Mat& img, Point pt1, Point pt2,
      const void* _color, int connectivity = 8 )
{
    if( connectivity == 0 )
        connectivity = 8;
    else if( connectivity == 1 )
        connectivity = 4;

    LineIterator iterator(img, pt1, pt2, connectivity, true);
    int i, count = iterator.count;
    int pix_size = (int)img.elemSize();
    const uchar* color = (const uchar*)_color;

    for( i = 0; i < count; i++, ++iterator )
    {
        uchar* ptr = *iterator;
        if( pix_size == 1 )
            ptr[0] = color[0];
        else if( pix_size == 3 )
        {
            ptr[0] = color[0];
            ptr[1] = color[1];
            ptr[2] = color[2];
        }
        else
            memcpy( *iterator, color, pix_size );
    }
}

That means that there shouldn't be too much of a performance hit for calling lines over polylines because the C++ code is doing more or less the same thing: iterate over a line drawing function.

If you want to test this, you can probably draw a single colored polygon with a number of sides close to what you need to use in your application by calling polylines and line and timing them. Here is a very quick and dirty example of how you can time stuff in Python from Learning Python 5th Edition page 630:

import time
def timer(func, *args):
    start = time.clock()
    for i in range(1000):
        func(*args)
    return time.clock() - start

I am sure you can find much better tools than this to test though:)

One last thought: If I am wrong and there is a noticeable performance difference between two methods, you can always optimize your code afterwards for more speed. There are tons of tools to speed up Python tools. You can start by looking at PyPy.

like image 60
Sinan Kurmus Avatar answered Sep 30 '22 07:09

Sinan Kurmus