File size: 3,715 Bytes
a85e26f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from pathlib import Path
from typing import Iterable, List
import cv2
import json
import numpy as np
import pandas as pd


def norm_by_x(df):
    df['x'] = df['x'] - df['x'].min()
    df['y'] = df['y'] - df['y'].min()
    maxx = df['x'].max()
    df['x'] /= maxx
    df['y'] /= maxx
    return df


def csv2dfs(filenames:Iterable[str], rotate_func:callable=None) -> List[pd.DataFrame]:
    ''' Extract x,y coordinates from a .csv/.xlsx file. Each file may include multiple outlines.'''

    # if multiple x,y columns in the csv/xlsx file 
    def _sheet2dfs(df, df_name):
        dfs_local = []
        if 'Unnamed: 0' in df.columns:
            df = df.drop(columns=['Unnamed: 0'])
            column_pairs = [(df.columns[i], df.columns[i+1]) for i in range(0, len(df.columns)-1, 2)]                
            for x_col, y_col in column_pairs:
                shoe_id = x_col
                x, y = df[x_col].iloc[1:].astype(float), df[y_col].iloc[1:].astype(float)
                if rotate_func: x, y = rotate_func(x, y)
                shoe_df = pd.DataFrame({'x': x, 'y': y}).dropna()
                shoe_df.name = shoe_id
                dfs_local.append(shoe_df)
        else:
            df.name = df_name
            dfs_local += [df]
        return dfs_local

    dfs = []
    for filename in filenames:
        filename = Path(filename)

        if filename.suffix.lower() == '.csv':
            df = pd.read_csv(filename)
            dfs += _sheet2dfs(df, filename.name)

        elif filename.suffix.lower() == '.xlsx':
            xls = pd.ExcelFile(filename)
            for sheet in xls.sheet_names:
                df = pd.read_excel(xls, sheet)
                dfs += _sheet2dfs(df, sheet)
        
        elif filename.suffix.lower() == '.json':
            vgg_json = json.load(filename.open())
            for _,v in vgg_json.items():
                fn = v['filename']
                for region in v['regions']:
                    if region['shape_attributes']['name'] != 'polygon': continue
                    xs,ys = region['shape_attributes']['all_points_x'], region['shape_attributes']['all_points_y'] 
                    df = pd.DataFrame({'x':xs, 'y':ys})
                    df.name = fn
                    dfs.append(df)
                    break

    return dfs


def coordsdf2image(df:pd.DataFrame, target_height:int=256, margin:float=0.1) -> np.ndarray:
    ''' Render a footprint outline from x,y coordinates in a DataFrame into a binary image (filled shape). '''

    x_coords, y_coords = df['x'].values, df['y'].values

    # Compute the bounding box of the footprint
    x_min, x_max = x_coords.min(), x_coords.max()
    y_min, y_max = y_coords.min(), y_coords.max()
    width = x_max - x_min
    height = y_max - y_min

    # Compute canvas width proportional to the footprint's aspect ratio
    scale = target_height / (1 + 2 * margin) / height
    target_width = int(scale * width + 2 * margin * target_height)

    image = np.zeros((target_height, target_width), dtype=np.uint8)

    # Scale coordinates to fit within the canvas
    x_scaled = ((x_coords - x_min) * scale).astype(np.int32)
    y_scaled = ((y_coords - y_min) * scale).astype(np.int32)

    # Compute the size of the scaled footprint
    scaled_width = x_scaled.max() - x_scaled.min()
    scaled_height = y_scaled.max() - y_scaled.min()

    # Compute and apply offsets for centering
    x_scaled += (target_width - scaled_width) // 2 - x_scaled.min()
    y_scaled += (target_height - scaled_height) // 2 - y_scaled.min()

    contour = np.array([np.stack((x_scaled, y_scaled), axis=-1)], dtype=np.int32)
    cv2.fillPoly(image, contour, color=255)  # Fill the shape with white (255)
    return image