mapreader 1.3.10__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mapreader-1.3.10 → mapreader-1.4.0}/PKG-INFO +2 -2
- {mapreader-1.3.10 → mapreader-1.4.0}/README.md +1 -1
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/_version.py +3 -3
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/annotate/annotator.py +40 -39
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/classify/classifier.py +0 -4
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/classify/datasets.py +74 -64
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/classify/load_annotations.py +91 -79
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/download/downloader.py +3 -3
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/download/sheet_downloader.py +269 -494
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/download/tile_loading.py +1 -1
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/download/tile_merging.py +4 -6
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/load/images.py +291 -287
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/process/context_post_process.py +30 -11
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/process/occlusion_analysis.py +21 -20
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/spot_text/deepsolo_runner.py +10 -122
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/spot_text/dptext_detr_runner.py +23 -9
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/spot_text/maptext_runner.py +11 -122
- mapreader-1.4.0/mapreader/spot_text/rec_runner_base.py +315 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/spot_text/runner_base.py +55 -94
- mapreader-1.4.0/mapreader/utils/load_frames.py +125 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader.egg-info/PKG-INFO +2 -2
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader.egg-info/SOURCES.txt +4 -1
- mapreader-1.4.0/tests/test_utils_load_frames.py +206 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/LICENSE +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/__main__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/annotate/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/annotate/utils.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/classify/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/classify/custom_models.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/download/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/download/data_structures.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/download/downloader_utils.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/load/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/load/geo_utils.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/load/loader.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/process/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/process/process.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/spot_text/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/utils/__init__.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/utils/compute_and_save_stats.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader/utils/slice_parallel.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader.egg-info/dependency_links.txt +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader.egg-info/entry_points.txt +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader.egg-info/not-zip-safe +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader.egg-info/requires.txt +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/mapreader.egg-info/top_level.txt +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/setup.cfg +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/setup.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/tests/test_geo_pipeline.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/tests/test_import.py +0 -0
- {mapreader-1.3.10 → mapreader-1.4.0}/versioneer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mapreader
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: A computer vision pipeline for the semantic exploration of maps/images at scale
|
|
5
5
|
Home-page: https://github.com/Living-with-machines/MapReader
|
|
6
6
|
Download-URL: https://github.com/Living-with-machines/MapReader/archive/refs/heads/main.zip
|
|
@@ -69,7 +69,7 @@ Requires-Dist: flake8<8.0.0,>=6.0.0; extra == "dev"
|
|
|
69
69
|
|
|
70
70
|
# MapReader
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
**MapReader is a computer vision pipeline for exploring and analyzing images at scale.**
|
|
73
73
|
|
|
74
74
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
75
75
|
[](#contributors)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MapReader
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**MapReader is a computer vision pipeline for exploring and analyzing images at scale.**
|
|
4
4
|
|
|
5
5
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
6
6
|
[](#contributors)
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2024-
|
|
11
|
+
"date": "2024-09-12T19:16:25+0100",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.
|
|
14
|
+
"full-revisionid": "2651e3de148e0069aa93151268b18534108dbe91",
|
|
15
|
+
"version": "1.4.0"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -4,13 +4,15 @@ import functools
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
|
+
import pathlib
|
|
7
8
|
import random
|
|
9
|
+
import re
|
|
8
10
|
import string
|
|
9
11
|
import warnings
|
|
10
|
-
from ast import literal_eval
|
|
11
12
|
from itertools import product
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
|
|
15
|
+
import geopandas as gpd
|
|
14
16
|
import ipywidgets as widgets
|
|
15
17
|
import numpy as np
|
|
16
18
|
import pandas as pd
|
|
@@ -18,6 +20,8 @@ from IPython.display import clear_output, display
|
|
|
18
20
|
from numpy import array_split
|
|
19
21
|
from PIL import Image, ImageOps
|
|
20
22
|
|
|
23
|
+
from mapreader.utils.load_frames import load_from_csv, load_from_geojson
|
|
24
|
+
|
|
21
25
|
from ..load.loader import load_patches
|
|
22
26
|
|
|
23
27
|
warnings.filterwarnings("ignore", category=UserWarning)
|
|
@@ -33,10 +37,10 @@ class Annotator:
|
|
|
33
37
|
|
|
34
38
|
Parameters
|
|
35
39
|
----------
|
|
36
|
-
patch_df : str
|
|
37
|
-
Path to a CSV file or a pandas DataFrame containing patch data, by default None
|
|
38
|
-
parent_df : str
|
|
39
|
-
Path to a CSV file or a pandas DataFrame containing parent data, by default None
|
|
40
|
+
patch_df : str, pathlib.Path, pd.DataFrame or gpd.GeoDataFrame or None, optional
|
|
41
|
+
Path to a CSV/geojson file or a pandas DataFrame/ geopandas GeoDataFrame containing patch data, by default None
|
|
42
|
+
parent_df : str, pathlib.Path, pd.DataFrame or gpd.GeoDataFrame or None, optional
|
|
43
|
+
Path to a CSV/geojson file or a pandas DataFrame/ geopandas GeoDataFrame containing parent data, by default None
|
|
40
44
|
labels : list, optional
|
|
41
45
|
List of labels for annotation, by default None
|
|
42
46
|
patch_paths : str or None, optional
|
|
@@ -89,7 +93,7 @@ class Annotator:
|
|
|
89
93
|
FileNotFoundError
|
|
90
94
|
If the provided patch_df or parent_df file path does not exist
|
|
91
95
|
ValueError
|
|
92
|
-
If patch_df or parent_df is not a valid path to a CSV file or a pandas DataFrame
|
|
96
|
+
If patch_df or parent_df is not a valid path to a CSV/geojson file or a pandas DataFrame or a geopandas GeoDataFrame
|
|
93
97
|
If patch_df or patch_paths is not provided
|
|
94
98
|
If the DataFrame does not have the required columns
|
|
95
99
|
If sortby is not a string or None
|
|
@@ -100,8 +104,8 @@ class Annotator:
|
|
|
100
104
|
|
|
101
105
|
def __init__(
|
|
102
106
|
self,
|
|
103
|
-
patch_df: str | pd.DataFrame | None = None,
|
|
104
|
-
parent_df: str | pd.DataFrame | None = None,
|
|
107
|
+
patch_df: str | pathlib.Path | pd.DataFrame | gpd.GeoDataFrame | None = None,
|
|
108
|
+
parent_df: str | pathlib.Path | pd.DataFrame | gpd.GeoDataFrame | None = None,
|
|
105
109
|
labels: list = None,
|
|
106
110
|
patch_paths: str | None = None,
|
|
107
111
|
parent_paths: str | None = None,
|
|
@@ -127,36 +131,40 @@ class Annotator:
|
|
|
127
131
|
if labels is None:
|
|
128
132
|
labels = []
|
|
129
133
|
if patch_df is not None:
|
|
130
|
-
if isinstance(patch_df, str):
|
|
131
|
-
if
|
|
132
|
-
patch_df =
|
|
134
|
+
if isinstance(patch_df, (str, pathlib.Path)):
|
|
135
|
+
if re.search(r"\..?sv$", str(patch_df)):
|
|
136
|
+
patch_df = load_from_csv(
|
|
133
137
|
patch_df,
|
|
134
|
-
|
|
135
|
-
sep=delimiter,
|
|
138
|
+
delimiter=delimiter,
|
|
136
139
|
)
|
|
140
|
+
elif re.search(r"\..*?json$", str(patch_df)):
|
|
141
|
+
patch_df = load_from_geojson(patch_df)
|
|
137
142
|
else:
|
|
138
|
-
raise
|
|
139
|
-
|
|
143
|
+
raise ValueError(
|
|
144
|
+
"[ERROR] ``patch_df`` must be a path to a CSV/TSV/etc or geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
145
|
+
)
|
|
146
|
+
elif not isinstance(patch_df, pd.DataFrame):
|
|
140
147
|
raise ValueError(
|
|
141
|
-
"[ERROR] ``patch_df`` must be a path to a
|
|
148
|
+
"[ERROR] ``patch_df`` must be a path to a CSV/TSV/etc or geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
142
149
|
)
|
|
143
|
-
patch_df = self._eval_df(patch_df) # eval tuples/lists in df
|
|
144
150
|
|
|
145
151
|
if parent_df is not None:
|
|
146
|
-
if isinstance(parent_df, str):
|
|
147
|
-
if
|
|
148
|
-
parent_df =
|
|
152
|
+
if isinstance(parent_df, (str, pathlib.Path)):
|
|
153
|
+
if re.search(r"\..?sv$", str(parent_df)):
|
|
154
|
+
parent_df = load_from_csv(
|
|
149
155
|
parent_df,
|
|
150
|
-
|
|
151
|
-
sep=delimiter,
|
|
156
|
+
delimiter=delimiter,
|
|
152
157
|
)
|
|
158
|
+
elif re.search(r"\..*?json$", str(parent_df)):
|
|
159
|
+
parent_df = load_from_geojson(parent_df)
|
|
153
160
|
else:
|
|
154
|
-
raise
|
|
161
|
+
raise ValueError(
|
|
162
|
+
"[ERROR] ``parent_df`` must be a path to a CSV/TSV/etc or geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
163
|
+
)
|
|
155
164
|
if not isinstance(parent_df, pd.DataFrame):
|
|
156
165
|
raise ValueError(
|
|
157
|
-
"[ERROR] ``parent_df`` must be a path to a
|
|
166
|
+
"[ERROR] ``parent_df`` must be a path to a CSV/TSV/etc or geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
158
167
|
)
|
|
159
|
-
parent_df = self._eval_df(parent_df) # eval tuples/lists in df
|
|
160
168
|
|
|
161
169
|
if patch_df is None:
|
|
162
170
|
# If we don't get patch data provided, we'll use the patches and parents to create the dataframes
|
|
@@ -354,18 +362,9 @@ class Annotator:
|
|
|
354
362
|
|
|
355
363
|
return parent_df, patch_df
|
|
356
364
|
|
|
357
|
-
@staticmethod
|
|
358
|
-
def _eval_df(df):
|
|
359
|
-
for col in df.columns:
|
|
360
|
-
try:
|
|
361
|
-
df[col] = df[col].apply(literal_eval)
|
|
362
|
-
except (ValueError, TypeError, SyntaxError):
|
|
363
|
-
pass
|
|
364
|
-
return df
|
|
365
|
-
|
|
366
365
|
@staticmethod
|
|
367
366
|
def _load_annotations(
|
|
368
|
-
patch_df: pd.DataFrame,
|
|
367
|
+
patch_df: pd.DataFrame | gpd.GeoDataFrame,
|
|
369
368
|
annotations_file: str,
|
|
370
369
|
labels: list,
|
|
371
370
|
label_col: str,
|
|
@@ -375,7 +374,7 @@ class Annotator:
|
|
|
375
374
|
|
|
376
375
|
Parameters
|
|
377
376
|
----------
|
|
378
|
-
patch_df : pd.DataFrame
|
|
377
|
+
patch_df : pd.DataFrame or gpd.GeoDataFrame
|
|
379
378
|
Current patch dataframe.
|
|
380
379
|
annotations_file : str
|
|
381
380
|
Name of the annotations file
|
|
@@ -387,7 +386,9 @@ class Annotator:
|
|
|
387
386
|
Delimiter used in CSV files
|
|
388
387
|
|
|
389
388
|
"""
|
|
390
|
-
existing_annotations =
|
|
389
|
+
existing_annotations = load_from_csv(
|
|
390
|
+
annotations_file, index_col=0, sep=delimiter
|
|
391
|
+
)
|
|
391
392
|
|
|
392
393
|
if label_col not in existing_annotations.columns:
|
|
393
394
|
raise ValueError(
|
|
@@ -995,8 +996,8 @@ class Annotator:
|
|
|
995
996
|
|
|
996
997
|
Returns
|
|
997
998
|
-------
|
|
998
|
-
pandas.DataFrame
|
|
999
|
-
A
|
|
999
|
+
pandas.DataFrame or geopandas.GeoDataFrame
|
|
1000
|
+
A DataFrame/GeoDataFrame containing the labelled images and their associated
|
|
1000
1001
|
label index.
|
|
1001
1002
|
"""
|
|
1002
1003
|
filtered_df = self.patch_df[self.patch_df[self.label_col].notna()].copy(
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
|
-
|
|
5
|
+
import pathlib
|
|
6
|
+
import re
|
|
6
7
|
from itertools import product
|
|
7
8
|
from typing import Callable
|
|
8
9
|
|
|
10
|
+
import geopandas as gpd
|
|
9
11
|
import matplotlib.pyplot as plt
|
|
10
12
|
import pandas as pd
|
|
11
13
|
import torch
|
|
@@ -24,14 +26,16 @@ except ImportError:
|
|
|
24
26
|
)
|
|
25
27
|
parhugin_installed = False
|
|
26
28
|
|
|
29
|
+
from mapreader.utils.load_frames import eval_dataframe, load_from_csv, load_from_geojson
|
|
30
|
+
|
|
27
31
|
|
|
28
32
|
class PatchDataset(Dataset):
|
|
29
33
|
"""A PyTorch Dataset class for loading image patches from a DataFrame.
|
|
30
34
|
|
|
31
35
|
Parameters
|
|
32
36
|
----------
|
|
33
|
-
patch_df : pandas.DataFrame or
|
|
34
|
-
DataFrame or path to
|
|
37
|
+
patch_df : str or pathlib.Path or pandas.DataFrame or gpd.GeoDataFrame
|
|
38
|
+
DataFrame or path to CSV/TSV/geojson file containing the paths to image patches and their labels.
|
|
35
39
|
transform : Union[str, transforms.Compose, Callable]
|
|
36
40
|
The transform to use on the image.
|
|
37
41
|
A string can be used to call default transforms - options are "train", "test" or "val".
|
|
@@ -39,7 +43,7 @@ class PatchDataset(Dataset):
|
|
|
39
43
|
and performs image transformations can be used.
|
|
40
44
|
At minimum, transform should be ``torchvision.transforms.ToTensor()``.
|
|
41
45
|
delimiter : str, optional
|
|
42
|
-
The delimiter to use when reading the
|
|
46
|
+
The delimiter to use when reading the CSV/TSV file. By default ``","``.
|
|
43
47
|
patch_paths_col : str, optional
|
|
44
48
|
The name of the column in the DataFrame containing the image paths. Default is "image_path".
|
|
45
49
|
label_col : str, optional
|
|
@@ -51,7 +55,7 @@ class PatchDataset(Dataset):
|
|
|
51
55
|
|
|
52
56
|
Attributes
|
|
53
57
|
----------
|
|
54
|
-
patch_df : pandas.DataFrame
|
|
58
|
+
patch_df : pandas.DataFrame or gpd.GeoDataFrame
|
|
55
59
|
DataFrame containing the paths to image patches and their labels.
|
|
56
60
|
label_col : str
|
|
57
61
|
The name of the column containing the image labels.
|
|
@@ -91,7 +95,7 @@ class PatchDataset(Dataset):
|
|
|
91
95
|
|
|
92
96
|
def __init__(
|
|
93
97
|
self,
|
|
94
|
-
patch_df: pd.DataFrame |
|
|
98
|
+
patch_df: str | pathlib.Path | pd.DataFrame | gpd.GeoDataFrame,
|
|
95
99
|
transform: str | (transforms.Compose | Callable),
|
|
96
100
|
delimiter: str = ",",
|
|
97
101
|
patch_paths_col: str | None = "image_path",
|
|
@@ -102,19 +106,22 @@ class PatchDataset(Dataset):
|
|
|
102
106
|
if isinstance(patch_df, pd.DataFrame):
|
|
103
107
|
self.patch_df = patch_df
|
|
104
108
|
|
|
105
|
-
elif isinstance(patch_df, str):
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
patch_df =
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
elif isinstance(patch_df, (str, pathlib.Path)):
|
|
110
|
+
print(f'[INFO] Reading "{patch_df}".')
|
|
111
|
+
if re.search(r"\..?sv$", str(patch_df)):
|
|
112
|
+
self.patch_df = load_from_csv(
|
|
113
|
+
patch_df,
|
|
114
|
+
delimiter=delimiter,
|
|
115
|
+
)
|
|
116
|
+
elif re.search(r"\..*?json$", str(patch_df)):
|
|
117
|
+
self.patch_df = load_from_geojson(patch_df)
|
|
112
118
|
else:
|
|
113
|
-
raise ValueError(
|
|
114
|
-
|
|
119
|
+
raise ValueError(
|
|
120
|
+
"[ERROR] ``patch_df`` must be a path to a CSV/geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
121
|
+
)
|
|
115
122
|
else:
|
|
116
123
|
raise ValueError(
|
|
117
|
-
"[ERROR]
|
|
124
|
+
"[ERROR] ``patch_df`` must be a path to a CSV/geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
118
125
|
)
|
|
119
126
|
|
|
120
127
|
# force index to be image_id
|
|
@@ -133,7 +140,7 @@ class PatchDataset(Dataset):
|
|
|
133
140
|
if self.label_col:
|
|
134
141
|
if self.label_col not in self.patch_df.columns:
|
|
135
142
|
raise ValueError(
|
|
136
|
-
f"[ERROR] Label column ({label_col}) not in
|
|
143
|
+
f"[ERROR] Label column ({label_col}) not in DataFrame."
|
|
137
144
|
)
|
|
138
145
|
else:
|
|
139
146
|
self.unique_labels = self.patch_df[self.label_col].unique().tolist()
|
|
@@ -142,14 +149,14 @@ class PatchDataset(Dataset):
|
|
|
142
149
|
if self.label_index_col not in self.patch_df.columns:
|
|
143
150
|
if self.label_col:
|
|
144
151
|
print(
|
|
145
|
-
f"[INFO] Label index column ({label_index_col}) not in
|
|
152
|
+
f"[INFO] Label index column ({label_index_col}) not in DataFrame. Creating column."
|
|
146
153
|
)
|
|
147
154
|
self.patch_df[self.label_index_col] = self.patch_df[
|
|
148
155
|
self.label_col
|
|
149
156
|
].apply(self._get_label_index)
|
|
150
157
|
else:
|
|
151
158
|
raise ValueError(
|
|
152
|
-
f"[ERROR] Label index column ({label_index_col}) not in
|
|
159
|
+
f"[ERROR] Label index column ({label_index_col}) not in DataFrame."
|
|
153
160
|
)
|
|
154
161
|
|
|
155
162
|
if isinstance(transform, str):
|
|
@@ -162,15 +169,6 @@ class PatchDataset(Dataset):
|
|
|
162
169
|
else:
|
|
163
170
|
self.transform = transform
|
|
164
171
|
|
|
165
|
-
@staticmethod
|
|
166
|
-
def _eval_df(df):
|
|
167
|
-
for col in df.columns:
|
|
168
|
-
try:
|
|
169
|
-
df[col] = df[col].apply(literal_eval)
|
|
170
|
-
except (ValueError, TypeError, SyntaxError):
|
|
171
|
-
pass
|
|
172
|
-
return df
|
|
173
|
-
|
|
174
172
|
def __len__(self) -> int:
|
|
175
173
|
"""
|
|
176
174
|
Return the length of the dataset.
|
|
@@ -393,15 +391,15 @@ class PatchContextDataset(PatchDataset):
|
|
|
393
391
|
|
|
394
392
|
Parameters
|
|
395
393
|
----------
|
|
396
|
-
patch_df : pandas.DataFrame or
|
|
397
|
-
DataFrame or path to
|
|
398
|
-
total_df : pandas.DataFrame or
|
|
399
|
-
DataFrame or path to
|
|
394
|
+
patch_df : str or pathlib.Path or pandas.DataFrame or gpd.GeoDataFrame
|
|
395
|
+
DataFrame or path to CSV/TSV/geojson file containing the paths to image patches and their labels.
|
|
396
|
+
total_df : str or pathlib.Path or pandas.DataFrame or gpd.GeoDataFrame
|
|
397
|
+
DataFrame or path to CSV/TSV/geojson file containing the paths to all images and their labels.
|
|
400
398
|
transform : str
|
|
401
399
|
Torchvision transform to be applied to context images.
|
|
402
400
|
Either "train" or "val".
|
|
403
401
|
delimiter : str
|
|
404
|
-
The delimiter to use when reading the
|
|
402
|
+
The delimiter to use when reading the CSV/TSV file. By default ``","``.
|
|
405
403
|
patch_paths_col : str, optional
|
|
406
404
|
The name of the column in the DataFrame containing the image paths. Default is "image_path".
|
|
407
405
|
label_col : str, optional
|
|
@@ -421,8 +419,8 @@ class PatchContextDataset(PatchDataset):
|
|
|
421
419
|
|
|
422
420
|
Attributes
|
|
423
421
|
----------
|
|
424
|
-
patch_df : pandas.DataFrame
|
|
425
|
-
|
|
422
|
+
patch_df : pandas.DataFrame or gpd.GeoDataFrame
|
|
423
|
+
DataFrame with columns representing image paths, labels,
|
|
426
424
|
and object bounding boxes.
|
|
427
425
|
label_col : str
|
|
428
426
|
The name of the column containing the image labels.
|
|
@@ -445,8 +443,8 @@ class PatchContextDataset(PatchDataset):
|
|
|
445
443
|
|
|
446
444
|
def __init__(
|
|
447
445
|
self,
|
|
448
|
-
patch_df: pd.DataFrame |
|
|
449
|
-
total_df: pd.DataFrame |
|
|
446
|
+
patch_df: str | pathlib.Path | pd.DataFrame | gpd.GeoDataFrame,
|
|
447
|
+
total_df: str | pathlib.Path | pd.DataFrame | gpd.GeoDataFrame,
|
|
450
448
|
transform: str,
|
|
451
449
|
delimiter: str = ",",
|
|
452
450
|
patch_paths_col: str | None = "image_path",
|
|
@@ -459,35 +457,42 @@ class PatchContextDataset(PatchDataset):
|
|
|
459
457
|
):
|
|
460
458
|
if isinstance(patch_df, pd.DataFrame):
|
|
461
459
|
self.patch_df = patch_df
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
460
|
+
elif isinstance(patch_df, (str, pathlib.Path)):
|
|
461
|
+
print(f'[INFO] Reading "{patch_df}".')
|
|
462
|
+
if re.search(r"\..?sv$", str(patch_df)):
|
|
463
|
+
self.patch_df = load_from_csv(
|
|
464
|
+
patch_df,
|
|
465
|
+
delimiter=delimiter,
|
|
466
|
+
)
|
|
467
|
+
elif re.search(r"\..*?json$", str(patch_df)):
|
|
468
|
+
self.patch_df = load_from_geojson(patch_df)
|
|
469
469
|
else:
|
|
470
|
-
raise ValueError(
|
|
471
|
-
|
|
470
|
+
raise ValueError(
|
|
471
|
+
"[ERROR] ``patch_df`` must be a path to a CSV/geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
472
|
+
)
|
|
472
473
|
else:
|
|
473
474
|
raise ValueError(
|
|
474
|
-
"[ERROR]
|
|
475
|
+
"[ERROR] ``patch_df`` must be a path to a CSV/geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
475
476
|
)
|
|
476
477
|
|
|
477
478
|
if isinstance(total_df, pd.DataFrame):
|
|
478
479
|
self.total_df = total_df
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if
|
|
482
|
-
total_df =
|
|
483
|
-
|
|
484
|
-
|
|
480
|
+
elif isinstance(total_df, (str, pathlib.Path)):
|
|
481
|
+
print(f'[INFO] Reading "{total_df}".')
|
|
482
|
+
if re.search(r"\..?sv$", str(total_df)):
|
|
483
|
+
self.total_df = load_from_csv(
|
|
484
|
+
total_df,
|
|
485
|
+
delimiter=delimiter,
|
|
486
|
+
)
|
|
487
|
+
elif re.search(r"\..*?json$", str(total_df)):
|
|
488
|
+
self.total_df = load_from_geojson(total_df)
|
|
485
489
|
else:
|
|
486
|
-
raise ValueError(
|
|
487
|
-
|
|
490
|
+
raise ValueError(
|
|
491
|
+
"[ERROR] ``total_df`` must be a path to a CSV/geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
492
|
+
)
|
|
488
493
|
else:
|
|
489
494
|
raise ValueError(
|
|
490
|
-
"[ERROR]
|
|
495
|
+
"[ERROR] ``total_df`` must be a path to a CSV/geojson file or a pandas DataFrame or a geopandas GeoDataFrame."
|
|
491
496
|
)
|
|
492
497
|
|
|
493
498
|
# force index to be image_id
|
|
@@ -506,18 +511,23 @@ class PatchContextDataset(PatchDataset):
|
|
|
506
511
|
if self.label_col:
|
|
507
512
|
if self.label_col not in self.patch_df.columns:
|
|
508
513
|
raise ValueError(
|
|
509
|
-
f"[ERROR] Label column ({self.label_col}) not in
|
|
514
|
+
f"[ERROR] Label column ({self.label_col}) not in DataFrame."
|
|
510
515
|
)
|
|
511
516
|
self.unique_labels = self.patch_df[self.label_col].unique().tolist()
|
|
512
517
|
|
|
513
518
|
if self.label_index_col:
|
|
514
519
|
if self.label_index_col not in self.patch_df.columns:
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
self.
|
|
520
|
-
|
|
520
|
+
if self.label_col:
|
|
521
|
+
print(
|
|
522
|
+
f"[INFO] Label index column ({label_index_col}) not in DataFrame. Creating column."
|
|
523
|
+
)
|
|
524
|
+
self.patch_df[self.label_index_col] = self.patch_df[
|
|
525
|
+
self.label_col
|
|
526
|
+
].apply(self._get_label_index)
|
|
527
|
+
else:
|
|
528
|
+
raise ValueError(
|
|
529
|
+
f"[ERROR] Label index column ({label_index_col}) not in DataFrame."
|
|
530
|
+
)
|
|
521
531
|
|
|
522
532
|
if isinstance(transform, str):
|
|
523
533
|
if transform in ["train", "val", "test"]:
|
|
@@ -646,7 +656,7 @@ class PatchContextDataset(PatchDataset):
|
|
|
646
656
|
)
|
|
647
657
|
if "parent_id" not in total_df.columns:
|
|
648
658
|
total_df["parent_id"] = total_df.index.map(lambda x: x.split("#")[1])
|
|
649
|
-
total_df =
|
|
659
|
+
total_df = eval_dataframe(total_df)
|
|
650
660
|
|
|
651
661
|
if not all(
|
|
652
662
|
[col in total_df.columns for col in ["min_x", "min_y", "max_x", "max_y"]]
|