mapreader 1.3.6__tar.gz → 1.3.8__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.
Files changed (49) hide show
  1. {mapreader-1.3.6 → mapreader-1.3.8}/PKG-INFO +1 -1
  2. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/_version.py +3 -3
  3. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/annotate/annotator.py +62 -18
  4. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/classify/load_annotations.py +25 -3
  5. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader.egg-info/PKG-INFO +1 -1
  6. {mapreader-1.3.6 → mapreader-1.3.8}/LICENSE +0 -0
  7. {mapreader-1.3.6 → mapreader-1.3.8}/README.md +0 -0
  8. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/__init__.py +0 -0
  9. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/__main__.py +0 -0
  10. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/annotate/__init__.py +0 -0
  11. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/annotate/utils.py +0 -0
  12. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/classify/__init__.py +0 -0
  13. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/classify/classifier.py +0 -0
  14. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/classify/custom_models.py +0 -0
  15. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/classify/datasets.py +0 -0
  16. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/download/__init__.py +0 -0
  17. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/download/data_structures.py +0 -0
  18. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/download/downloader.py +0 -0
  19. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/download/downloader_utils.py +0 -0
  20. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/download/sheet_downloader.py +0 -0
  21. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/download/tile_loading.py +0 -0
  22. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/download/tile_merging.py +0 -0
  23. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/load/__init__.py +0 -0
  24. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/load/geo_utils.py +0 -0
  25. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/load/images.py +0 -0
  26. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/load/loader.py +0 -0
  27. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/process/__init__.py +0 -0
  28. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/process/context_post_process.py +0 -0
  29. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/process/occlusion_analysis.py +0 -0
  30. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/process/process.py +0 -0
  31. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/spot_text/__init__.py +0 -0
  32. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/spot_text/deepsolo_runner.py +0 -0
  33. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/spot_text/dptext_detr_runner.py +0 -0
  34. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/spot_text/maptext_runner.py +0 -0
  35. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/spot_text/runner_base.py +0 -0
  36. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/utils/__init__.py +0 -0
  37. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/utils/compute_and_save_stats.py +0 -0
  38. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader/utils/slice_parallel.py +0 -0
  39. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader.egg-info/SOURCES.txt +0 -0
  40. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader.egg-info/dependency_links.txt +0 -0
  41. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader.egg-info/entry_points.txt +0 -0
  42. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader.egg-info/not-zip-safe +0 -0
  43. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader.egg-info/requires.txt +0 -0
  44. {mapreader-1.3.6 → mapreader-1.3.8}/mapreader.egg-info/top_level.txt +0 -0
  45. {mapreader-1.3.6 → mapreader-1.3.8}/setup.cfg +0 -0
  46. {mapreader-1.3.6 → mapreader-1.3.8}/setup.py +0 -0
  47. {mapreader-1.3.6 → mapreader-1.3.8}/tests/test_geo_pipeline.py +0 -0
  48. {mapreader-1.3.6 → mapreader-1.3.8}/tests/test_import.py +0 -0
  49. {mapreader-1.3.6 → mapreader-1.3.8}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapreader
3
- Version: 1.3.6
3
+ Version: 1.3.8
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
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-08-09T14:10:51+0100",
11
+ "date": "2024-08-12T15:36:33+0100",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "5ba6c479346a58dc6d937776c895b30d6c21f833",
15
- "version": "1.3.6"
14
+ "full-revisionid": "6eed2e27675ba1e8f50986e52d7ba5c25d95dc49",
15
+ "version": "1.3.8"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -55,6 +55,8 @@ class Annotator:
55
55
  Name of the column in which labels are stored in patch DataFrame, by default "label"
56
56
  show_context : bool, optional
57
57
  Whether to show context when loading patches, by default False
58
+ border : bool, optional
59
+ Whether to add a border around the central patch when showing context, by default False
58
60
  auto_save : bool, optional
59
61
  Whether to automatically save annotations, by default True
60
62
  delimiter : str, optional
@@ -108,6 +110,7 @@ class Annotator:
108
110
  patch_paths_col: str = "image_path",
109
111
  label_col: str = "label",
110
112
  show_context: bool = False,
113
+ border: bool = False,
111
114
  auto_save: bool = True,
112
115
  delimiter: str = ",",
113
116
  sortby: str | None = None,
@@ -255,6 +258,7 @@ class Annotator:
255
258
  self.patch_paths_col = patch_paths_col
256
259
  self.annotations_file = annotations_file
257
260
  self.show_context = show_context
261
+ self.border = border
258
262
  self.auto_save = auto_save
259
263
  self.username = username
260
264
  self.task_name = task_name
@@ -452,7 +456,7 @@ class Annotator:
452
456
 
453
457
  self.navbox = widgets.VBox([widgets.HBox([prev_btn, next_btn])])
454
458
 
455
- def get_queue(
459
+ def _get_queue(
456
460
  self, as_type: str | None = "list"
457
461
  ) -> list[int] | (pd.Index | pd.Series):
458
462
  """
@@ -520,7 +524,7 @@ class Annotator:
520
524
  return indices
521
525
  return queue_df
522
526
 
523
- def get_context(self):
527
+ def _get_context(self):
524
528
  """
525
529
  Provides the surrounding context for the patch to be annotated.
526
530
 
@@ -531,15 +535,21 @@ class Annotator:
531
535
  context.
532
536
  """
533
537
 
534
- def get_path(image_path, dim=True):
538
+ def get_square(image_path, dim=True, border=False):
535
539
  # Resize the image
536
540
  im = Image.open(image_path)
537
541
 
538
542
  # Dim the image
539
- if dim in [True, "True"]:
543
+ if dim in ["True", True]:
540
544
  im_array = np.array(im)
541
545
  im_array = 256 - (256 - im_array) * 0.4 # lighten image
542
546
  im = Image.fromarray(im_array.astype(np.uint8))
547
+
548
+ if border in ["True", True] and self.border:
549
+ w, h = im.size
550
+ im = ImageOps.expand(im, border=2, fill="red")
551
+ im = im.resize((w, h))
552
+
543
553
  return im
544
554
 
545
555
  def get_empty_square(patch_size: tuple[int, int]):
@@ -593,7 +603,10 @@ class Annotator:
593
603
 
594
604
  # derive ids from items
595
605
  ids = [x.index[0] if len(x.index) == 1 else None for x in items]
596
- ids = [x != ix for x in ids]
606
+ # list of booleans, True if not the current patch, False if the current patch
607
+ # used for dimming the surrounding patches and adding a border to the current patch
608
+ dim_bools = [x != ix for x in ids]
609
+ border_bools = [x == ix for x in ids]
597
610
 
598
611
  # derive images from items
599
612
  image_paths = [
@@ -601,14 +614,16 @@ class Annotator:
601
614
  ]
602
615
 
603
616
  # zip them
604
- image_list = list(zip(image_paths, ids))
617
+ image_list = list(zip(image_paths, dim_bools, border_bools))
605
618
 
606
619
  # split them into rows
607
620
  per_row = len(deltas)
608
621
  images = [
609
622
  [
610
- get_path(x[0], dim=x[1]) if x[0] else get_empty_square((width, height))
611
- for x in lst
623
+ get_square(image_path, dim=dim, border=border)
624
+ if image_path
625
+ else get_empty_square((width, height))
626
+ for image_path, dim, border in lst
612
627
  ]
613
628
  for lst in array_split(image_list, per_row)
614
629
  ]
@@ -641,6 +656,7 @@ class Annotator:
641
656
  def annotate(
642
657
  self,
643
658
  show_context: bool | None = None,
659
+ border: bool | None = None,
644
660
  sortby: str | None = None,
645
661
  ascending: bool | None = None,
646
662
  min_values: dict | None = None,
@@ -648,6 +664,7 @@ class Annotator:
648
664
  surrounding: int | None = None,
649
665
  resize_to: int | None = None,
650
666
  max_size: int | None = None,
667
+ show_vals: list[str] | None = None,
651
668
  ) -> None:
652
669
  """Annotate at the patch-level of the current patch.
653
670
  Renders the annotation interface for the first image.
@@ -657,6 +674,9 @@ class Annotator:
657
674
  show_context : bool or None, optional
658
675
  Whether or not to display the surrounding context for each image.
659
676
  Default is None.
677
+ border : bool or None, optional
678
+ Whether or not to display a border around the image (when using `show_context`).
679
+ Default is None.
660
680
  sortby : str or None, optional
661
681
  Name of the column to use to sort the patch DataFrame, by default None.
662
682
  Default sort order is ``ascending=True``. Pass ``ascending=False`` keyword argument to sort in descending order.
@@ -677,12 +697,18 @@ class Annotator:
677
697
  max_size : int or None, optional
678
698
  The size in pixels for the longest side to which constrain each
679
699
  patch image. Default: 100.
700
+ resize_to : int or None, optional
701
+ The size in pixels for the longest side to which resize each patch image. Default: None.
702
+ show_vals : list[str] or None, optional
703
+ List of column names to show in the display. By default, None.
680
704
 
681
705
  Notes
682
706
  -----
683
707
  This method is a wrapper for the
684
708
  :meth:`~.annotate.annotator.Annotate._annotate` method.
685
709
  """
710
+ if border is not None:
711
+ self.border = border
686
712
  if sortby is not None:
687
713
  self._sortby = sortby
688
714
  if ascending is not None:
@@ -693,8 +719,10 @@ class Annotator:
693
719
  if max_values is not None:
694
720
  self._max_values = max_values
695
721
 
722
+ self.show_vals = show_vals
723
+
696
724
  # re-set up queue using new min/max values
697
- self._queue = self.get_queue()
725
+ self._queue = self._get_queue()
698
726
 
699
727
  self._annotate(
700
728
  show_context=show_context,
@@ -743,7 +771,7 @@ class Annotator:
743
771
  self.max_size = max_size
744
772
 
745
773
  # re-set up queue
746
- self._queue = self.get_queue()
774
+ self._queue = self._get_queue()
747
775
 
748
776
  if self._filter_for is not None:
749
777
  print(f"[INFO] Filtering for: {self._filter_for}")
@@ -767,7 +795,7 @@ class Annotator:
767
795
  Previous index, current index, and path of the current image.
768
796
  """
769
797
  if self.current_index == len(self._queue):
770
- self.render_complete()
798
+ self._render_complete()
771
799
  return
772
800
 
773
801
  self.previous_index = self.current_index
@@ -777,7 +805,7 @@ class Annotator:
777
805
 
778
806
  img_path = self.patch_df.at[ix, self.patch_paths_col]
779
807
 
780
- self.render()
808
+ self._render()
781
809
  return self.previous_index, self.current_index, img_path
782
810
 
783
811
  def _prev_example(self, *_) -> tuple[int, int, str]:
@@ -790,7 +818,7 @@ class Annotator:
790
818
  Previous index, current index, and path of the current image.
791
819
  """
792
820
  if self.current_index == len(self._queue):
793
- self.render_complete()
821
+ self._render_complete()
794
822
  return
795
823
 
796
824
  if self.current_index > 0:
@@ -801,10 +829,10 @@ class Annotator:
801
829
 
802
830
  img_path = self.patch_df.at[ix, self.patch_paths_col]
803
831
 
804
- self.render()
832
+ self._render()
805
833
  return self.previous_index, self.current_index, img_path
806
834
 
807
- def render(self) -> None:
835
+ def _render(self) -> None:
808
836
  """
809
837
  Displays the image at the current index in the annotation interface.
810
838
 
@@ -817,7 +845,7 @@ class Annotator:
817
845
  """
818
846
  # Check whether we have reached the end
819
847
  if self.current_index >= len(self) - 1:
820
- self.render_complete()
848
+ self._render_complete()
821
849
  return
822
850
 
823
851
  ix = self._queue[self.current_index]
@@ -841,7 +869,7 @@ class Annotator:
841
869
  clear_output(wait=True)
842
870
  image = self.get_patch_image(ix)
843
871
  if self.show_context:
844
- context = self.get_context()
872
+ context = self._get_context()
845
873
  self._context_image = context
846
874
  display(context.convert("RGB"))
847
875
  else:
@@ -852,6 +880,22 @@ class Annotator:
852
880
  text = f'<p><a href="{url}" target="_blank">Click to see entire map.</a></p>'
853
881
  add_ins += [widgets.HTML(text)]
854
882
 
883
+ if self.show_vals:
884
+ patch_info = []
885
+ for col in self.show_vals:
886
+ if col in self.patch_df.columns:
887
+ val = self.patch_df.at[ix, col]
888
+ if isinstance(val, float):
889
+ val = f"{val:.4g}"
890
+ patch_info.append(f"<b>{col}</b>: {val}")
891
+ add_ins += [
892
+ widgets.HTML(
893
+ '<p style="text-align: center">'
894
+ + "<br>".join(patch_info)
895
+ + "</p>"
896
+ )
897
+ ]
898
+
855
899
  value = self.current_index + 1 if self.current_index else 1
856
900
  description = f"{value} / {len(self._queue)}"
857
901
  add_ins += [
@@ -983,7 +1027,7 @@ class Annotator:
983
1027
  _filter = ~self.patch_df[self.label_col].isna()
984
1028
  return self.patch_df[_filter]
985
1029
 
986
- def render_complete(self):
1030
+ def _render_complete(self):
987
1031
  """
988
1032
  Renders the completion message once all images have been annotated.
989
1033
 
@@ -25,6 +25,7 @@ class AnnotationsLoader:
25
25
 
26
26
  def __init__(self):
27
27
  self.annotations = pd.DataFrame()
28
+ self.labels_map = {}
28
29
  self.reviewed = pd.DataFrame()
29
30
  self.patch_paths_col = None
30
31
  self.label_col = None
@@ -33,6 +34,7 @@ class AnnotationsLoader:
33
34
  def load(
34
35
  self,
35
36
  annotations: str | pd.DataFrame,
37
+ labels_map: dict | None = None,
36
38
  delimiter: str | None = ",",
37
39
  images_dir: str | None = None,
38
40
  remove_broken: bool | None = True,
@@ -52,6 +54,8 @@ class AnnotationsLoader:
52
54
  annotations : Union[str, pd.DataFrame]
53
55
  The annotations.
54
56
  Can either be the path to a csv file or a pandas.DataFrame.
57
+ labels_map : Optional[dict], optional
58
+ A dictionary mapping labels to indices. If not provided, labels will be mapped to indices based on the order in which they appear in the annotations dataframe. By default None.
55
59
  delimiter : Optional[str], optional
56
60
  The delimiter to use when loading the csv file as a dataframe, by default ",".
57
61
  images_dir : Optional[str], optional
@@ -132,8 +136,25 @@ class AnnotationsLoader:
132
136
 
133
137
  self.unique_labels = self.annotations[self.label_col].unique().tolist()
134
138
 
135
- labels_map = {i: label for i, label in enumerate(self.unique_labels)}
136
- self.labels_map = labels_map
139
+ # if labels_map is explicitly provided
140
+ if labels_map:
141
+ self.labels_map = dict(
142
+ sorted(labels_map.items())
143
+ ) # sort labels_map by keys
144
+ if not set(self.unique_labels).issubset(set(labels_map.values())):
145
+ raise ValueError(
146
+ "[ERROR] There are label(s) in the annotations that are not in the labels map. Please check the labels_map."
147
+ )
148
+ # if inferring labels_map
149
+ else:
150
+ if append:
151
+ for label in self.unique_labels:
152
+ if label not in self.labels_map.values():
153
+ self.labels_map[len(self.labels_map)] = label
154
+ else:
155
+ # reset labels map
156
+ labels_map = {i: label for i, label in enumerate(self.unique_labels)}
157
+ self.labels_map = labels_map
137
158
 
138
159
  self.annotations["label_index"] = self.annotations[self.label_col].apply(
139
160
  self._get_label_index
@@ -861,7 +882,8 @@ Please check your image paths and update them if necessary.'
861
882
  Used to generate the ``label_index`` column.
862
883
 
863
884
  """
864
- return self.unique_labels.index(label)
885
+ index_map = {v: k for k, v in self.labels_map.items()}
886
+ return index_map[label]
865
887
 
866
888
  def __str__(self):
867
889
  print(f"[INFO] Number of annotations: {len(self.annotations)}\n")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapreader
3
- Version: 1.3.6
3
+ Version: 1.3.8
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
File without changes
File without changes
File without changes
File without changes
File without changes