1 /*
2  * This file is part of Filmulator.
3  *
4  * Copyright 2013 Omer Mano and Carlo Vaccari
5  *
6  * Filmulator is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Filmulator is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Filmulator. If not, see <http://www.gnu.org/licenses/>
18  */
19 #include "imagePipeline.h"
20 #include <algorithm>
21 #include <stdio.h>
22 #include <unistd.h>
23 
24 //Function-------------------------------------------------------------------------
filmulate(matrix<float> & input_image,matrix<float> & output_density,ParameterManager * paramManager,ImagePipeline * pipeline)25 bool ImagePipeline::filmulate(matrix<float> &input_image,
26                               matrix<float> &output_density,
27                               ParameterManager * paramManager,
28                               ImagePipeline * pipeline)
29 {
30     FilmParams filmParam;
31     AbortStatus abort;
32     Valid valid;
33     std::tie(valid, abort, filmParam) = paramManager->claimFilmParams();
34     if(abort == AbortStatus::restart)
35     {
36         return true;
37     }
38 
39     //Extract parameters from struct
40     float initial_developer_concentration = filmParam.initialDeveloperConcentration;
41     float reservoir_thickness = filmParam.reservoirThickness;
42     float active_layer_thickness = filmParam.activeLayerThickness;
43     float crystals_per_pixel = filmParam.crystalsPerPixel;
44     float initial_crystal_radius = filmParam.initialCrystalRadius;
45     float initial_silver_salt_density = filmParam.initialSilverSaltDensity;
46     float developer_consumption_const = filmParam.developerConsumptionConst;
47     float crystal_growth_const = filmParam.crystalGrowthConst;
48     float silver_salt_consumption_const = filmParam.silverSaltConsumptionConst;
49     float total_development_time = filmParam.totalDevelopmentTime;
50     int agitate_count = filmParam.agitateCount;
51     int development_steps = filmParam.developmentSteps;
52     float film_area = filmParam.filmArea;
53     float sigma_const = filmParam.sigmaConst;
54     float layer_mix_const = filmParam.layerMixConst;
55     float layer_time_divisor = filmParam.layerTimeDivisor;
56     float rolloff_boundary = filmParam.rolloffBoundary;
57     float toe_boundary = filmParam.toeBoundary;
58 
59     //Set up timers
60     struct timeval initialize_start, development_start, develop_start,
61                    diffuse_start, agitate_start, layer_mix_start;
62     double develop_dif = 0, diffuse_dif = 0, agitate_dif = 0, layer_mix_dif= 0;
63     gettimeofday(&initialize_start,NULL);
64 
65     int nrows = (int) input_image.nr();
66     int ncols = (int) input_image.nc()/3;
67     int npix = nrows*ncols;
68 
69     //Now we activate some of the crystals on the film. This is literally
70     //akin to exposing film to light.
71     matrix<float> active_crystals_per_pixel = input_image;
72     exposure(active_crystals_per_pixel, crystals_per_pixel, rolloff_boundary, toe_boundary);
73     //We set the crystal radius to a small seed value for each color.
74     matrix<float>& crystal_radius = output_density;
75     crystal_radius.set_size(nrows,ncols*3);
76     crystal_radius = initial_crystal_radius;
77 
78     //All layers share developer, so we only make it the original image size.
79     matrix<float> developer_concentration;
80     developer_concentration.set_size(nrows,ncols);
81     developer_concentration = initial_developer_concentration;
82 
83     //Each layer gets its own silver salt which will feed crystal growth.
84     matrix<float> silver_salt_density;
85     silver_salt_density.set_size(nrows,ncols*3);
86     silver_salt_density = initial_silver_salt_density;
87 
88     //Now, we set up the reservoir.
89     //Because we don't want the film area to influence the brightness, we
90     // increase the reservoir size in proportion.
91 #define FILMSIZE 864;//36x24mm
92     reservoir_thickness *= film_area/FILMSIZE;
93     float reservoir_developer_concentration = initial_developer_concentration;
94 
95     //This is a value used in diffuse to set the length scale.
96     float pixels_per_millimeter = sqrt(npix/film_area);
97 
98     //Here we do some math for the control logic for the differential
99     //equation approximation computations.
100     float timestep = total_development_time/development_steps;
101 	int agitate_period;
102 	if(agitate_count > 0)
103     {
104         agitate_period = floor(development_steps/agitate_count);
105     }
106     else
107     {
108         agitate_period = 3*development_steps;
109     }
110 	int half_agitate_period = floor(agitate_period/2);
111 
112     tout << "Initialization time: " << timeDiff(initialize_start)
113          << " seconds" << endl;
114     gettimeofday(&development_start,NULL);
115 
116     //Now we begin the main development/diffusion loop, which approximates the
117     //differential equation of film development.
118     for(int i = 0; i <= development_steps; i++)
119     {
120         //Check for cancellation
121         abort = paramManager->claimFilmAbort();
122         if(abort == AbortStatus::restart)
123         {
124             return true;
125         }
126 
127         //Updating for starting the development simulation. Valid is one too high here.
128         pipeline->updateProgress(Valid::partfilmulation, float(i)/float(development_steps));
129 
130         gettimeofday(&develop_start,NULL);
131 
132         //This is where we perform the chemical reaction part.
133         //The crystals grow.
134         //The developer in the active layer is consumed.
135         //So is the silver salt in the film.
136         // The amount consumed increases as the crystals grow larger.
137         //Because the developer and silver salts are consumed in bright regions,
138         // this reduces the rate at which they grow. This gives us global
139         // contrast reduction.
140         develop(crystal_radius,crystal_growth_const,active_crystals_per_pixel,
141                 silver_salt_density,developer_concentration,
142                 active_layer_thickness,developer_consumption_const,
143                 silver_salt_consumption_const,timestep);
144 
145         develop_dif += timeDiff(develop_start);
146         gettimeofday(&diffuse_start,NULL);
147 
148         //Check for cancellation
149         abort = paramManager->claimFilmAbort();
150         if(abort == AbortStatus::restart)
151         {
152             return true;
153         }
154 
155         //Updating for starting the diffusion simulation. Valid is one too high here.
156         pipeline->updateProgress(Valid::partfilmulation, float(i)/float(development_steps));
157 
158         //Now, we are going to perform the diffusion part.
159         //Here we mix the layer among itself, which grants us the
160         // local contrast increases.
161         diffuse(developer_concentration,
162                 sigma_const,
163                 pixels_per_millimeter,
164                 timestep);
165 //        diffuse_short_convolution(developer_concentration,
166 //                                  sigma_const,
167 //                                  pixels_per_millimeter,
168 //                                  timestep);
169 //        diffuse_resize_iir(developer_concentration,
170 //                           sigma_const,
171 //                           pixels_per_millimeter,
172 //                           timestep);
173 
174         diffuse_dif += timeDiff(diffuse_start);
175 
176         gettimeofday(&layer_mix_start,NULL);
177         //This performs mixing between the active layer adjacent to the film
178         // and the reservoir.
179         //This keeps the effects from getting too crazy.
180         layer_mix(developer_concentration,
181                   active_layer_thickness,
182                   reservoir_developer_concentration,
183                   reservoir_thickness,
184                   layer_mix_const,
185                   layer_time_divisor,
186                   pixels_per_millimeter,
187                   timestep);
188 
189         layer_mix_dif += timeDiff(layer_mix_start);
190 
191         gettimeofday(&agitate_start,NULL);
192 
193         //I want agitation to only occur in the middle of development, not
194         //at the very beginning or the ends. So, I add half the agitate
195         //period to the current cycle count.
196         if((i+half_agitate_period) % agitate_period ==0)
197             agitate(developer_concentration, active_layer_thickness,
198                     reservoir_developer_concentration, reservoir_thickness,
199                     pixels_per_millimeter);
200 
201         agitate_dif += timeDiff(agitate_start);
202     }
203     tout << "Development time: " <<timeDiff(development_start)<< " seconds" << endl;
204     tout << "Develop time: " << develop_dif << " seconds" << endl;
205     tout << "Diffuse time: " << diffuse_dif << " seconds" << endl;
206     tout << "Layer mix time: " << layer_mix_dif << " seconds" << endl;
207     tout << "Agitate time: " << agitate_dif << " seconds" << endl;
208 
209     //Now we compute the density (opacity) of the film.
210     //We assume that overlapping crystals or dye clouds are
211     //nonexistent. It works okay, for now...
212     //The output is crystal_radius^2 * active_crystals_per_pixel
213     struct timeval mult_start;
214     gettimeofday(&mult_start,NULL);
215 
216     abort = paramManager->claimFilmAbort();
217     if(abort == AbortStatus::restart)
218     {
219         return true;
220     }
221 
222     const int numRows = crystal_radius.nr();
223     const int numCols = crystal_radius.nc();
224 
225     #pragma omp parallel for
226     for (int i = 0; i < numRows; ++i) {
227         for (int j = 0; j < numCols; ++j) {
228             output_density(i, j) = crystal_radius(i, j) * crystal_radius(i, j) * active_crystals_per_pixel(i, j);
229         }
230     }
231     tout << "Output density time: "<<timeDiff(mult_start) << endl;
232 #ifdef DOUT
233     debug_out.close();
234 #endif
235     return false;
236 }
237 
238