@@ -24,8 +24,21 @@ class Inpainting(PluginBase):
2424 **kwargs (): Additional keyword arguments.
2525 """
2626
27- def __init__ (self , cell_n : int = 100 , method : str = "telea" , ** kwargs ):
27+ def __init__ (
28+ self ,
29+ cell_n : int = 100 ,
30+ method : str = "telea" ,
31+ input_layer_name : str = "elevation" ,
32+ resolution : float = 0.1 ,
33+ max_hole_size_m : float = - 1.0 ,
34+ max_hole_area_m2 : float = - 1.0 ,
35+ ** kwargs ,
36+ ):
2837 super ().__init__ ()
38+ self .input_layer_name = input_layer_name
39+ self .resolution = float (resolution )
40+ self .max_hole_size_m = float (max_hole_size_m )
41+ self .max_hole_area_m2 = float (max_hole_area_m2 )
2942 if method == "telea" :
3043 self .method = cv .INPAINT_TELEA
3144 elif method == "ns" : # Navier-Stokes
@@ -54,12 +67,41 @@ def __call__(
5467 cupy._core.core.ndarray:
5568 """
5669 valid_layer = elevation_map [2 ]
57- mask_np = cp .asnumpy ((valid_layer < 0.5 ).astype ("uint8" ))
58- elevation = elevation_map [0 ]
70+ if self .input_layer_name in layer_names :
71+ elevation = elevation_map [layer_names .index (self .input_layer_name )]
72+ elif self .input_layer_name in plugin_layer_names :
73+ elevation = plugin_layers [plugin_layer_names .index (self .input_layer_name )]
74+ else :
75+ raise ValueError (f"Inpainting could not find layer '{ self .input_layer_name } '" )
5976 finite_elevation = cp .isfinite (elevation )
6077 valid_mask = cp .logical_and (valid_layer > 0.5 , finite_elevation )
78+ invalid_mask = cp .logical_not (valid_mask )
79+ mask_np = cp .asnumpy (invalid_mask .astype ("uint8" ))
80+
81+ max_hole_size_cells = np .inf
82+ if self .max_hole_size_m > 0.0 :
83+ max_hole_size_cells = self .max_hole_size_m / max (self .resolution , 1e-6 )
84+
85+ if self .max_hole_area_m2 > 0.0 :
86+ max_hole_area_cells = self .max_hole_area_m2 / max (self .resolution * self .resolution , 1e-6 )
87+ elif np .isfinite (max_hole_size_cells ):
88+ max_hole_area_cells = max_hole_size_cells * max_hole_size_cells
89+ else :
90+ max_hole_area_cells = np .inf
91+
92+ if np .isfinite (max_hole_size_cells ) or np .isfinite (max_hole_area_cells ):
93+ num_labels , labels , stats , _ = cv .connectedComponentsWithStats (mask_np , connectivity = 8 )
94+ inpaint_mask_np = np .zeros_like (mask_np , dtype = "uint8" )
95+ for label in range (1 , num_labels ):
96+ width = float (stats [label , cv .CC_STAT_WIDTH ])
97+ height = float (stats [label , cv .CC_STAT_HEIGHT ])
98+ area = float (stats [label , cv .CC_STAT_AREA ])
99+ if width <= max_hole_size_cells and height <= max_hole_size_cells and area <= max_hole_area_cells :
100+ inpaint_mask_np [labels == label ] = 1
101+ else :
102+ inpaint_mask_np = mask_np
61103
62- if (mask_np < 1 ).any ():
104+ if (inpaint_mask_np > 0 ).any ():
63105 if not cp .any (valid_mask ):
64106 return elevation
65107
@@ -78,12 +120,16 @@ def __call__(
78120 # Replace NaNs with the minimum elevation value.
79121 safe_elevation = cp .where (finite_elevation , elevation , h_min )
80122 scaled = cp .asnumpy ((safe_elevation - h_min ) * 255.0 / denom ).astype ("uint8" )
81- dst = cv .inpaint (scaled , mask_np , 1 , self .method )
123+ dst = cv .inpaint (scaled , inpaint_mask_np , 1 , self .method )
82124 h_inpainted = dst .astype (np .float32 ) * denom / 255.0 + h_min
83125 filled = cp .asarray (h_inpainted , dtype = cp .float32 )
84126
85- # Ensure already-valid cells mirror the authoritative elevation layer.
86- filled = cp .where (valid_mask , elevation , filled )
87- return filled .astype (cp .float64 )
127+ # Already-valid cells stay authoritative, supported small holes get filled,
128+ # and deeper unsupported gaps remain invalid (NaN) instead of being invented away.
129+ inpaint_mask = cp .asarray (inpaint_mask_np .astype (bool ))
130+ output = cp .full (elevation .shape , cp .nan , dtype = cp .float32 )
131+ output = cp .where (inpaint_mask , filled , output )
132+ output = cp .where (valid_mask , elevation , output )
133+ return output .astype (cp .float64 )
88134 else :
89- return elevation_map [ 0 ]
135+ return cp . where ( valid_mask , elevation , cp . nan ). astype ( cp . float64 )
0 commit comments