Search code examples
pythonimage-processinglineopencv

1 px thick line cv2


I need to draw a line in an image where no pixel is thicker than 1 pixel in the horizontal dimension.

Despite I use thickness=1 in poly lines,

cv2.polylines(img, np.int32([points]), isClosed=False, color=(255, 255, 255), thickness=1)

in the resulting plot there may be 2 pixels horizontally adjacent set to 255, like in this pic:

enter image description here

How can I prevent to have adjacent pixels set to 255? Or equivalently: what is an efficient way to set to 0 one of the 2?

I thought to Erosion but then, in those lines where there is only 1 255 pixel, such a pixel would be set to 0 as well.


Solution

  • It looks like we need to use for loops.

    Removing one pixel out of two horizontally adjacent pixels is an iterative operation.
    I can't see a way to vectorize it, or use filtering or morphological operations.
    There could be something I am missing, but I think we need to use a loop.

    In case the image large, you may use Numba (or Cython) for accelerating the execution time.

    import cv2
    import numpy as np
    from numba import jit
    
    @jit(nopython=True)  # Use Numba JIT for accelerating the code execution time  
    def horiz_skip_pix(im):
        for y in range(im.shape[0]):
            for x in range(1, im.shape[1]):
                # Use logical operation instead of using "if statement".
                # We could have used an if statement, like if im[y, x]==255 and im[y, x-1]==255: im[y, x] = 0 ...
                im[y, x] = im[y, x] & (255-im[y, x-1])
    
    
    # Build sample input image
    src_img = np.zeros((10, 14), np.uint8)
    points = np.array([[2,2], [5,8], [12,5], [12,2], [3, 2]], np.int32)
    cv2.polylines(src_img, [points], isClosed=False, color=(255, 255, 255), thickness=1)
    
    dst_img = src_img.copy()
    
    # Remove horizontally adjacent pixels.
    horiz_skip_pix(dst_img)
    
    # Show result
    cv2.imshow('src_img', cv2.resize(src_img, (14*20, 10*20), interpolation=cv2.INTER_NEAREST))
    cv2.imshow('dst_img', cv2.resize(dst_img, (14*20, 10*20), interpolation=cv2.INTER_NEAREST))
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    src_img:
    src_img

    dst_img:
    dst_img

    I wouldn't call the result a "1 px thick line", but it meats the condition of "prevent to having adjacent pixels".