@@ -212,6 +212,20 @@ def __init__(
212212 weight_power
213213 The power the weight values are raised too, which allows more pixels to be drawn from the higher weight
214214 regions of the adapt image.
215+
216+ image_mesh_min_mesh_pixels_per_pixel
217+ If not None, the image-mesh must place this many mesh pixels per image pixels in the N highest weighted
218+ regions of the adapt data, or an `InversionException` is raised. This can be used to force the image-mesh
219+ to cluster large numbers of source pixels to the adapt-datas brightest regions.
220+ image_mesh_min_mesh_number
221+ The value N given above in the docstring for `image_mesh_min_mesh_pixels_per_pixel`, indicating how many
222+ image pixels are checked for having a threshold number of mesh pixels.
223+ image_mesh_adapt_background_percent_threshold
224+ If not None, the image-mesh must place this percentage of mesh-pixels in the background regions of the
225+ `adapt_data`, where the background is the `image_mesh_adapt_background_percent_check` masked data pixels
226+ with the lowest values.
227+ image_mesh_adapt_background_percent_check
228+ The percentage of masked data pixels which are checked for the background criteria.
215229 """
216230
217231 super ().__init__ (
@@ -285,3 +299,132 @@ def image_plane_mesh_grid_from(
285299 )
286300
287301 return mesh_grid
302+
303+
304+ def check_mesh_pixels_per_image_pixels (
305+ self , mask : Mask2D , mesh_grid : Grid2DIrregular , settings : SettingsInversion
306+ ):
307+ """
308+ Checks the number of mesh pixels in every image pixel and raises an `InversionException` if there are fewer
309+ mesh pixels inside a certain number of image-pixels than the input settings.
310+
311+ This allows a user to force a model-fit to use image-mesh's which cluster a large number of mesh pixels to
312+ the brightest regions of the image data (E.g. the highst weighted regions).
313+
314+ The check works as follows:
315+
316+ 1) Compute the 2D array of the number of mesh pixels in every masked data image pixel.
317+ 2) Find the number of mesh pixels in the N data pixels with the larger number of mesh pixels, where N is
318+ given by `settings.image_mesh_min_mesh_number`. For example, if `settings.image_mesh_min_mesh_number=5` then
319+ the number of mesh pixels in the 5 data pixels with the most data pixels is computed.
320+ 3) Compare the lowest value above to the value `settings.image_mesh_min_mesh_pixels_per_pixel`. If the value is
321+ below this value, raise an `InversionException`.
322+
323+ Therefore, by settings `settings.image_mesh_min_mesh_pixels_per_pixel` to a value above 1 the code is forced
324+ to adapt the image mesh enough to put many mesh pixels in the brightest image pixels.
325+
326+ Parameters
327+ ----------
328+ mask
329+ The mask of the dataset being analysed, which the pixelization grid maps too. The number of
330+ mesh pixels mapped inside each of this mask's image-pixels is returned.
331+ mesh_grid
332+ The image mesh-grid computed by the class which adapts to the data's mask. The number of image mesh pixels
333+ that fall within each of the data's mask pixels is returned.
334+ settings
335+ The inversion settings, which have the criteria dictating if the image-mesh has clustered enough or if
336+ an exception is raised.
337+ """
338+
339+ if os .environ .get ("PYAUTOFIT_TEST_MODE" ) == "1" :
340+ return
341+
342+ if settings is not None :
343+ if settings .image_mesh_min_mesh_pixels_per_pixel is not None :
344+ mesh_pixels_per_image_pixels = self .mesh_pixels_per_image_pixels_from (
345+ mask = mask , mesh_grid = mesh_grid
346+ )
347+
348+ indices_of_highest_values = np .argsort (mesh_pixels_per_image_pixels )[
349+ - settings .image_mesh_min_mesh_number :
350+ ]
351+ lowest_mesh_pixels = np .min (
352+ mesh_pixels_per_image_pixels [indices_of_highest_values ]
353+ )
354+
355+ if lowest_mesh_pixels < settings .image_mesh_min_mesh_pixels_per_pixel :
356+ raise exc .InversionException ()
357+
358+ return mesh_grid
359+
360+ def check_adapt_background_pixels (
361+ self ,
362+ mask : Mask2D ,
363+ mesh_grid : Grid2DIrregular ,
364+ adapt_data : Optional [np .ndarray ],
365+ settings : SettingsInversion ,
366+ ):
367+ """
368+ Checks the number of mesh pixels in the background of the image-mesh and raises an `InversionException` if
369+ there are fewer mesh pixels in the background than the input settings.
370+
371+ This allows a user to force a model-fit to use image-mesh's which cluster a minimum number of mesh pixels to
372+ the faintest regions of the image data (E.g. the lowest weighted regions). This prevents too few image-mesh
373+ pixels being allocated to the background of the data.
374+
375+ The check works as follows:
376+
377+ 1) Find all pixels in the background of the `adapt_data`, which are N pixels with the lowest values, where N is
378+ a percentage given by `settings.image_mesh_adapt_background_percent_check`. If N is 50%, then the half of
379+ pixels in `adapt_data` with the lowest values will be checked.
380+ 2) Sum the total number of mesh pixels in these background pixels, thereby estimating the number of mesh pixels
381+ assigned to background pixels.
382+ 3) Compare this value to the total number of mesh pixels multiplied
383+ by `settings.image_mesh_adapt_background_percent_threshold` and raise an `InversionException` if the number
384+ of mesh pixels is below this value, meaning the background did not have sufficient mesh pixels in it.
385+
386+ Therefore, by setting `settings.image_mesh_adapt_background_percent_threshold` the code is forced
387+ to adapt the image mesh in a way that places many mesh pixels in the background regions.
388+
389+ Parameters
390+ ----------
391+ mask
392+ The mask of the dataset being analysed, which the pixelization grid maps too. The number of
393+ mesh pixels mapped inside each of this mask's image-pixels is returned.
394+ mesh_grid
395+ The image mesh-grid computed by the class which adapts to the data's mask. The number of image mesh pixels
396+ that fall within each of the data's mask pixels is returned.
397+ adapt_data
398+ A image which represents one or more components in the masked 2D data in the image-plane.
399+ settings
400+ The inversion settings, which have the criteria dictating if the image-mesh has clustered enough or if
401+ an exception is raised.
402+ """
403+
404+ if os .environ .get ("PYAUTOFIT_TEST_MODE" ) == "1" :
405+ return
406+
407+ if settings is not None :
408+ if settings .image_mesh_adapt_background_percent_threshold is not None :
409+ pixels = mesh_grid .shape [0 ]
410+
411+ pixels_in_background = int (
412+ mask .shape_slim * settings .image_mesh_adapt_background_percent_check
413+ )
414+
415+ indices_of_lowest_values = np .argsort (adapt_data )[:pixels_in_background ]
416+ mask_background = np .zeros_like (adapt_data , dtype = bool )
417+ mask_background [indices_of_lowest_values ] = True
418+
419+ mesh_pixels_per_image_pixels = self .mesh_pixels_per_image_pixels_from (
420+ mask = mask , mesh_grid = mesh_grid
421+ )
422+
423+ mesh_pixels_in_background = sum (
424+ mesh_pixels_per_image_pixels [mask_background ]
425+ )
426+
427+ if mesh_pixels_in_background < (
428+ pixels * settings .image_mesh_adapt_background_percent_threshold
429+ ):
430+ raise exc .InversionException ()
0 commit comments