img-phy-sim 0.4__tar.gz → 1.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.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: img-phy-sim
3
- Version: 0.4
3
+ Version: 1.0
4
4
  Summary: Physical Simulations on Images.
5
5
  Home-page: https://github.com/M-106/Image-Physics-Simulation
6
- Download-URL: https://github.com/M-106/Image-Physics-Simulation/archive/v_03.tar.gz
6
+ Download-URL: https://github.com/M-106/Image-Physics-Simulation/archive/v_05.tar.gz
7
7
  Author: Tobia Ippolito
8
8
  Project-URL: Documentation, https://M-106.github.io/Image-Physics-Simulation/img_phy_sim
9
9
  Project-URL: Source, https://github.com/M-106/Image-Physics-Simulation
@@ -21,6 +21,14 @@ Requires-Dist: numpy
21
21
  Requires-Dist: opencv-python
22
22
  Requires-Dist: matplotlib
23
23
  Requires-Dist: scikit-image
24
+ Requires-Dist: joblib
25
+ Requires-Dist: shapely
26
+ Requires-Dist: numba
27
+ Provides-Extra: full
28
+ Requires-Dist: torch; extra == "full"
29
+ Requires-Dist: torchvision; extra == "full"
30
+ Requires-Dist: datasets==3.6.0; extra == "full"
31
+ Requires-Dist: prime_printer; extra == "full"
24
32
  Dynamic: author
25
33
  Dynamic: classifier
26
34
  Dynamic: description
@@ -29,6 +37,7 @@ Dynamic: download-url
29
37
  Dynamic: home-page
30
38
  Dynamic: keywords
31
39
  Dynamic: project-url
40
+ Dynamic: provides-extra
32
41
  Dynamic: requires-dist
33
42
  Dynamic: summary
34
43
 
@@ -44,10 +53,14 @@ Contents:
44
53
  - [Raytracing Computation](#raytracing-computation)
45
54
  - [Raytracing Tutorial](#raytracing-tutorial)
46
55
  - [Performance Test](#performance-test)
56
+ - [Ray-Tracing Formats](#ray-tracing-formats)
47
57
 
48
58
  [> Documentation <](https://M-106.github.io/Image-Physics-Simulation/img_phy_sim.html)
49
59
 
50
- <img src="https://github.com/M-106/Image-Physics-Simulation/raw/main/img_phy_sim/raytracing_example.png"></img>
60
+ <img src="https://github.com/M-106/Image-Physics-Simulation/raw/main/img_phy_sim/raytracing_example.png" width="46%"></img>
61
+ <img src="https://github.com/M-106/Image-Physics-Simulation/raw/main/img_phy_sim/ism_example.png" width="46%"></img>
62
+
63
+ > Ray-Beams and ISM
51
64
 
52
65
  <br><br>
53
66
 
@@ -58,18 +71,27 @@ This repo only need some basic libraries:
58
71
  - `matplotlib`
59
72
  - `opencv-python`
60
73
  - `scikit-image`
74
+ - `joblib`
75
+
76
+ If you want to use the `data` module then this package needs also:
77
+ - `torch`
78
+ - `torchvision`
79
+ - `datasets`
80
+ - `prime_printer`
61
81
 
62
82
  You can download / clone this repo and run the example notebook via following Python/Anaconda setup:
63
83
  ```bash
64
84
  conda create -n img-phy-sim python=3.13 pip -y
65
85
  conda activate img-phy-sim
66
- pip install numpy matplotlib opencv-python ipython jupyter shapely prime_printer datasets==3.6.0 scikit-image
86
+ pip install numpy matplotlib opencv-python ipython jupyter shapely prime_printer datasets==3.6.0 scikit-image joblib shapely
67
87
  pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
68
88
  ```
69
89
 
70
90
  You can also use this repo via [Python Package Index (PyPI)](https://pypi.org/) as a package:
71
91
  ```bash
72
92
  pip install img-phy-sim
93
+ # or for using `data` module:
94
+ pip install img-phy-sim[full]
73
95
  ```
74
96
 
75
97
  Here the instructions to use the package version of `ips` and an anconda setup:
@@ -77,11 +99,13 @@ Here the instructions to use the package version of `ips` and an anconda setup:
77
99
  conda create -n img-phy-sim python=3.13 pip -y
78
100
  conda activate img-phy-sim
79
101
  pip install img-phy-sim
102
+ # or for using `data` module:
103
+ pip install img-phy-sim[full]
80
104
  ```
81
105
 
82
- To run the example code you still need:
106
+ To run the example code you also need (this is included in `img-phy-sim[full]`):
83
107
  ```bash
84
- pip install prime_printer shapely datasets==3.6.0
108
+ pip install prime_printer datasets==3.6.0
85
109
  pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
86
110
  ```
87
111
 
@@ -89,20 +113,20 @@ pip install torch torchvision torchaudio --index-url https://download.pytorch.or
89
113
 
90
114
  ### Download Example Data
91
115
 
92
- You can download Physgen data if wanted via the `physgen_dataset.py` using following commands:
116
+ You can download Physgen data if wanted via the `data.py` using following commands:
93
117
 
94
118
  ```bash
95
119
  conda activate img-phy-sim
96
- cd "D:\Informatik\Projekte\Image-Physics-Simulation" && D:
97
- python physgen_dataset.py --output_real_path ./datasets/physgen_train_raw/real --output_osm_path ./datasets/physgen_train_raw/osm --variation sound_reflection --input_type osm --output_type standard --data_mode train
120
+ cd "D:\Informatik\Projekte\Image-Physics-Simulation\img_phy_sim" && D:
121
+ python data.py --output_real_path ./datasets/physgen_train_raw/real --output_osm_path ./datasets/physgen_train_raw/osm --variation sound_reflection --input_type osm --output_type standard --data_mode train
98
122
  ```
99
123
 
100
124
  ```bash
101
- python physgen_dataset.py --output_real_path ./datasets/physgen_test_raw/real --output_osm_path ./datasets/physgen_test_raw/osm --variation sound_reflection --input_type osm --output_type standard --data_mode test
125
+ python data.py --output_real_path ./datasets/physgen_test_raw/real --output_osm_path ./datasets/physgen_test_raw/osm --variation sound_reflection --input_type osm --output_type standard --data_mode test
102
126
  ```
103
127
 
104
128
  ```bash
105
- python physgen_dataset.py --output_real_path ./datasets/physgen_val_raw/real --output_osm_path ./datasets/physgen_val_raw/osm --variation sound_reflection --input_type osm --output_type standard --data_mode validation
129
+ python data.py --output_real_path ./datasets/physgen_val_raw/real --output_osm_path ./datasets/physgen_val_raw/osm --variation sound_reflection --input_type osm --output_type standard --data_mode validation
106
130
  ```
107
131
 
108
132
  <br><br>
@@ -196,6 +220,23 @@ class PhysGenDataset(Dataset):
196
220
  - `open`: load your saved rays txt file
197
221
  - `get_linear_degree_range`: get a range for your beam-directions -> example beams between 0-360 with stepsize 10
198
222
  - `merge_rays`: merge 2 rays to one 'object'
223
+ - `ism`
224
+ - `reflect_point_across_infinite_line`: Reflects a point across the infinite line defined by two endpoints
225
+ - `paths_to_rays`: Converts polyline paths into your ray/segment representation, optionally normalizing points to ([0,1]) image space
226
+ - `reflection_map_to_img`: Normalizes a float reflection/energy map to a uint8 visualization image in ([0,255])
227
+ - `Segment`: Immutable dataclass representing a 2D line segment with convenience access to its endpoints
228
+ - `_seg_seg_intersection`: Computes the unique intersection point of two finite 2D segments, returning None for parallel/colinear/no-hit cases
229
+ - `_bresenham_points`: Generates all integer grid points along a line between two pixels using Bresenham’s algorithm
230
+ - `is_visible_raster`: Tests line-of-sight between two points by checking whether Bresenham-sampled pixels hit an occlusion raster
231
+ - `build_wall_mask`: Builds a binary 0/255 wall mask from an input image using explicit wall labels or mask-like heuristics
232
+ - `get_wall_segments_from_mask`: Extracts wall boundary contours from a binary mask and converts them into geometric Segment primitives
233
+ - `build_occlusion_from_wallmask`: Produces a binary occlusion raster (optionally dilated) used for fast visibility checks
234
+ - `enumerate_wall_sequences_indices`: Enumerates all reflection sequences (as wall-index tuples) up to a maximum reflection order
235
+ - `precompute_image_sources`: Computes image-source positions for each reflection sequence by iteratively mirroring the source across the corresponding walls
236
+ - `build_path_for_sequence`: Reconstructs the specular reflection polyline for a given wall sequence by backtracking virtual receivers and segment intersections
237
+ - `path_energy`: Computes a simple physically-inspired path contribution based on total path length and per-reflection losses
238
+ - `check_path_visibility_raster`: Verifies that every segment of a candidate path is unobstructed using raster line-of-sight tests
239
+ - `compute_reflection_map`: Evaluates all valid ISM paths from one source to a receiver grid and accumulates path counts
199
240
  - `img`
200
241
  - `open`: load an image via Open-CV
201
242
  - `save`: save an image
@@ -203,6 +244,21 @@ class PhysGenDataset(Dataset):
203
244
  - `advanced_imshow`: show multiple images with many options
204
245
  - `show_image_with_line_and_profile`: show an image with a red line + the values of the image on this line
205
246
  - `plot_image_with_values`: plot an image with it's value plotted and averaged to see your image in values
247
+ - `math`
248
+ - `get_linear_degree_range`: generate evenly spaced degrees within a range
249
+ - `degree_to_vector`: convert a degree angle to a 2D unit vector
250
+ - `vector_to_degree`: convert a 2D vector into its corresponding degree
251
+ - `normalize_point`: Normalize a 2D point to [0, 1] range
252
+ - `denormalize_point`: Denormalize a 2D point to pixel coordinates
253
+ - `numpy_info`: Get statistics about an numpy array
254
+ - `eval`
255
+ - `calc_metrices`: calculate F1, Recall and Precision between rays (or optinal an image) and an image
256
+ - `data`
257
+ - `PhysGenDataset()`: PyTorch dataset wrapper for PhysGen with flexible input/output configuration
258
+ - `resize_tensor_to_divisible_by_14`: resize tensors so height and width are divisible by 14
259
+ - `get_dataloader`: create a PyTorch DataLoader for the PhysGen dataset
260
+ - `get_image`: retrieve a single dataset sample (optionally as NumPy arrays)
261
+ - `save_dataset`: export PhysGen inputs and targets as PNG images to disk
206
262
 
207
263
 
208
264
  That are not all functions but the ones which should be most useful. Check out the documentation for all functions.
@@ -225,14 +281,9 @@ That are not all functions but the ones which should be most useful. Check out t
225
281
  [See also the example notebook 👀](./example/physgen.ipynb)
226
282
 
227
283
  In general you need to do:
228
- 1. **Load your Image** -> using `ips.img.open`
229
- 2. **Calculate the Wall-Map** -> using `ips.ray_tracing.get_wall_map`
230
- 3. **Calculate the Beams** -> using `ips.img.open`
231
- 4. **Draw (Export) the Beams** -> using `ips.img.open`
284
+ 1. Load your Image + Calculate the Wall-Map + **Calculate the Beams** -> using `ips.ray_tracing.trace_beams`
285
+ 2. **Draw (Export) the Beams on a image** -> using `ips.ray_tracing.draw_rays`
232
286
 
233
- Using this lib, this is reduced to:
234
- 1. **Calculate the Beams** (including Wall-Map and loading your Image) -> using `ips.img.open`
235
- 2. **Draw (Export) the Beams** -> using `ips.img.open`
236
287
 
237
288
  See this example:
238
289
 
@@ -361,7 +412,17 @@ I hope this little tutorial could be helpful. Good luck with your project <3
361
412
 
362
413
  <br>
363
414
 
364
- [> See the notebook/code <](./example/physgen_performance.ipynb)
415
+ [> See the notebook/code <](./example/physgen_performance.ipynb) [(or parallel notebook)](./example/physgen_parallel_performance.ipynb)
416
+
417
+ <br><br>
418
+
419
+ Comparison no parallel vs parallel computing:
420
+ - Parallel Mean Experiment time: 3.48 seconds (mean first experiment: 8.85 seconds)
421
+ - Standard Mean Experiment time: 4.53 seconds (mean first experiment: 16.00 seconds)
422
+
423
+ <br><br>
424
+
425
+ Parameter Experiments:
365
426
 
366
427
  Executed with 50 random images.
367
428
 
@@ -481,6 +542,216 @@ The Stepsize/amount of rays have the biggest impact on the performance. The othe
481
542
  | **3. Reflection Order** | 6 | 0.93 ± 0.74 s | 0.90 ± 0.72 s | 0.029 ± 0.016 s | **227.71 %** | Increasing (+0.37 s/exp) | Performance changes **significantly** |
482
543
  | **4. Detail Draw** | 2 | 0.63 ± 0.05 s | 0.56 ± 0.00 s | 0.071 ± 0.050 s | **16.39 %** | Increasing (+0.10 s/exp) | Performance changes **slightly** |
483
544
 
545
+ <!--
546
+ <br><br>
547
+
548
+ ### Optimization
549
+
550
+ <br>
551
+
552
+ Speed comparison between `standard`, `with joblib` and `joblib + CPython`.
553
+
554
+ FIXME -> table
555
+
556
+
557
+ <br>
558
+
559
+ Cython is Pyhon code which is closer to C. Instead of compiling to Python-Bytecode (`.pyc`), your code will be compiled as C-Extension (`.so`/`.pyd`).
560
+
561
+ There are 3 layers of Cython optimization:
562
+ 1. Writing in `.pyx` files not `.py` files -> you can just rename your file<br>
563
+ - +5% to +30% speedup
564
+ 2. Set `cdef` for local variables + helper-functions + `cpdef` for API-functions -> add types<br>
565
+ Example:
566
+ ```python
567
+ cdef double x0, y0, dx, dy
568
+ cdef int cell_x, cell_y, steps
569
+ ```
570
+ - +5x to +50x speedup
571
+ 3. Add types everywhere, especially in numpy arrays. <br>
572
+ Example:
573
+ ```python
574
+ cimport numpy as cnp
575
+
576
+ def trace(..., cnp.ndarray[double, ndim=2] img):
577
+ cdef double value = img[y, x]
578
+ ```
579
+ - +20× to +500×
580
+
581
+
582
+
583
+ | **Optimization Layer** | **Effort** | **Speedup** | **What It Does** |
584
+ |---|---|---|---|
585
+ | **1. `.py` → `.pyx`** | minimal | **+5–30%** | Reduces Python interpreter overhead and applies basic Cython optimizations |
586
+ | **2. `cdef` variables & `cpdef` functions** | medium | **+5×–50×** | Moves loops and math into pure C, eliminating Python object overhead |
587
+ | **3. `cimport numpy` + typed NumPy arrays** | high | **+20×–500×** | Enables direct C‑level memory access with zero Python indexing overhead |
588
+
589
+
590
+
591
+ > Use `pip install cypthon` to install it.
592
+
593
+ In `setup.py` you need following changes:
594
+ ```python
595
+ from setuptools import setup, find_packages, Extension
596
+ from Cython.Build import cythonize
597
+
598
+ ...
599
+
600
+ ext_1 = Extension(
601
+ name="img_phy_sim.ray_tracing",
602
+ sources=["img_phy_sim/ray_tracing.pyx"],
603
+ include_dirs=[],
604
+ extra_compile_args=["-O3"],
605
+ )
606
+
607
+ ext_2 = Extension(
608
+ name="img_phy_sim.math",
609
+ sources=["img_phy_sim/math.pyx"],
610
+ include_dirs=[],
611
+ extra_compile_args=["-O3"],
612
+ )
613
+
614
+ setup(
615
+ ext_modules=cythonize(
616
+ [ext_1, ext_2],
617
+ compiler_directives={
618
+ "language_level": "3",
619
+ "boundscheck": False,
620
+ "wraparound": False,
621
+ "initializedcheck": False,
622
+ "nonecheck": False,
623
+ "cdivision": True,
624
+ },
625
+ annotate=True,
626
+ ),
627
+ ...
628
+ )
629
+ ```
630
+
631
+ -->
632
+
633
+ <br><br>
634
+
635
+ ### Ray-Tracing Formats
636
+
637
+ <br>
638
+
639
+
640
+ **Your current approach (DDA / Pixel Ray Marching)**
641
+ * **Forward integration**: Ray is propagated step by step through a **discrete grid** (pixel/grid).
642
+ * **Collision model**: A "hit" occurs when the ray enters a **wall cell** (quantization).
643
+ * **Reflection**: Occurs **locally at the collision pixel** with a (often quantized) normal/orientation.
644
+ * Result: good for "many rays" / field of view, but **not deterministic with regard to reflection paths** (you need directions/sampling).
645
+
646
+ **Noise modeling style (image source method / geometric acoustics)**
647
+ * **Path construction**: Reflection paths are constructed **deterministically** via **mirror sources** (virtual sources).
648
+ * **Continuous geometry**: works in $\mathbb{R}^2 / \mathbb{R}^3$ with lines/segments/polygons ("infinity-based" in the sense of *continuous space*, not raster).
649
+ * **Validation**: Path is then accepted/rejected via **visibility/occlusion checks**.
650
+ * Result: Delivers **all specular paths up to order N** without angle sampling.
484
651
 
652
+ Short form:
653
+
654
+ * **Pixel-based + stochastic/directed** (original approach here) vs. **continuous + deterministically constructed** (noise modelling).
655
+
656
+
657
+ <br>
658
+
659
+ How to use which of them in `img-phy-sim`:
660
+
661
+ Classical Ray-Tracing:
662
+ ```python
663
+ # calc rays
664
+ rays = ips.ray_tracing.trace_beams(rel_position=[0.5, 0.5],
665
+ img_src=input_src,
666
+ directions_in_degree=ips.math.get_linear_degree_range(start=0, stop=360, step_size=5),
667
+ wall_values=None,
668
+ wall_thickness=1,
669
+ img_border_also_collide=False,
670
+ reflexion_order=3,
671
+ should_scale_rays=True,
672
+ should_scale_img=False)
673
+
674
+ # show rays on input
675
+ ray_img = ips.ray_tracing.draw_rays(rays, detail_draw=False,
676
+ output_format="single_image",
677
+ img_background=input_, ray_value=2, ray_thickness=1,
678
+ img_shape=(256, 256), dtype=float, standard_value=0,
679
+ should_scale_rays_to_image=True, original_max_width=None, original_max_height=None,
680
+ show_only_reflections=True)
681
+ ips.img.imshow(ray_img, size=4)
682
+ ```
683
+
684
+ ISM:
685
+ ```python
686
+ reflection_map = ips.ism.compute_reflection_map(
687
+ source_rel=(0.5, 0.5),
688
+ img=ips.img.open(input_src),
689
+ wall_values=[0],
690
+ wall_thickness=1,
691
+ max_order=1,
692
+ step_px=1,
693
+ parallelization=-1
694
+ )
695
+
696
+ ips.img.imshow(ips.ism.reflection_map_to_img(reflection_map), size=5)
697
+ ```
698
+
699
+ <br><br>
700
+
701
+ Both formats are also available in a **iterative format**.
702
+
703
+
704
+ Classical Ray-Tracing:
705
+ ```python
706
+ rays_ = ips.ray_tracing.trace_beams(rel_position=[0.5, 0.5],
707
+ img_src=img_src,
708
+ directions_in_degree=[22, 56, 90, 146, 234, 285, 320],
709
+ wall_values=0.0,
710
+ wall_thickness=0,
711
+ img_border_also_collide=False,
712
+ reflexion_order=2,
713
+ should_scale_rays=False,
714
+ should_scale_img=True,
715
+ iterative_tracking=True, # IMPORTANT
716
+ iterative_steps=None # IMPORTANT
717
+ )
718
+ print("\nAccessing works the same, example Ray:", rays_[0][0][:min(len(rays_[0][0])-1, 3)])
719
+
720
+ ray_imgs = ips.ray_tracing.draw_rays(rays_, detail_draw=False,
721
+ output_format="single_image",
722
+ img_background=img, ray_value=2, ray_thickness=1,
723
+ img_shape=(256, 256), dtype=float, standard_value=0,
724
+ should_scale_rays_to_image=False, original_max_width=None, original_max_height=None)
725
+
726
+ ips.img.advanced_imshow(ray_imgs[:10], title=None, image_width=4, axis=False,
727
+ color_space="gray", cmap=None, cols=5, save_to=None,
728
+ hspace=0.2, wspace=0.2,
729
+ use_original_style=False, invert=False)
730
+ ```
731
+
732
+ ISM:
733
+ ```python
734
+ reflection_map_per_time = ips.ism.compute_reflection_map(
735
+ source_rel=(0.5, 0.5),
736
+ img=ips.img.open(input_src),
737
+ wall_values=[0],
738
+ wall_thickness=1,
739
+ max_order=1,
740
+ step_px=1,
741
+ iterative_tracking=True,
742
+ iterative_steps=6, # IMPORTANT
743
+ parallelization=-1 # IMPORTANT
744
+ )
745
+
746
+ ips.img.imshow(ips.ism.reflection_map_to_img(reflection_map_per_time[0]), size=5)
747
+
748
+ len_ = len(reflection_map_per_time)
749
+ ips.img.advanced_imshow([reflection_map_per_time[0], reflection_map_per_time[1], reflection_map_per_time[2],
750
+ reflection_map_per_time[3], reflection_map_per_time[4], reflection_map_per_time[5]],
751
+ title=None, image_width=4, axis=False,
752
+ color_space="gray", cmap=None, cols=3, save_to=None,
753
+ hspace=0.2, wspace=0.2,
754
+ use_original_style=False, invert=False)
755
+ ```
485
756
 
486
757