[Python] Fitting curve not correct - automatic extrapolation

Discussão em 'Python' iniciado por Stack, Julho 25, 2025.

  1. Stack

    Stack Membro Participativo

    all, I have some graphs in .pdf that I want to digitize with coordinates in .csv. I have the python code that reads the pdf, recognizes the graph markers and fits a b-spline. Then I want to transfer the curve from pixel coordinates to real-world coordinates, but giving the min/max X and Y values. Then comes the problem - the code just extrapolates the curve to align with the X and Y extreme values which gives invalid results.

    Does anybody know a way to teach the code to read the original graph position with respect to X and Y values (since the original graph never starts from X0,Y0, but the code extrapolates the curve falsely to start from X0,Y0)? Contour detection and B-spline fit to pixels vs. original graph

    Fitted B-spline to real-world coordinates

    import fitz # PyMuPDF
    import cv2
    import numpy as np
    from PIL import Image
    import matplotlib.pyplot as plt
    from scipy.interpolate import splrep, splev
    import csv

    # Load the PDF
    pdf_path = "app 1,2 2.pdf"
    doc = fitz.open(pdf_path)

    # Process each page
    for page_num in range(len(doc)):
    page = doc.load_page(page_num)
    pix = page.get_pixmap()
    image = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
    image_np = np.array(image)

    # Convert image to HSV and create a blue color mask
    hsv = cv2.cvtColor(image_np, cv2.COLOR_RGB2HSV)
    lower_blue = np.array([85, 30, 30])
    upper_blue = np.array([135, 255, 255])
    blue_mask = cv2.inRange(hsv, lower_blue, upper_blue)

    # Find contours and compute centers
    contours, _ = cv2.findContours(blue_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    circular_centers = []

    for cnt in contours:
    M = cv2.moments(cnt)
    if M["m00"] != 0:
    cx = int(M["m10"] / M["m00"])
    cy = int(M["m01"] / M["m00"])
    circular_centers.append((cx, cy))

    # Proceed only if enough points are detected
    if len(circular_centers) >= 5:
    circular_centers = sorted(circular_centers, key=lambda pt: pt[0])
    x_pixels = np.array([pt[0] for pt in circular_centers])
    y_pixels = np.array([pt[1] for pt in circular_centers])

    # Fit B-spline only within the detected pixel range
    spline_params = splrep(x_pixels, y_pixels, s=0)
    x_fit_pixels = np.linspace(min(x_pixels), max(x_pixels), len(x_pixels))
    y_fit_pixels = splev(x_fit_pixels, spline_params)

    # Map pixel X to real-world log scale (0.001 to 200 mm)
    x_min_real, x_max_real = 0.001, 200
    x_min_pixel, x_max_pixel = min(x_pixels), max(x_pixels)
    log_x_real = np.log10(x_min_real) + (x_fit_pixels - x_min_pixel) / (x_max_pixel - x_min_pixel) * (
    np.log10(x_max_real) - np.log10(x_min_real))
    x_real_values = 10 ** log_x_real

    # Normalize and invert Y values
    y_min, y_max = min(y_pixels), max(y_pixels)
    y_fit_normalized = 100 * (y_fit_pixels - y_min) / (y_max - y_min)
    y_inverted = 100 - y_fit_normalized

    Continue reading...

Compartilhe esta Página