OpenCV 3.0 LineIterator OpenCV 3.0 LineIterator python python

OpenCV 3.0 LineIterator


I've solved my own problem. Line iterator seems to be unavailable in the cv2 library. Therefore, I made my own line iterator. No loops are used, so it should be pretty fast. Here is the code if anybody needs it:

def createLineIterator(P1, P2, img):    """    Produces and array that consists of the coordinates and intensities of each pixel in a line between two points    Parameters:        -P1: a numpy array that consists of the coordinate of the first point (x,y)        -P2: a numpy array that consists of the coordinate of the second point (x,y)        -img: the image being processed    Returns:        -it: a numpy array that consists of the coordinates and intensities of each pixel in the radii (shape: [numPixels, 3], row = [x,y,intensity])         """   #define local variables for readability   imageH = img.shape[0]   imageW = img.shape[1]   P1X = P1[0]   P1Y = P1[1]   P2X = P2[0]   P2Y = P2[1]   #difference and absolute difference between points   #used to calculate slope and relative location between points   dX = P2X - P1X   dY = P2Y - P1Y   dXa = np.abs(dX)   dYa = np.abs(dY)   #predefine numpy array for output based on distance between points   itbuffer = np.empty(shape=(np.maximum(dYa,dXa),3),dtype=np.float32)   itbuffer.fill(np.nan)   #Obtain coordinates along the line using a form of Bresenham's algorithm   negY = P1Y > P2Y   negX = P1X > P2X   if P1X == P2X: #vertical line segment       itbuffer[:,0] = P1X       if negY:           itbuffer[:,1] = np.arange(P1Y - 1,P1Y - dYa - 1,-1)       else:           itbuffer[:,1] = np.arange(P1Y+1,P1Y+dYa+1)                 elif P1Y == P2Y: #horizontal line segment       itbuffer[:,1] = P1Y       if negX:           itbuffer[:,0] = np.arange(P1X-1,P1X-dXa-1,-1)       else:           itbuffer[:,0] = np.arange(P1X+1,P1X+dXa+1)   else: #diagonal line segment       steepSlope = dYa > dXa       if steepSlope:           slope = dX.astype(np.float32)/dY.astype(np.float32)           if negY:               itbuffer[:,1] = np.arange(P1Y-1,P1Y-dYa-1,-1)           else:               itbuffer[:,1] = np.arange(P1Y+1,P1Y+dYa+1)           itbuffer[:,0] = (slope*(itbuffer[:,1]-P1Y)).astype(np.int) + P1X       else:           slope = dY.astype(np.float32)/dX.astype(np.float32)           if negX:               itbuffer[:,0] = np.arange(P1X-1,P1X-dXa-1,-1)           else:               itbuffer[:,0] = np.arange(P1X+1,P1X+dXa+1)           itbuffer[:,1] = (slope*(itbuffer[:,0]-P1X)).astype(np.int) + P1Y   #Remove points outside of image   colX = itbuffer[:,0]   colY = itbuffer[:,1]   itbuffer = itbuffer[(colX >= 0) & (colY >=0) & (colX<imageW) & (colY<imageH)]   #Get intensities from img ndarray   itbuffer[:,2] = img[itbuffer[:,1].astype(np.uint),itbuffer[:,0].astype(np.uint)]   return itbuffer


Edit:The function line from scikit-image can make the same effect and it's faster than anything we could code.

from skimage.draw import line# being start and end two points (x1,y1), (x2,y2)discrete_line = list(zip(*line(*start, *end)))

Also the timeit result is quite faster. So, use this.

Old "deprecated" answer:

As previous answer says, it's not implemented so you must do it yourself.I didn't do it from scratch i just rewrote some parts of the function in a fancier and more modern way that should handle all cases correctly unlike the most voted answer that didn't work correctly for me. I took the example from here and did some cleanup and some styling. Feel free to comment it. Also i added the clipline test like in the source code that can be found in the drawing.cpp in the source code for OpenCv 4.xThank you all for the references and the hard work.

    def bresenham_march(img, p1, p2):        x1 = p1[0]        y1 = p1[1]        x2 = p2[0]        y2 = p2[1]        #tests if any coordinate is outside the image        if (             x1 >= img.shape[0]            or x2 >= img.shape[0]            or y1 >= img.shape[1]            or y2 >= img.shape[1]        ): #tests if line is in image, necessary because some part of the line must be inside, it respects the case that the two points are outside            if not cv2.clipLine((0, 0, *img.shape), p1, p2):                print("not in region")                return        steep = math.fabs(y2 - y1) > math.fabs(x2 - x1)        if steep:            x1, y1 = y1, x1            x2, y2 = y2, x2        # takes left to right        also_steep = x1 > x2        if also_steep:            x1, x2 = x2, x1            y1, y2 = y2, y1        dx = x2 - x1        dy = math.fabs(y2 - y1)        error = 0.0        delta_error = 0.0        # Default if dx is zero        if dx != 0:            delta_error = math.fabs(dy / dx)        y_step = 1 if y1 < y2 else -1        y = y1        ret = []        for x in range(x1, x2):            p = (y, x) if steep else (x, y)            if p[0] < img.shape[0] and p[1] < img.shape[1]:                ret.append((p, img[p]))            error += delta_error            if error >= 0.5:                y += y_step                error -= 1        if also_steep:  # because we took the left to right instead            ret.reverse()        return ret


Not a fancy way to do this, but an effective and very very simple one-liner:

points_on_line = np.linspace(pt_a, pt_b, 100) # 100 samples on the line

If you want to approximately get each pixel along the way

points_on_line = np.linspace(pt_a, pt_b, np.linalg.norm(pt_a - pt_b))

(e.g. number of samples as the number of pixels between point A and point B)

For example:

pt_a = np.array([10, 11])pt_b = np.array([45, 67])im = np.zeros((80, 80, 3), np.uint8)for p in np.linspace(pt_a, pt_b, np.linalg.norm(pt_a-pt_b)):    cv2.circle(im, tuple(np.int32(p)), 1, (255,0,0), -1)plt.imshow(im)

points on line