使用Python/PIL进行图像轮廓提取

20

我有一张苹果的彩色照片,如何使用Python/PIL只显示它的轮廓(内部为白色,背景为黑色)?


包括一些预期输入的示例图像。 - mmgp
3个回答

30

像这样的东西应该可以工作。

from PIL import Image, ImageFilter

image = Image.open('your_image.png')
image = image.filter(ImageFilter.FIND_EDGES)
image.save('new_name.png') 

如果这不能给你想要的结果,那么你可以尝试使用PIL和Python以及其他库来实现Prewitt边缘检测、Sobel边缘检测或Canny边缘检测,参见相关question和以下example
如果你想做粒子检测/分析而不仅仅是边缘检测,你可以尝试使用py4ij调用ImageJ方法,该方法链接会给你期望的结果,或者尝试另一个粒子分析Python库EMAN,或者你可以使用PIL、SciPy和NumPy编写粒子检测算法。

嗨, 这些照片是彩色的,即使我将它们转换为灰度并运行滤镜,也无法得到我想要的苹果轮廓线,这可行吗?这有点像rsbweb.nih.gov/ij/docs/pdfs/examples.pdf中描述的ImageJ方法。谢谢。 - user1212200
1
@Appleman1234,非常感谢您参考我的帖子。嘿,您可以导入Canny边缘检测模块并运行它以仅查找边缘。输出将是一个numpy ndarray,但您可以使用im = Image.fromarray(imarray)将其转换为PIL图像。 - Vishwanath
@Appleman,基于FIND_EDGES,将图像分割成组件是否可能? - user1658296

4

如果您的对象与背景对比度相当明显

from PIL import Image
image = Image.open(your_image_file)
mask=image.convert("L")
th=150 # the value has to be adjusted for an image of interest 
mask = mask.point(lambda i: i < th and 255)
mask.save(file_where_to_save_result)

如果高对比度只存在于三种颜色之一,你可以将图像分成带状而不是将其转换为灰度。
如果图像或背景相当复杂,则需要更复杂的处理。

1

苹果 vs 线条 你只需使用PIL和Python不到200行的代码就可以完成。使用库中的canny-edge-detection会更容易。
以下是步骤:将图像转换为灰度图进行亮度处理。使用Sobel进行核图像处理来检测边缘。通过Sobel获得的幅度和斜率对边缘进行细化。

from PIL import Image
import math


def one_to_two_dimension_array(list_,columns):
    #use list slice
    return [ list_[i:i+columns] for i in range(0, len(list_),columns) ] 

def flatten_matrix(matrix):
    return [val for sublist in matrix for val in sublist]

def matrix_convole(matrix, kernel_matrix, multiplier):
    return_list=[]
    return_matrix=[]

    border=(len(kernel_matrix) - 1) / 2;border=int(border)
    center_kernel_pos=border
    for matrix_row in range( len( matrix )):
        for matrix_col in range(len( matrix[matrix_row] ) ):
            accumulator = 0
            if (matrix_row - border)<0 or \
            (matrix_col-border)< 0 or \
            (matrix_row+border) > (len( matrix )-border) or \
            (matrix_col+border) > (len( matrix[matrix_row] )-border):
                return_list.append(matrix[matrix_row][matrix_col])
                continue
            for kernel_row in range(len (kernel_matrix) ):
                for kernel_col in range(len (kernel_matrix[kernel_row]) ):      

                    relative_row= kernel_row - center_kernel_pos
                    relative_col= kernel_col - center_kernel_pos
                    kernel = kernel_matrix[kernel_row][kernel_col]
                    pixel = matrix [matrix_row + relative_row] [matrix_col + relative_col]
                    accumulator += pixel * kernel
            return_list.append(accumulator* multiplier )
    return_matrix = one_to_two_dimension_array( return_list, len( matrix[0] ) )
    return return_matrix
            
def canny_round_degree(deg):
    #0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5, 180

    if deg >= 0 and deg <= 22.5:
        return 0
    elif deg >= 22.5 and deg <= 67.5:
        return 45
    elif deg > 67.5 and deg <=112.5:
        return 90
    elif deg > 112.5 and deg <=157.5:
        return 135
    elif deg >= 157.5 and deg <= 180:
        return 0

    if deg <= 0 and deg >= -22.5:
        return 0
    elif deg <= -22.5 and deg >= -67.5:
        return 135
    elif deg < -67.5 and deg >= -112.5:
        return 90
    elif deg < -112.5 and deg >= -157.5:
        return 45
    elif deg <= -157.5 and deg >= -180:
        return 0

image_path='apple.jpg'
gaussian_5x5_kernel=[[2,4,5,4,2],[4,9,12,9,4],[5,12,15,12,5],[4,9,12,9,4],[2,4,5,4,2]] #multiplier 1/159
sobel_kernel_gx=[[-1,0,1],[-2,0,2],[-1,0,1]]
sobel_kernel_gy=[[-1,-2,-1],[0,0,0],[1,2,1]] 
im_list=list(Image.open(image_path).convert('L').getdata(0)) #grayscale, get first channel
im_width=Image.open(image_path).width
im_height=Image.open(image_path).height
im_matrix = one_to_two_dimension_array(im_list, im_width)

im_matrix_blur=matrix_convole(im_matrix,gaussian_5x5_kernel, 1/159)

sobel_gx_matrix=matrix_convole(im_matrix_blur,sobel_kernel_gx, 1)
sobel_gy_matrix=matrix_convole(im_matrix_blur,sobel_kernel_gy, 1)

sobel_gy_list=flatten_matrix(sobel_gy_matrix) 
sobel_gx_list=flatten_matrix(sobel_gx_matrix) 

sobel_g_magnitude_list = [math.hypot(gy,gx) for gx,gy in zip(sobel_gx_list,sobel_gy_list)]


sobel_g_angle_list = [ canny_round_degree(math.degrees(math.atan2(gy,gx))) for gx,gy in zip(sobel_gx_list,sobel_gy_list)]

sobel_g_angle_matrix = one_to_two_dimension_array(sobel_g_angle_list, im_width)
sobel_g_magnitude_matrix = one_to_two_dimension_array(sobel_g_magnitude_list, im_width)
suppression_list = []
for s_row in range( len( sobel_g_angle_matrix)):
    for s_col in range(len( sobel_g_angle_matrix[s_row] ) ):
        if (s_row - 1)<0 or \
            (s_col-1)< 0 or \
            (s_row+1) > (len( sobel_g_angle_matrix )-1) or \
            (s_col+1) > (len( sobel_g_angle_matrix[s_row] )-1):
            suppression_list.append(0)
            continue
    
        magnitude_in_question = sobel_g_magnitude_matrix[s_row][s_col]
        #thresholding magnitude continue, arbitrary 129
        if magnitude_in_question < 36:
            suppression_list.append(0)
            continue    

        angle_in_question = sobel_g_angle_matrix[s_row][s_col]

        east_magnitude = sobel_g_magnitude_matrix[s_row][s_col-1]
        west_magnitude = sobel_g_magnitude_matrix[s_row][s_col+1]
        north_magnitude = sobel_g_magnitude_matrix[s_row-1][s_col]
        south_magnitude = sobel_g_magnitude_matrix[s_row+1][s_col]
        north_east_magnitude = sobel_g_magnitude_matrix[s_row-1][s_col-1]
        north_west_magnitude = sobel_g_magnitude_matrix[s_row-1][s_col+1]
        south_east_magnitude = sobel_g_magnitude_matrix[s_row+1][s_col-1]
        south_west_magnitude = sobel_g_magnitude_matrix[s_row+1][s_col+1]       
        if angle_in_question == 0 and magnitude_in_question > east_magnitude \
            and magnitude_in_question > west_magnitude:
            suppression_list.append(1)  
        elif angle_in_question == 90 and magnitude_in_question > north_magnitude \
            and magnitude_in_question > south_magnitude:
            suppression_list.append(1)  
        elif angle_in_question == 135 and magnitude_in_question > north_west_magnitude \
            and magnitude_in_question > south_east_magnitude:
            suppression_list.append(1)  
        elif angle_in_question == 45 and magnitude_in_question > north_east_magnitude \
            and magnitude_in_question > south_west_magnitude:
            suppression_list.append(1)  
        else:
            suppression_list.append(0)  
            

new_img = Image.new('1', (im_width,im_height)) #bw=1;grayscale =L
new_img.putdata( suppression_list )
new_img.save('apple-lines.png', 'PNG')


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接