You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python从PNG中仅提取拐角顶点数据?

Extracting Corner Vertices from PNG Instead of Line Segment Points

Hey there! Let's fix your issue where your current PIL code extracts every non-transparent pixel (including all the middle points of line segments) when you only need the corner vertices. I'll walk you through two practical solutions—one using OpenCV (great for shape detection) and another sticking with PIL if you prefer that library.

Why Your Current Code Isn't Working

Your existing code simply collects every pixel that's not the transparent white background. Line segments are made of hundreds of consecutive non-transparent pixels, so this approach pulls in all of them instead of just the turning points (corners) you care about.

Solution 1: Use OpenCV for Contour & Polygon Approximation

OpenCV has built-in tools to detect image contours and simplify them down to their key vertices, which is perfect for this task. Here's how to implement it:

First, install OpenCV if you haven't already:

pip install opencv-python

Then use this code:

import cv2
import numpy as np

def extract_corner_vertices(filename):
    # Load the image with alpha channel support
    img = cv2.imread(filename, cv2.IMREAD_UNCHANGED)
    
    # Handle transparent backgrounds: mask out fully transparent areas
    if img.shape[2] == 4:
        alpha_channel = img[:, :, 3]
        _, transparency_mask = cv2.threshold(alpha_channel, 0, 255, cv2.THRESH_BINARY)
        # Keep only non-transparent parts of the RGB channels
        img = cv2.bitwise_and(img[:, :, :3], img[:, :, :3], mask=transparency_mask)
    
    # Convert to grayscale for contour detection
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Binarize the image: turn non-background pixels white, background black
    _, binary_img = cv2.threshold(gray_img, 240, 255, cv2.THRESH_BINARY_INV)
    
    # Find external contours in the binary image
    contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    corner_vertices = []
    for contour in contours:
        # Simplify the contour to a polygon (this removes middle line points)
        # Epsilon controls how "tight" the approximation is—adjust based on your image
        epsilon = 0.01 * cv2.arcLength(contour, True)
        approximated_polygon = cv2.approxPolyDP(contour, epsilon, True)
        
        # Convert vertices to your desired format (scaled x*5, y*5)
        for point in approximated_polygon:
            x, y = point[0]
            # Grab the color of the vertex from the original image
            b, g, r = img[y, x]
            corner_vertices.append({"x": x*5, "y": y*5, "color": (r, g, b)})
    
    return corner_vertices

How This Works:

  1. Transparency Handling: We mask out any fully transparent parts of the image so they don't interfere with contour detection.
  2. Binarization: Converts the image to black and white to clearly separate your lines from the background.
  3. Contour Detection: Finds the outline of your drawn shapes.
  4. Polygon Approximation: The cv2.approxPolyDP function takes the full contour (all line pixels) and simplifies it to only the corner vertices by merging straight line segments. The epsilon value lets you adjust how precise the approximation is—smaller values mean closer matches to the original shape.

Solution 2: Stick with PIL (Neighborhood Analysis)

If you want to keep using PIL, you can check each non-transparent pixel's neighborhood to determine if it's a corner. A corner point will have a different pattern of neighboring non-transparent pixels compared to a middle line point.

Here's the code:

from PIL import Image

def is_corner(pixel_access, x, y, img_width, img_height):
    # Check all 8 surrounding neighbors of the current pixel
    neighbor_directions = [(-1,-1), (-1,0), (-1,1),
                           (0,-1),          (0,1),
                           (1,-1),  (1,0), (1,1)]
    
    non_transparent_neighbors = []
    for dx, dy in neighbor_directions:
        nx = x + dx
        ny = y + dy
        # Make sure we don't go out of image bounds
        if 0 <= nx < img_width and 0 <= ny < img_height:
            if pixel_access[nx, ny] != (255, 255, 255, 0):
                non_transparent_neighbors.append((dx, dy))
    
    # Rule out middle line points: they have exactly 2 opposite neighbors
    if len(non_transparent_neighbors) == 2:
        (dx1, dy1), (dx2, dy2) = non_transparent_neighbors
        # Check if neighbors are directly opposite (e.g., left/right, up/down, diagonal opposite)
        if dx1 == -dx2 and dy1 == -dy2:
            return False
    # All other cases are corners
    return True

def imageProcess(filename):
    corner_data = []
    with Image.open(filename) as im:
        px = im.load()
        width, height = im.size
        for x in range(width):
            for y in range(height):
                if px[x, y] != (255, 255, 255, 0):
                    if is_corner(px, x, y, width, height):
                        corner_data.append({"x": x*5, "y": y*5, "color": px[x, y]})
    return corner_data

How This Works:

  • The is_corner function checks the 8 pixels around the current point.
  • A middle line pixel (like on a straight horizontal line) will have exactly two neighbors: one to the left and one to the right (opposite directions). We filter these out.
  • Any pixel that doesn't fit this pattern (e.g., 3 neighbors for a sharp corner, or 2 neighbors that aren't opposite) is marked as a corner.

Which Solution to Choose?

  • Use OpenCV if your shapes are made of straight lines (like polygons). It's faster and more accurate for this use case, especially with complex shapes.
  • Use PIL if you need a lightweight solution without adding new libraries, or if you're working with irregular, freehand-style corners (adjust the is_corner logic if needed for your specific image).

内容的提问来源于stack exchange,提问作者Oliver Dimes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 17:42:54