Trim scanned images with PIL?

后端 未结 2 1578
独厮守ぢ
独厮守ぢ 2021-01-07 06:55

What would be the approach to trim an image that\'s been input using a scanner and therefore has a large white/black area?

相关标签:
2条回答
  • 2021-01-07 07:48

    For starters, Here is a similar question. Here is a related question. And a another related question.

    Here is just one idea, there are certainly other approaches. I would select an arbitrary crop edge and then measure the entropy* on either side of the line, then proceed to re-select the crop line (probably using something like a bisection method) until the entropy of the cropped-out portion falls below a defined threshold. As I think, you may need to resort to a brute root-finding method as you will not have a good indication of when you have cropped too little. Then repeat for the remaining 3 edges.

    *I recall discovering that the entropy method in the referenced website was not completely accurate, but I could not find my notes (I'm sure it was in a SO post, however.)

    Edit: Other criteria for the "emptiness" of an image portion (other than entropy) might be contrast ratio or contrast ratio on an edge-detect result.

    0 讨论(0)
  • 2021-01-07 07:55

    the entropy solution seems problematic and overly intensive computationally. Why not edge detect?

    I just wrote this python code to solve this same problem for myself. My background was dirty white-ish, so the criteria that I used was darkness and color. I simplified this criteria by just taking the smallest of the R, B or B value for each pixel, so that black or saturated red both stood out the same. I also used the average of the however many darkest pixels for each row or column. Then I started at each edge and worked my way in till I crossed a threshold.

    Here is my code:

    #these values set how sensitive the bounding box detection is
    threshold = 200     #the average of the darkest values must be _below_ this to count (0 is darkest, 255 is lightest)
    obviousness = 50    #how many of the darkest pixels to include (1 would mean a single dark pixel triggers it)
    
    from PIL import Image
    
    def find_line(vals):
        #implement edge detection once, use many times 
        for i,tmp in enumerate(vals):
            tmp.sort()
            average = float(sum(tmp[:obviousness]))/len(tmp[:obviousness])
            if average <= threshold:
                return i
        return i    #i is left over from failed threshold finding, it is the bounds
    
    def getbox(img):
        #get the bounding box of the interesting part of a PIL image object
        #this is done by getting the darekest of the R, G or B value of each pixel
        #and finding were the edge gest dark/colored enough
        #returns a tuple of (left,upper,right,lower)
    
        width, height = img.size    #for making a 2d array
        retval = [0,0,width,height] #values will be disposed of, but this is a black image's box 
    
        pixels = list(img.getdata())
        vals = []                   #store the value of the darkest color
        for pixel in pixels:
            vals.append(min(pixel)) #the darkest of the R,G or B values
    
        #make 2d array
        vals = np.array([vals[i * width:(i + 1) * width] for i in xrange(height)])
    
        #start with upper bounds
        forupper = vals.copy()
        retval[1] = find_line(forupper)
    
        #next, do lower bounds
        forlower = vals.copy()
        forlower = np.flipud(forlower)
        retval[3] = height - find_line(forlower)
    
        #left edge, same as before but roatate the data so left edge is top edge
        forleft = vals.copy()
        forleft = np.swapaxes(forleft,0,1)
        retval[0] = find_line(forleft)
    
        #and right edge is bottom edge of rotated array
        forright = vals.copy()
        forright = np.swapaxes(forright,0,1)
        forright = np.flipud(forright)
        retval[2] = width - find_line(forright)
    
        if retval[0] >= retval[2] or retval[1] >= retval[3]:
            print "error, bounding box is not legit"
            return None
        return tuple(retval)
    
    if __name__ == '__main__':
        image = Image.open('cat.jpg')
        box = getbox(image)
        print "result is: ",box
        result = image.crop(box)
        result.show()
    
    0 讨论(0)
提交回复
热议问题