File size: 11,328 Bytes
add142e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
import re
import numpy as np
import pandas as pd
from collections import defaultdict

from params import (
    CLASSES_SCS,
    SEVERITIES,
    CLASSES_CROP,
    LEVELS,
)


def get_coords(row):
    """
    Extracts coordinates from a row of data.

    Args:
        row (pandas Series): Row containing 'instance_number', 'x', 'y', and 'frames' columns.

    Returns:
        np.array: Coordinates.
    """
    coords = []
    for i, x, y in zip(row["instance_number"], row["x"], row["y"]):
        coords.append([row["frames"].index(i), x, y])
    return np.array(coords)


def prepare_data(data_path="../input/"):
    """
    Prepares data for training by loading and processing CSV files.

    Args:
        data_path (str, optional): The path to the input data directory. Defaults to "../input/".

    Returns:
        pd.DataFrame: A DataFrame containing the prepared data with additional columns for
                      orientation, weighting, image paths, and coordinates.
    """
    df = pd.read_csv(data_path + "train_series_descriptions.csv")

    df["orient"] = df["series_description"].apply(lambda x: x.split()[0])
    df["weighting"] = df["series_description"].apply(lambda x: x.split()[1])

    df["img_path"] = df["study_id"].astype(str) + "_" + df["series_id"].astype(str)
    df["img_path"] = data_path + "npy2/" + df["img_path"] + ".npy"

    labels = pd.read_csv(data_path + "train_label_coordinates.csv")
    labels = labels.groupby(["study_id", "series_id"]).agg(list).reset_index()

    frames = pd.read_csv("../input/df_frames.csv")
    frames["frames"] = frames["frames"].apply(eval)

    labels = labels.merge(frames, how="left")

    labels["coords"] = labels.apply(get_coords, axis=1)
    df = df.merge(
        labels[["study_id", "series_id", "condition", "level", "coords"]], how="left"
    )
    return df


def get_target(row):
    """
    Extracts the target value from a row based on the condition and level.

    Args:
        row (pandas Series): A row containing 'level', 'condition', and relevant target columns.

    Returns:
        float or np.nan: The target value if found, otherwise np.nan.
    """
    la, lb = row.level.lower().split("/")
    c = re.sub(" ", "_", row.condition.lower())
    try:
        return row[f"{c}_{la}_{lb}"]
    except KeyError:
        return np.nan


def prepare_data_scs(data_path="../input/", crop_folder=None, explode=True):
    """
    Prepares data for the SCS task by loading and processing CSV files.

    Args:
        data_path (str, optional): The path to the input data directory. Defaults to "../input/".
        crop_folder (str, optional): Path to the folder containing cropped images. Defaults to None.
        explode (bool, optional): Whether to explode the DataFrame. Defaults to True.

    Returns:
        pd.DataFrame: A DataFrame containing the prepared data.
    """
    df = prepare_data(data_path)
    df = df[df["weighting"] == "T2/STIR"].reset_index(drop=True)

    df_train = pd.read_csv(data_path + "train.csv")
    df_train = df_train[["study_id"] + CLASSES_SCS]

    for c in df_train.columns[1:]:
        df_train[c] = df_train[c].map(dict(zip(SEVERITIES, [0, 1, 2]))).fillna(-1)
    df_train = df_train.astype(int)
    df = df.merge(df_train, on="study_id", how="left")

    if explode:
        df = (
            df.explode(["condition", "level", "coords"]).dropna().reset_index(drop=True)
        )
        df["target"] = df.apply(get_target, axis=1)
        df.drop(CLASSES_SCS, axis=1, inplace=True)

        if crop_folder is not None:
            df["img_path"] = (
                df["study_id"].astype(str) + "_" + df["series_id"].astype(str)
            )
            lvl = df["level"].apply(lambda x: re.sub("/", "_", x.lower()))
            df["img_path"] = crop_folder + df["img_path"] + "_" + lvl + ".npy"

        try:
            df_coords_crops = pd.read_csv(crop_folder + "df_injury_coords.csv")
            df = df.merge(
                df_coords_crops, on=["study_id", "series_id", "level"], how="left"
            )
            df["side"] = df["side"].fillna("Center")

        except FileNotFoundError:
            df["side"] = "Center"
            pass

    return df


def prepare_data_lvl2(data_path="../input/"):
    """
    Prepares data for the Level 2 task by loading and processing CSV files.

    Args:
        data_path (str, optional): The path to the input data directory. Defaults to "../input/".

    Returns:
        pd.DataFrame: A DataFrame containing the prepared data with additional columns for targets.
    """
    df = pd.read_csv(data_path + "train_series_descriptions.csv")
    df = df.groupby("study_id").agg(list)

    df_train = pd.read_csv(data_path + "train.csv")
    for c in df_train.columns[1:]:
        df_train[c] = df_train[c].map(dict(zip(SEVERITIES, [0, 1, 2]))).fillna(-1)
    df_train = df_train.astype(int)
    df = df.merge(df_train, on="study_id", how="left")

    return df


def get_target_crop(row):
    """
    Extracts the target values for cropping from a row based on the condition and level.

    Args:
        row (pandas Series): A row from a DataFrame containing 'level' and relevant target columns.

    Returns:
        list or np.nan: A list of target values if found, otherwise np.nan.
    """
    la, lb = row.level.lower().split("/")
    try:
        labels = []
        for c in CLASSES_CROP:
            labels.append(row[f"{c}_{la}_{lb}"])
        return labels
    except KeyError:
        return np.nan


def simplify_coords(x):
    """
    Simplifies the coordinates by calculating the median of the x-coordinates for each side.

    Args:
        x (pandas Series): A row from a DataFrame containing 'condition' and 'coords' columns.

    Returns:
        np.array: An array of simplified coordinates for the left, center, and right sides.
    """
    coords = -1 * np.ones(3)
    d = defaultdict(list)

    if isinstance(x.condition, float):
        return coords

    for i, c in enumerate(x.condition):
        side = c.split(" ")[0]
        side = "Center" if side == "Spinal" else side
        d[side].append(x.coords[i][0])

    for k in d:
        d[k] = int(np.median(d[k]))

    coords[0] = d.get("Right", -1)
    coords[1] = d.get("Center", -1)
    coords[2] = d.get("Left", -1)
    return coords


def prepare_data_crop(data_path, crop_folder=None, axial=False):
    """
    Prepares data for the crop task by loading and processing CSV files.

    Args:
        data_path (str): The path to the input data directory.
        crop_folder (str, optional): Path to the folder containing cropped images. Defaults to None.
        axial (bool, optional): Whether to use axial orientation. Defaults to False.

    Returns:
        pd.DataFrame: A DataFrame containing the prepared data.
    """
    df = prepare_data(data_path)

    df["level"] = [
        ["L1/L2", "L2/L3", "L3/L4", "L4/L5", "L5/S1"] for _ in range(len(df))
    ]
    df["side"] = "Center"

    if axial:
        df = df[df["orient"] == "Axial"].reset_index(drop=True)
    else:
        df = df[df["orient"] == "Sagittal"].reset_index(drop=True)

    df["coords"] = df.apply(simplify_coords, axis=1)
    df["coords"] = df["coords"].apply(
        lambda x: int(np.mean(x[x > -1])) if x.max() > -1 else -1
    )
    df.drop("condition", axis=1, inplace=True)

    df_train = pd.read_csv(data_path + "train.csv")
    for c in df_train.columns[1:]:
        df_train[c] = df_train[c].map(dict(zip(SEVERITIES, [0, 1, 2]))).fillna(-1)
    df_train = df_train.astype(int)
    df = df.merge(df_train, on="study_id", how="left")

    df = df.explode("level").reset_index(drop=True)
    df["target"] = df.apply(get_target_crop, axis=1)
    df.drop(df_train.columns[1:], axis=1, inplace=True)

    if crop_folder is not None:
        df["img_path"] = df["study_id"].astype(str) + "_" + df["series_id"].astype(str)
        lvl = df["level"].apply(lambda x: re.sub("/", "_", x.lower()))
        df["img_path"] = crop_folder + df["img_path"] + "_" + lvl + ".npy"

    return df


def get_coords_target(row, axial=False, relative=False):
    """
    Extracts the target coordinates from a row based on the side or level.

    Args:
        row (pandas Series): A row from a DataFrame containing 'side', 'x', 'y',
                             'level', 'relative_x', and 'relative_y' columns.
        axial (bool, optional): Whether to use axial orientation. Defaults to False.
        relative (bool, optional): Whether to use relative coordinates. Defaults to False.

    Returns:
        np.array: An array of target coordinates.
    """
    if axial:
        target = np.zeros((2, 2), dtype=float) - 1
        for s, side in enumerate(["Left", "Right"]):
            i = row.side.index(side)
            if relative:
                target[s, 0] = row.relative_x[i]
                target[s, 1] = row.relative_y[i]
            else:
                target[s, 0] = row.x[i]
                target[s, 1] = row.y[i]
    else:
        target = np.zeros((5, 2), dtype=float) - 1
        for lvl, level in enumerate(LEVELS):
            if level in row.level:
                i = row.level.index(level)
                if relative:
                    target[lvl, 0] = row.relative_x[i]
                    target[lvl, 1] = row.relative_y[i]
                else:
                    target[lvl, 0] = row.x[i]
                    target[lvl, 1] = row.y[i]
    return target


def prepare_coords_data(data_path="../input/coords/", axial=False, use_ext=True):
    """
    Prepares coordinate data by loading and processing CSV files.

    Args:
        data_path (str, optional): Path to the input data directory. Defaults to "../input/coords/".
        axial (bool, optional): Whether to use axial orientation. Defaults to False.
        use_ext (bool, optional): Whether to use external data. Defaults to True.

    Returns:
        pd.DataFrame: A DataFrame containing the prepared coordinate data.
    """
    if axial:
        df = pd.read_csv(data_path + "coords_ax.csv")
        df = df.groupby(["study_id", "series_id", "img_path"]).agg(list).reset_index()

        df["target"] = df.apply(
            lambda x: get_coords_target(x, axial=True, relative=False), axis=1
        ).tolist()
        df["target_rel"] = df.apply(
            lambda x: get_coords_target(x, axial=True, relative=True), axis=1
        ).tolist()
    else:
        if use_ext:
            df = pd.read_csv(data_path + "coords_pretrain.csv")
            df["img_path"] = (
                data_path + "data/processed_" + df["source"] + "_jpgs/" + df["filename"]
            )
            df = df.sort_values(["source", "filename", "level"], ignore_index=True)
            df = df.rename(columns={"source": "study_id", "filename": "series_id"})

            df = pd.concat(
                [df, pd.read_csv(data_path + "coords_comp.csv")], ignore_index=True
            )
        else:
            df = pd.read_csv(data_path + "coords_comp_3ch.csv")

        df = df.groupby(["study_id", "series_id", "img_path"]).agg(list).reset_index()

        df["target"] = df.apply(
            lambda x: get_coords_target(x, relative=False), axis=1
        ).tolist()
        df["target_rel"] = df.apply(
            lambda x: get_coords_target(x, relative=True), axis=1
        ).tolist()
    return df