Outline a region in a graph

后端 未结 1 1358
心在旅途
心在旅途 2021-01-20 20:51

I have two 2D numpy arrays (of the same dimensions) that I am plotting using matplotlib. The first array I\'ve plotted as a color map in gray-scale. The second one represe

相关标签:
1条回答
  • 2021-01-20 21:47

    That is an interesting question, if I understood it correctly. In order to make sure what you mean, you would like to draw a line with some color around all contiguous areas where the pixel value is 3.

    I do not think there is a ready-made function for that, but let's not let that stop us. We will need to create our own function.

    We can start by creating a boolean map of the area which needs to be outlined:

    import numpy as np
    import matplotlib.pyplot as plt
    
    # our image with the numbers 1-3 is in array maskimg
    # create a boolean image map which has trues only where maskimg[x,y] == 3
    mapimg = (maskimg == 3)
    
    # a vertical line segment is needed, when the pixels next to each other horizontally
    #   belong to diffferent groups (one is part of the mask, the other isn't)
    # after this ver_seg has two arrays, one for row coordinates, the other for column coordinates 
    ver_seg = np.where(mapimg[:,1:] != mapimg[:,:-1])
    
    # the same is repeated for horizontal segments
    hor_seg = np.where(mapimg[1:,:] != mapimg[:-1,:])
    
    # if we have a horizontal segment at 7,2, it means that it must be drawn between pixels
    #   (2,7) and (2,8), i.e. from (2,8)..(3,8)
    # in order to draw a discountinuous line, we add Nones in between segments
    l = []
    for p in zip(*hor_seg):
        l.append((p[1], p[0]+1))
        l.append((p[1]+1, p[0]+1))
        l.append((np.nan,np.nan))
    
    # and the same for vertical segments
    for p in zip(*ver_seg):
        l.append((p[1]+1, p[0]))
        l.append((p[1]+1, p[0]+1))
        l.append((np.nan, np.nan))
    
    # now we transform the list into a numpy array of Nx2 shape
    segments = np.array(l)
    
    # now we need to know something about the image which is shown
    #   at this point let's assume it has extents (x0, y0)..(x1,y1) on the axis
    #   drawn with origin='lower'
    # with this information we can rescale our points
    segments[:,0] = x0 + (x1-x0) * segments[:,0] / mapimg.shape[1]
    segments[:,1] = y0 + (y1-y0) * segments[:,1] / mapimg.shape[0]
    
    # and now there isn't anything else to do than plot it
    plt.plot(segments[:,0], segments[:,1], color=(1,0,0,.5), linewidth=3)
    

    Let us test this by generating some data and showing it:

    image = np.cumsum(np.random.random((20,20))-.5, axis=1)
    maskimg = np.zeros(image.shape, dtype='int')
    maskimg[image > 0] = 3
    
    x0 = -1.5
    x1 =  1.5
    y0 = 2.3
    y1 = 3.8
    
    plt.figure()
    plt.imshow(maskimg, origin='lower', extent=[x0,x1,y0,y1], cmap=plt.cm.gray, interpolation='nearest')
    plt.axis('tight')
    

    After that we run the procedure on the top, and get:

    enter image description here

    The code can be made much denser, if needed, but now comments take a lot of space. With large images it might be wise to optimize the image segment creation by finding continuous paths. That will reduce the number of points to plot by a factor of up to three. However, doing that requires a bit different code, which is not as clear as this one. (If there will appear comments asking for that and an appropriate number of upvotes, I'll add it :)

    0 讨论(0)
提交回复
热议问题