@@ -79,7 +79,6 @@ typedef struct dt_iop_hazeremoval_gui_data_t
7979 rgb_pixel A0 ;
8080 float distance_max ;
8181 dt_hash_t hash ;
82- gboolean redo ;
8382} dt_iop_hazeremoval_gui_data_t ;
8483
8584typedef struct dt_iop_hazeremoval_global_data_t
@@ -211,6 +210,20 @@ void cleanup_global(dt_iop_module_so_t *self)
211210 self -> data = NULL ;
212211}
213212
213+
214+ void gui_update (dt_iop_module_t * self )
215+ {
216+ dt_iop_hazeremoval_gui_data_t * g = self -> gui_data ;
217+
218+ dt_iop_gui_enter_critical_section (self );
219+ g -> distance_max = NAN ;
220+ g -> A0 [0 ] = NAN ;
221+ g -> A0 [1 ] = NAN ;
222+ g -> A0 [2 ] = NAN ;
223+ g -> hash = 0 ;
224+ dt_iop_gui_leave_critical_section (self );
225+ }
226+
214227void gui_changed (dt_iop_module_t * self ,
215228 GtkWidget * w ,
216229 void * previous )
@@ -224,8 +237,12 @@ void gui_init(dt_iop_module_t *self)
224237{
225238 dt_iop_hazeremoval_gui_data_t * g = IOP_GUI_ALLOC (hazeremoval );
226239
240+ g -> distance_max = NAN ;
241+ g -> A0 [0 ] = NAN ;
242+ g -> A0 [1 ] = NAN ;
243+ g -> A0 [2 ] = NAN ;
227244 g -> hash = DT_INVALID_CACHEHASH ;
228- g -> redo = FALSE;
245+
229246 g -> strength = dt_bauhaus_slider_from_params (self , N_ ("strength" ));
230247 gtk_widget_set_tooltip_text (g -> strength , _ ("amount of haze reduction" ));
231248
@@ -399,7 +416,7 @@ void _quick_select(float *first,
399416 }
400417}
401418
402- // calculate diffusive ambient light and the maximal depth in the image.
419+ // calculate diffusive ambient light and the maximal depth in the image
403420// depth is estimated by the local amount of haze and given in units of the
404421// characteristic haze depth, i.e., the distance over which object light is
405422// reduced by the factor exp(-1)
@@ -513,12 +530,6 @@ static float _ambient_light(const const_rgb_image img,
513530 : logf (FLT_MAX ) / 2 ; // return the maximal depth
514531}
515532
516- static inline void _restart_pipe (dt_dev_pixelpipe_t * pipe , dt_iop_module_t * self )
517- {
518- dt_atomic_set_int (& pipe -> shutdown , self -> iop_order );
519- pipe -> changed |= DT_DEV_PIPE_SYNCH ;
520- }
521-
522533void process (dt_iop_module_t * self ,
523534 dt_dev_pixelpipe_iop_t * piece ,
524535 const void * const ivoid ,
@@ -532,7 +543,6 @@ void process(dt_iop_module_t *self,
532543 return ;
533544 dt_iop_hazeremoval_gui_data_t * const g = self -> gui_data ;
534545 dt_iop_hazeremoval_params_t * d = piece -> data ;
535- dt_dev_pixelpipe_t * pipe = piece -> pipe ;
536546
537547 const int width = roi_in -> width ;
538548 const int height = roi_in -> height ;
@@ -551,64 +561,61 @@ void process(dt_iop_module_t *self,
551561 float * const restrict out = (float * )ovoid ;
552562 const const_rgb_image img_in = (const_rgb_image ){ in , width , height , 4 };
553563
554- const dt_hash_t phash = dt_dev_pixelpipe_piece_hash (piece , NULL , TRUE);
555- const gboolean fullpipe = pipe -> type == DT_DEV_PIXELPIPE_FULL ;
556- const gboolean gui = self -> dev -> gui_attached && g ;
557-
558- /* hazeremoval needs the color and the haziness (which yields distance_max)
559- of the most hazy region of the image.
560- In pixelpipe DT_DEV_PIXELPIPE_FULL we can not reliably get this value as
561- the pixelpipe sees only part of the image (region of interest).
562-
563- To get consistent output in darkroom canvas and the exported image we don't
564- use A0 and distance_max (A&D) calculated from the preview pipe but want data
565- from a HQ pixelpipe.
566-
567- So we
568- a) ensure correct data by a hash calculated from all upstream modules.
569- b) If we run a full pipe without validated A&D we enforce an immediate
570- HQ pipe re-run, calculate A&D and again do an immediate re-run of the pipe.
571- c) If we have valid A&D we always use them, other do the fallback via
572- _ambient_light().
573- */
574- if (gui && fullpipe && phash != g -> hash )
564+ // estimate diffusive ambient light and image depth
565+ rgb_pixel A0 = { NAN , NAN , NAN , 0.0f };
566+ float distance_max = NAN ;
567+
568+ // hazeremoval module needs the color and the haziness (which yields
569+ // distance_max) of the most hazy region of the image. In pixelpipe
570+ // FULL we can not reliably get this value as the pixelpipe might
571+ // only see part of the image (region of interest). Therefore, we
572+ // try to get A0 and distance_max from the PREVIEW pixelpipe which
573+ // luckily stores it for us.
574+ if (self -> dev -> gui_attached && g && (piece -> pipe -> type & DT_DEV_PIXELPIPE_FULL ))
575575 {
576- if (!darktable .develop -> late_scaling .enabled )
577- {
578- dt_print_pipe (DT_DEBUG_PIPE | DT_DEBUG_VERBOSE , "HQ request" , pipe , piece -> module , pipe -> devid , NULL , NULL );
579- darktable .develop -> late_scaling .enabled = TRUE;
580- g -> redo = TRUE;
581- _restart_pipe (pipe , self );
582- return ;
583- }
584- else
585- {
586- g -> distance_max = _ambient_light (img_in , w1 , & g -> A0 , compatibility_mode );
587- g -> hash = phash ;
588- if (g -> redo )
589- {
590- dt_print_pipe (DT_DEBUG_PIPE | DT_DEBUG_VERBOSE , "HQ done" , pipe , piece -> module , pipe -> devid , NULL , NULL );
591- darktable .develop -> late_scaling .enabled = FALSE;
592- g -> redo = FALSE;
593- _restart_pipe (pipe , self );
594- return ;
595- }
596- }
576+ dt_iop_gui_enter_critical_section (self );
577+ const dt_hash_t hash = g -> hash ;
578+ dt_iop_gui_leave_critical_section (self );
579+ // Note that the case 'hash == 0' on first invocation in a session
580+ // implies that g->distance_max is NAN, which initiates special
581+ // handling below to avoid inconsistent results. In all other
582+ // cases we make sure that the preview pipe has left us with
583+ // proper readings for distance_max and A0. If data are not yet
584+ // there we need to wait (with timeout).
585+ if (hash != DT_INVALID_CACHEHASH
586+ && !dt_dev_sync_pixelpipe_hash (self -> dev , piece -> pipe , self -> iop_order ,
587+ DT_DEV_TRANSFORM_DIR_BACK_INCL ,
588+ & self -> gui_lock , & g -> hash ))
589+ dt_control_log (_ ("inconsistent output" ));
590+ dt_iop_gui_enter_critical_section (self );
591+ A0 [0 ] = g -> A0 [0 ];
592+ A0 [1 ] = g -> A0 [1 ];
593+ A0 [2 ] = g -> A0 [2 ];
594+ distance_max = g -> distance_max ;
595+ dt_iop_gui_leave_critical_section (self );
597596 }
598597
599- // estimated diffusive ambient light and image depth
600- rgb_pixel A0 ;
601- float distance_max ;
598+ // FIXME in pipe->type |= DT_DEV_PIXELPIPE_IMAGE mode we currently can't receive data from preview
599+ // so we at least leave a note to the user
600+ if (piece -> pipe -> type & DT_DEV_PIXELPIPE_IMAGE )
601+ dt_control_log (_ ("inconsistent output" ));
602602
603- const gboolean hashed = gui && phash == g -> hash ;
604- if (hashed )
603+ // In all other cases we calculate distance_max and A0 here.
604+ if (dt_isnan (distance_max ))
605+ distance_max = _ambient_light (img_in , w1 , & A0 , compatibility_mode );
606+ // PREVIEW pixelpipe stores values.
607+ if (self -> dev -> gui_attached && g && (piece -> pipe -> type & DT_DEV_PIXELPIPE_PREVIEW ))
605608 {
606- dt_print_pipe (DT_DEBUG_PIPE | DT_DEBUG_VERBOSE , "haze from HQ" , pipe , piece -> module , pipe -> devid , NULL , NULL );
607- for (int i = 0 ; i < 3 ; i ++ ) A0 [i ] = g -> A0 [i ];
608- distance_max = g -> distance_max ;
609+ dt_hash_t hash = dt_dev_hash_plus (self -> dev , piece -> pipe ,
610+ self -> iop_order , DT_DEV_TRANSFORM_DIR_BACK_INCL );
611+ dt_iop_gui_enter_critical_section (self );
612+ g -> A0 [0 ] = A0 [0 ];
613+ g -> A0 [1 ] = A0 [1 ];
614+ g -> A0 [2 ] = A0 [2 ];
615+ g -> distance_max = distance_max ;
616+ g -> hash = hash ;
617+ dt_iop_gui_leave_critical_section (self );
609618 }
610- else // In all other cases we calculate distance_max and A0 here.
611- distance_max = _ambient_light (img_in , w1 , & A0 , compatibility_mode );
612619
613620 // calculate the transition map
614621 gray_image trans_map = new_gray_image (width , height );
@@ -621,15 +628,15 @@ void process(dt_iop_module_t *self,
621628 guided_filter (img_in .data , trans_map .data , trans_map_filtered .data ,
622629 width , height , 4 , w2 , eps , 1.f , - FLT_MAX , FLT_MAX );
623630
624- // finally, calculate the haze-free image
631+ // finally, calculate the haze-free image, minimum allowed value for transition map
625632 const float t_min = CLAMP (expf (- distance * distance_max ), 1.0f / 1024.0f , 1.0f );
626633
627634 const dt_aligned_pixel_t c_A0 = { A0 [0 ], A0 [1 ], A0 [2 ], A0 [3 ] };
628635 const gray_image c_trans_map_filtered = trans_map_filtered ;
629636 DT_OMP_FOR ()
630637 for (size_t i = 0 ; i < size ; i ++ )
631638 {
632- const float t = MAX (c_trans_map_filtered .data [i ], t_min );
639+ float t = MAX (c_trans_map_filtered .data [i ], t_min );
633640 dt_aligned_pixel_t res ;
634641 for_each_channel (c , aligned (in ))
635642 res [c ] = (in [4 * i + c ] - c_A0 [c ]) / t + c_A0 [c ];
@@ -659,24 +666,19 @@ static float _ambient_light_cl(dt_iop_module_t *self,
659666 const int width = dt_opencl_get_image_width (img );
660667 const int height = dt_opencl_get_image_height (img );
661668 const int element_size = dt_opencl_get_image_element_size (img );
662-
663- cl_int err = DT_OPENCL_SYSMEM_ALLOCATION ;
664- float max_depth = 0.0f ;
665-
666669 float * in = dt_alloc_aligned ((size_t )width * height * element_size );
667- if (in == NULL ) goto error ;
668-
669- err = dt_opencl_read_host_from_device (devid , in , img , width , height , element_size );
670+ cl_int err = dt_opencl_read_host_from_device (devid , in , img , width , height , element_size );
670671 if (err != CL_SUCCESS ) goto error ;
671-
672672 const const_rgb_image img_in = (const_rgb_image )
673673 { in , width , height , element_size / sizeof (float ) };
674674
675- max_depth = _ambient_light (img_in , w1 , pA0 , compatibility_mode );
676-
677- error :
675+ const float max_depth = _ambient_light (img_in , w1 , pA0 , compatibility_mode );
678676 dt_free_align (in );
679677 return max_depth ;
678+ error :
679+ dt_print (DT_DEBUG_OPENCL , "[hazeremoval, ambient_light_cl] unknown error: %d" , err );
680+ dt_free_align (in );
681+ return 0.f ;
680682}
681683
682684static int _box_min_cl (dt_iop_module_t * self ,
@@ -797,10 +799,9 @@ int process_cl(dt_iop_module_t *self,
797799{
798800 dt_iop_hazeremoval_gui_data_t * const g = (dt_iop_hazeremoval_gui_data_t * )self -> gui_data ;
799801 dt_iop_hazeremoval_params_t * d = piece -> data ;
800- dt_dev_pixelpipe_t * pipe = piece -> pipe ;
801802
802803 const int ch = piece -> colors ;
803- const int devid = pipe -> devid ;
804+ const int devid = piece -> pipe -> devid ;
804805 const int width = roi_in -> width ;
805806 const int height = roi_in -> height ;
806807 const int w1 = 6 ; // window size (positive integer) for determining
@@ -813,50 +814,68 @@ int process_cl(dt_iop_module_t *self,
813814 const float eps = sqrtf (0.025f ); // regularization parameter for guided filter
814815 const gboolean compatibility_mode = d -> compatibility_mode ;
815816
816- const dt_hash_t phash = dt_dev_pixelpipe_piece_hash (piece , NULL , TRUE);
817- const gboolean fullpipe = pipe -> type == DT_DEV_PIXELPIPE_FULL ;
818- const gboolean gui = self -> dev -> gui_attached && g ;
819-
820- // For "how this works" see cpu code
821- if (gui && fullpipe && phash != g -> hash )
817+ // estimate diffusive ambient light and image depth
818+ rgb_pixel A0 = { NAN , NAN , NAN , 0.0f };
819+ float distance_max = NAN ;
820+
821+ // hazeremoval module needs the color and the haziness (which yields
822+ // distance_max) of the most hazy region of the image. In pixelpipe
823+ // FULL we can not reliably get this value as the pixelpipe might
824+ // only see part of the image (region of interest). Therefore, we
825+ // try to get A0 and distance_max from the PREVIEW pixelpipe which
826+ // luckily stores it for us.
827+ if (self -> dev -> gui_attached
828+ && g
829+ && (piece -> pipe -> type & DT_DEV_PIXELPIPE_FULL ))
822830 {
823- if (!darktable .develop -> late_scaling .enabled )
831+ dt_iop_gui_enter_critical_section (self );
832+ const dt_hash_t hash = g -> hash ;
833+ dt_iop_gui_leave_critical_section (self );
834+ // Note that the case 'hash == 0' on first invocation in a session
835+ // implies that g->distance_max is NAN, which initiates special
836+ // handling below to avoid inconsistent results. In all other
837+ // cases we make sure that the preview pipe has left us with
838+ // proper readings for distance_max and A0. If data are not yet
839+ // there we need to wait (with timeout).
840+ if (hash != DT_INVALID_CACHEHASH
841+ && !dt_dev_sync_pixelpipe_hash (self -> dev , piece -> pipe ,
842+ self -> iop_order , DT_DEV_TRANSFORM_DIR_BACK_INCL ,
843+ & self -> gui_lock , & g -> hash ))
824844 {
825- dt_print_pipe (DT_DEBUG_PIPE | DT_DEBUG_VERBOSE , "HQ request" , pipe , piece -> module , devid , NULL , NULL );
826- darktable .develop -> late_scaling .enabled = TRUE;
827- g -> redo = TRUE;
828- _restart_pipe (pipe , self );
829- return CL_SUCCESS ;
830- }
831- else
832- {
833- g -> distance_max = _ambient_light_cl (self , devid , img_in , w1 , & g -> A0 , compatibility_mode );
834- g -> hash = phash ;
835- if (g -> redo )
836- {
837- dt_print_pipe (DT_DEBUG_PIPE | DT_DEBUG_VERBOSE , "HQ done" , pipe , piece -> module , devid , NULL , NULL );
838- darktable .develop -> late_scaling .enabled = FALSE;
839- g -> redo = FALSE;
840- _restart_pipe (pipe , self );
841- return CL_SUCCESS ;
842- }
845+ dt_control_log (_ ("inconsistent output" ));
843846 }
847+
848+ dt_iop_gui_enter_critical_section (self );
849+ A0 [0 ] = g -> A0 [0 ];
850+ A0 [1 ] = g -> A0 [1 ];
851+ A0 [2 ] = g -> A0 [2 ];
852+ distance_max = g -> distance_max ;
853+ dt_iop_gui_leave_critical_section (self );
844854 }
845855
846- // estimated diffusive ambient light and image depth
847- rgb_pixel A0 ;
848- float distance_max ;
856+ // FIXME in pipe->type |= DT_DEV_PIXELPIPE_IMAGE mode we currently can't receive data from preview
857+ // so we at least leave a note to the user
858+ if (piece -> pipe -> type & DT_DEV_PIXELPIPE_IMAGE )
859+ dt_control_log (_ ("inconsistent output" ));
849860
850- const gboolean hashed = gui && phash == g -> hash ;
851- if (hashed )
861+ // In all other cases we calculate distance_max and A0 here.
862+ if (dt_isnan (distance_max ))
863+ distance_max = _ambient_light_cl (self , devid , img_in , w1 , & A0 , compatibility_mode );
864+ // PREVIEW pixelpipe stores values.
865+ if (self -> dev -> gui_attached
866+ && g
867+ && (piece -> pipe -> type & DT_DEV_PIXELPIPE_PREVIEW ))
852868 {
853- dt_print_pipe (DT_DEBUG_PIPE | DT_DEBUG_VERBOSE , "haze from HQ" , pipe , piece -> module , pipe -> devid , NULL , NULL );
854- for (int i = 0 ; i < 3 ; i ++ ) A0 [i ] = g -> A0 [i ];
855- distance_max = g -> distance_max ;
869+ dt_hash_t hash = dt_dev_hash_plus (self -> dev , piece -> pipe ,
870+ self -> iop_order , DT_DEV_TRANSFORM_DIR_BACK_INCL );
871+ dt_iop_gui_enter_critical_section (self );
872+ g -> A0 [0 ] = A0 [0 ];
873+ g -> A0 [1 ] = A0 [1 ];
874+ g -> A0 [2 ] = A0 [2 ];
875+ g -> distance_max = distance_max ;
876+ g -> hash = hash ;
877+ dt_iop_gui_leave_critical_section (self );
856878 }
857- else
858- distance_max = _ambient_light_cl (self , devid , img_in , w1 , & A0 , compatibility_mode );
859-
860879 cl_mem trans_map = NULL ;
861880 cl_mem trans_map_filtered = NULL ;
862881 cl_int err = CL_MEM_OBJECT_ALLOCATION_FAILURE ;
0 commit comments