drawing a bounding box in large images

前端 未结 2 1255
予麋鹿
予麋鹿 2021-01-22 02:22

I have a large binary image (4k x 7k pix) from which I want to extract the entire yellow portion as a single rectangle. I tried binary erosion to even out features insi

相关标签:
2条回答
  • 2021-01-22 03:02

    As the image you provided includes distracting axes, and is the wrong colour and too small, I created as realistic a version as I could with ImageMagick like this in Terminal:

    convert bbox.png -alpha off -crop 120x215+40+13 -colorspace gray -normalize -threshold 50% -scale 4200x7200\! bbox.png
    

    The full-size version is 4200x7200.

    I then wrote a numpy-based version of bbox as follows

    #!/usr/local/bin/python3
    import numpy as np
    from PIL import Image
    
    def bbox(image):
        """Find bounding box of image"""
        # Project all columns into row same width as image
        proj=np.any(image,axis=0)
        # Find first non-zero value from Left
        L=np.argmax(proj)
        # And right
        R=image.shape[1]-np.argmax(np.flipud(proj))-1
        # Project all rows into column same height as image
        proj=np.any(image,axis=1)
        # Find first non-zero value from Top
        T=np.argmax(proj)
        # And Bottom
        B=image.shape[0]-np.argmax(np.flipud(proj))-1
        return T,L,B,R
    
    image=np.array(Image.open("a.png").convert("L"))
    print(bbox(image))
    

    That runs in 5.3ms on my Mac. Just for fun, I threaded it and ran the horizontal projection and vertical projection on separate parallel threads and it came down to 3.6ms with the same results.

    #!/usr/local/bin/python3
    import numpy as np
    from PIL import Image
    
    import threading
    import queue
    
    def DoOneDim(image,axis,q):
        """Find bounding box of image"""
        proj=np.any(image,axis=axis)
        # Find first non-zero value
        A=np.argmax(proj)
        # And and last
        B=image.shape[1-axis]-np.argmax(np.flipud(proj))-1
        q.put({axis:(A,B)})
    
    
    def bboxTh(image):
        """Threaded version of bbox() that does vertical and horizontal on their own theads"""
        q = queue.Queue()
        Hthread=threading.Thread(target=DoOneDim, args=(image,0,q))
        Vthread=threading.Thread(target=DoOneDim, args=(image,1,q))
        Hthread.start()
        Vthread.start()
        Hthread.join()
        Vthread.join()
        results=dict()
        while not q.empty():
           results.update(q.get())
        return results
    
    image=np.array(Image.open("a.png").convert("L"))
    print(bboxTh(image))
    

    The identified box looks likes this:

    0 讨论(0)
  • 2021-01-22 03:16

    Because you are looking for a single bounding box, don't use regionprops or any per-object function. This also makes it so that you don't need to try to make a single object out of all the yellow dots.

    The simplest solution here is to walk over the image, and for each pixel, determine if it is "yellow enough" (whatever that means for your application). If so, add the pixel's coordinates to the running bounding box calculation.

    The bounding box calculation is quite simple:

    top_left = [1e9, 1e9]
    bottom_right = [0, 0]
    for ...:
       # within your loop over the pixels, [x, y] are the current coordinates
       top_left = [min(top_left[0], x), min(top_left[1], y)];
       bottom_right = [max(bottom_right[0], x), max(bottom_right[1], y)];
    

    There might be a way with skimage to do this without loops, but I don't know that package at all.

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