1 #include <iostream>
2 #include <vector>
3 #include <stdexcept>  // std::domain_error
4 #include <limits>     // std::numeric_limits
5 #include "igs_color_rgb_hsv.h"
6 #include "igs_math_random.h"
7 #include "igs_ifx_common.h"  // igs::image::rgba
8 #include "igs_hsv_noise_in_camera.h"
9 
10 namespace igs {
11 namespace hsv_noise_in_camera {
12 /* バッファをまとめて確保(return or throwで自動解放) */
13 class noise_reference {
14 public:
noise_reference(const int ww,const int hh,const double hue_range,const double sat_range,const double val_range,const double alp_range,const unsigned long random_seed,const double near_blur,const int camera_x,const int camera_y,const int camera_w,const int camera_h)15   noise_reference(const int ww, const int hh, const double hue_range,
16                   const double sat_range, const double val_range,
17                   const double alp_range, const unsigned long random_seed,
18                   const double near_blur
19 
20                   ,
21                   const int camera_x, const int camera_y, const int camera_w,
22                   const int camera_h)
23       : w_(ww), h_(hh), nblur_(near_blur) {
24     if (0 == ww) {
25       return;
26     }
27     if (0 == hh) {
28       return;
29     }
30 
31     /* 枚数ゼロ(rangeが全部ゼロ)なのでノイズかけない */
32     if ((0.0 == hue_range) && (0.0 == sat_range) && (0.0 == val_range) &&
33         (0.0 == alp_range)) {
34       return;
35     }
36 
37     /* memory確保 */
38     const int sz = ww * hh;
39     if (0.0 != hue_range) {
40       this->hue_array_.resize(sz);
41     }
42     if (0.0 != sat_range) {
43       this->sat_array_.resize(sz);
44     }
45     if (0.0 != val_range) {
46       this->val_array_.resize(sz);
47     }
48     if (0.0 != alp_range) {
49       this->alp_array_.resize(sz);
50     }
51 
52     /* タネを設定 */
53     igs::math::random hue_rand, sat_rand, val_rand, alp_rand;
54     unsigned long step = 0;
55     if (0.0 != hue_range) {
56       hue_rand.seed(random_seed + step++);
57     }
58     if (0.0 != sat_range) {
59       sat_rand.seed(random_seed + step++);
60     }
61     if (0.0 != val_range) {
62       val_rand.seed(random_seed + step++);
63     }
64     if (0.0 != alp_range) {
65       alp_rand.seed(random_seed + step++);
66     }
67     igs::math::random hue_rand_ext, sat_rand_ext, val_rand_ext, alp_rand_ext;
68     if (0.0 != hue_range) {
69       hue_rand_ext.seed(random_seed + step++);
70     }
71     if (0.0 != sat_range) {
72       sat_rand_ext.seed(random_seed + step++);
73     }
74     if (0.0 != val_range) {
75       val_rand_ext.seed(random_seed + step++);
76     }
77     if (0.0 != alp_range) {
78       alp_rand_ext.seed(random_seed + step++);
79     }
80 
81     /* ノイズを書く */
82     /* -???_range/2〜???_range/2 */
83     int x1 = camera_x;
84     int y1 = camera_y;
85     int x2 = camera_x + camera_w - 1;
86     int y2 = camera_y + camera_h - 1;
87 
88     /* マージンあるかないかでノイズパターンが変わってしまうため、
89 near_blur用に広げてはいけない */
90     // if (0.0 != near_blur) { x1-=1; x2+=1; y1-=1; y2+=1; }
91 
92     if (0.0 != hue_range) {
93       int pos = 0;
94       for (int yy = 0; yy < hh; ++yy) {
95         for (int xx = 0; xx < ww; ++xx, ++pos) {
96           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
97             this->hue_array_[pos] = hue_range * (hue_rand_ext.next_d() - 0.5);
98           } else {
99             this->hue_array_[pos] = hue_range * (hue_rand.next_d() - 0.5);
100           }
101         }
102       }
103     }
104     if (0.0 != sat_range) {
105       int pos = 0;
106       for (int yy = 0; yy < hh; ++yy) {
107         for (int xx = 0; xx < ww; ++xx, ++pos) {
108           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
109             this->sat_array_[pos] = sat_range * (sat_rand_ext.next_d() - 0.5);
110           } else {
111             this->sat_array_[pos] = sat_range * (sat_rand.next_d() - 0.5);
112           }
113         }
114       }
115     }
116     if (0.0 != val_range) {
117       int pos = 0;
118       for (int yy = 0; yy < hh; ++yy) {
119         for (int xx = 0; xx < ww; ++xx, ++pos) {
120           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
121             this->val_array_[pos] = val_range * (val_rand_ext.next_d() - 0.5);
122           } else {
123             this->val_array_[pos] = val_range * (val_rand.next_d() - 0.5);
124           }
125         }
126       }
127     }
128     if (0.0 != alp_range) {
129       int pos = 0;
130       for (int yy = 0; yy < hh; ++yy) {
131         for (int xx = 0; xx < ww; ++xx, ++pos) {
132           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
133             this->alp_array_[pos] = alp_range * (alp_rand_ext.next_d() - 0.5);
134           } else {
135             this->alp_array_[pos] = alp_range * (alp_rand.next_d() - 0.5);
136           }
137         }
138       }
139     }
140   }
hue_value(const int xx,const int yy)141   double hue_value(const int xx, const int yy) {
142     return this->noise_value_(this->hue_array_, this->w_, this->h_, xx, yy,
143                               this->nblur_);
144   }
sat_value(const int xx,const int yy)145   double sat_value(const int xx, const int yy) {
146     return this->noise_value_(this->sat_array_, this->w_, this->h_, xx, yy,
147                               this->nblur_);
148   }
val_value(const int xx,const int yy)149   double val_value(const int xx, const int yy) {
150     return this->noise_value_(this->val_array_, this->w_, this->h_, xx, yy,
151                               this->nblur_);
152   }
alp_value(const int xx,const int yy)153   double alp_value(const int xx, const int yy) {
154     return this->noise_value_(this->alp_array_, this->w_, this->h_, xx, yy,
155                               this->nblur_);
156   }
clear()157   void clear() {
158     this->alp_array_.clear();
159     this->val_array_.clear();
160     this->sat_array_.clear();
161     this->hue_array_.clear();
162   }
~noise_reference()163   ~noise_reference() { this->clear(); }
164 
165 private:
166   const int w_;
167   const int h_;
168   const double nblur_;
169   std::vector<double> hue_array_;
170   std::vector<double> sat_array_;
171   std::vector<double> val_array_;
172   std::vector<double> alp_array_;
accum_in_(const double * noise_array,const int ww,const int hh,const int xx,const int yy,double & accum_val,int & accum_count)173   void accum_in_(const double *noise_array, const int ww, const int hh,
174                  const int xx, const int yy, double &accum_val,
175                  int &accum_count) {
176     if ((0 <= xx) && (xx < ww) && (0 <= yy) && (yy < hh)) {
177       accum_val += noise_array[yy * ww + xx];
178       ++accum_count;
179     }
180   }
noise_value_(const std::vector<double> & noise_vector,const int ww,const int hh,const int xx,const int yy,const double near_blur)181   double noise_value_(const std::vector<double> &noise_vector, const int ww,
182                       const int hh, const int xx, const int yy,
183                       const double near_blur) {
184     if (noise_vector.size() <= 0) {
185       return 0.0;
186     }
187     const double *noise_array = &noise_vector.at(0);
188     if (0.0 == near_blur) {
189       return noise_array[yy * ww + xx];
190     }
191 
192     double accum_val = 0.0;
193     int accum_count  = 0;
194     this->accum_in_(noise_array, ww, hh, xx - 1, yy - 1, accum_val,
195                     accum_count);
196     this->accum_in_(noise_array, ww, hh, xx, yy - 1, accum_val, accum_count);
197     this->accum_in_(noise_array, ww, hh, xx + 1, yy - 1, accum_val,
198                     accum_count);
199     this->accum_in_(noise_array, ww, hh, xx - 1, yy, accum_val, accum_count);
200     this->accum_in_(noise_array, ww, hh, xx + 1, yy, accum_val, accum_count);
201     this->accum_in_(noise_array, ww, hh, xx - 1, yy + 1, accum_val,
202                     accum_count);
203     this->accum_in_(noise_array, ww, hh, xx, yy + 1, accum_val, accum_count);
204     this->accum_in_(noise_array, ww, hh, xx + 1, yy + 1, accum_val,
205                     accum_count);
206     if (accum_count <= 0) {
207       return noise_array[yy * ww + xx];
208     }
209     accum_val /= static_cast<double>(accum_count);
210 
211     /* 中心Pixelと廻り(1Pixel幅)の平均値とのバランスを返す */
212     return (near_blur * accum_val) +
213            ((1.0 - near_blur) * noise_array[yy * ww + xx]);
214   }
215 
216   /* copy constructorを無効化 */
217   noise_reference(const noise_reference &);
218 
219   /* 代入演算子を無効化 */
220   noise_reference &operator=(const noise_reference &);
221 };
222 }
223 }
224 namespace igs {
225 namespace hsv_noise_in_camera {
226 /* 端値を適度に調整する */
227 class control_term_within_limits {
228 public:
control_term_within_limits(const double effective_low=0.0,const double effective_high=0.0,const double center=0.5,const int type=0,const double noise_range=0.0)229   control_term_within_limits(const double effective_low  = 0.0,
230                              const double effective_high = 0.0,
231                              const double center = 0.5, const int type = 0,
232                              const double noise_range = 0.0)
233       : effective_low_(effective_low)
234       , effective_high_(effective_high)
235       , center_(center)
236       , type_(static_cast<term_type_>(type))
237       , noise_range_(noise_range) {}
exec(const double current_value,double & noise,double & shift_value)238   void exec(const double current_value /* 0...1 */
239             ,
240             double &noise /* -noise_range/2...noise_range/2 */
241             ,
242             double &shift_value) {
243     if ((0.0 < this->effective_low_) && (current_value < this->center_)) {
244       const double cen = this->center_;
245       const double val = current_value;
246       const double ran = this->noise_range_;
247       const double eff = this->effective_low_;
248       switch (this->type_) {
249       case shift_all_:
250         shift_value = ((cen - val) / cen) * (ran / 2.0) * eff;
251         break;
252       case shift_term_:
253         if (val < ran) {
254           shift_value = (((cen < ran) ? cen : ran) - val) / 2.0 * eff;
255         }
256         break;
257       case decrease_all_: {
258         const double tmp = (cen - val) / cen * eff;
259         if (0.0 < tmp) {
260           noise *= 1.0 - tmp;
261         }
262       } break;
263       case decrease_term_:
264         if (val < (ran / 2.0)) {
265           const double stop = (cen < (ran / 2.0)) ? cen : ran / 2.0;
266           const double tmp  = (stop - val) / stop * eff;
267           if (0.0 < tmp) {
268             noise *= 1.0 - tmp;
269           }
270         }
271         break;
272       }
273     }
274 
275     if ((0.0 < this->effective_high_) && (this->center_ < current_value)) {
276       const double cen = this->center_;
277       const double val = current_value;
278       const double ran = this->noise_range_;
279       const double eff = this->effective_high_;
280       switch (this->type_) {
281       case shift_all_:
282         shift_value = ((cen - val) / (1.0 - cen)) * (ran / 2.0) * eff;
283         break;
284       case shift_term_:
285         if ((1.0 - ran) < val) {
286           const double ira = 1.0 - ran;
287           shift_value      = (((cen < ira) ? ira : cen) - val) / 2.0 * eff;
288         }
289         break;
290       case decrease_all_: {
291         const double tmp = (val - cen) / (1.0 - cen) * eff;
292         if (0.0 < tmp) {
293           noise *= 1.0 - tmp;
294         }
295       } break;
296       case decrease_term_:
297         if ((1.0 - (ran / 2.0)) < val) {
298           const double rpos = 1.0 - (ran / 2.0);
299           const double stop = (cen < rpos) ? rpos : cen;
300           const double tmp  = (val - stop) / (1.0 - stop) * eff;
301           if (0.0 < tmp) {
302             noise *= 1.0 - tmp;
303           }
304         }
305         break;
306       }
307     }
308   }
noise_range(void) const309   double noise_range(void) const { return this->noise_range_; }
310 
311 private:
312   /* low,high両方ゼロ =exec()内処理せず =端値はカット =default */
313   const double effective_low_;
314   const double effective_high_;
315 
316   const double center_;
317 
318   enum term_type_ {                 /* 端値の調整方法 */
319                     shift_all_ = 0, /* 0.全体的にノイズ位置がずれる */
320                     shift_term_, /* 1.端のみでノイズ位置がずれる */
321                     decrease_all_,  /* 2.全体的にノイズ幅が減る */
322                     decrease_term_, /* 3.端のみでノイズ幅が減る */
323   };
324   const term_type_ type_;
325 
326   const double noise_range_;
327 
328   /* copy constructorを無効化 */
329   control_term_within_limits(const control_term_within_limits &);
330 
331   /* 代入演算子を無効化 */
332   control_term_within_limits &operator=(const control_term_within_limits &);
333 };
334 }
335 }
336 namespace igs {
337 namespace hsv_noise_in_camera {
338 /* RGB値にノイズをのせる */
339 void pixel_rgb(const double red_in, const double gre_in, const double blu_in,
340                const double alp_in, const double hue_noise,
341                const double sat_noise, const double val_noise,
342                control_term_within_limits &sat_term,
343                control_term_within_limits &val_term, double &red_out,
344                double &gre_out, double &blu_out);
345 /* Alpha値にノイズをのせる */
346 void pixel_a(const double alp_in, const double alp_noise,
347              control_term_within_limits &alp_term, double &alp_out);
348 }
349 }
pixel_rgb(const double red_in,const double gre_in,const double blu_in,const double alp_in,const double hue_noise,const double sat_noise,const double val_noise,control_term_within_limits & sat_term,control_term_within_limits & val_term,double & red_out,double & gre_out,double & blu_out)350 void igs::hsv_noise_in_camera::pixel_rgb(
351     const double red_in, const double gre_in, const double blu_in,
352     const double alp_in, const double hue_noise, const double sat_noise,
353     const double val_noise, control_term_within_limits &sat_term,
354     control_term_within_limits &val_term, double &red_out, double &gre_out,
355     double &blu_out) {
356   if (0.0 == alp_in) {
357     red_out = red_in;
358     gre_out = gre_in;
359     blu_out = blu_in;
360     return;
361   }
362   double hue, sat, val;
363   igs::color::rgb_to_hsv(red_in, gre_in, blu_in, hue, sat, val);
364   if (0.0 != hue_noise) {
365     hue += 360.0 * hue_noise * alp_in;
366     while (hue < 0.0) {
367       hue += 360.0;
368     }
369     while (360.0 <= hue) {
370       hue -= 360.0;
371     }
372   }
373   if (0.0 != sat_term.noise_range()) {
374     double shift_value = 0;
375     double satnoise    = sat_noise;
376     sat_term.exec(sat, satnoise, shift_value);
377     sat += shift_value * alp_in;
378     sat += satnoise * alp_in;
379     if (sat < 0.0) {
380       sat = 0.0;
381     } else if (1.0 < sat) {
382       sat = 1.0;
383     }
384     // if( 0.0 == sat ) hue = -1.0; // hsv_to_rgb(-)
385   }
386   if (0.0 != val_term.noise_range()) {
387     double shift_value = 0;
388     double valnoise    = val_noise;
389     val_term.exec(val, valnoise, shift_value);
390     val += shift_value * alp_in;
391     val += valnoise * alp_in;
392     if (val < 0.0) {
393       val = 0.0;
394     } else if (1.0 < val) {
395       val = 1.0;
396     }
397   }
398   igs::color::hsv_to_rgb(hue, sat, val, red_out, gre_out, blu_out);
399 }
pixel_a(const double alp_in,const double alp_noise,control_term_within_limits & alp_term,double & alp_out)400 void igs::hsv_noise_in_camera::pixel_a(const double alp_in,
401                                        const double alp_noise,
402                                        control_term_within_limits &alp_term,
403                                        double &alp_out) {
404   // if (0.0 == alp_in) { return; }
405   double alpin = alp_in;
406   if (0.0 != alp_term.noise_range()) {
407     double shift_value = 0.0;
408     double alpnoise    = alp_noise;
409     alp_term.exec(alpin, alpnoise, shift_value);
410     const double mask = alpin;
411     alpin += shift_value * mask;
412     alpin += alpnoise * mask;
413     if (alpin < 0.0) {
414       alpin = 0.0;
415     } else if (1.0 < alpin) {
416       alpin = 1.0;
417     }
418   }
419   alp_out = alpin;
420 }
421 namespace igs {
422 namespace hsv_noise_in_camera {
423 /* raster画像にノイズをのせるtemplate */
424 template <class T>
change_template_(T * image_array,const int ww,const int hh,const int ch,noise_reference & noise,const double hue_range,control_term_within_limits & sat_term,control_term_within_limits & val_term,control_term_within_limits & alp_term)425 void change_template_(T *image_array, const int ww, const int hh, const int ch,
426                       noise_reference &noise, const double hue_range,
427                       control_term_within_limits &sat_term,
428                       control_term_within_limits &val_term,
429                       control_term_within_limits &alp_term) {
430   const double div_val = static_cast<double>(std::numeric_limits<T>::max());
431   const double mul_val =
432       static_cast<double>(std::numeric_limits<T>::max()) + 0.999999;
433   double rr, gg, bb, aa;
434 
435   if (igs::image::rgba::siz == ch) {
436     using namespace igs::image::rgba;
437     for (int yy = 0; yy < hh; ++yy) {
438       for (int xx = 0; xx < ww; ++xx) {
439         if (((0.0 != hue_range) || (0.0 != val_term.noise_range()) ||
440              (0.0 !=
441               sat_term.noise_range())) /* ノイズがhsvのどれか一つはある */
442             ) {
443           pixel_rgb(static_cast<double>(image_array[red]) / div_val,
444                     static_cast<double>(image_array[gre]) / div_val,
445                     static_cast<double>(image_array[blu]) / div_val,
446                     static_cast<double>(image_array[alp]) / div_val,
447                     noise.hue_value(xx, yy), noise.sat_value(xx, yy),
448                     noise.val_value(xx, yy), sat_term, val_term, rr, gg, bb);
449           image_array[red] = static_cast<T>(rr * mul_val);
450           image_array[gre] = static_cast<T>(gg * mul_val);
451           image_array[blu] = static_cast<T>(bb * mul_val);
452         }
453         if (0.0 != alp_term.noise_range()) {
454           pixel_a(static_cast<double>(image_array[alp]) / div_val,
455                   noise.alp_value(xx, yy), alp_term, aa);
456           image_array[alp] = static_cast<T>(aa * mul_val);
457         }
458         image_array += ch;
459       }
460     }
461   } else if (igs::image::rgb::siz == ch) {
462     using namespace igs::image::rgb;
463     if (((0.0 != hue_range) || (0.0 != sat_term.noise_range()) ||
464          (0.0 != val_term.noise_range())) /* ノイズがhsvのどれか一つはある */
465         ) {
466       for (int yy = 0; yy < hh; ++yy) {
467         for (int xx = 0; xx < ww; ++xx, image_array += ch) {
468           pixel_rgb(static_cast<double>(image_array[red]) / div_val,
469                     static_cast<double>(image_array[gre]) / div_val,
470                     static_cast<double>(image_array[blu]) / div_val, 1.0,
471                     noise.hue_value(xx, yy), noise.sat_value(xx, yy),
472                     noise.val_value(xx, yy), sat_term, val_term, rr, gg, bb);
473           image_array[red] = static_cast<T>(rr * mul_val);
474           image_array[gre] = static_cast<T>(gg * mul_val);
475           image_array[blu] = static_cast<T>(bb * mul_val);
476         }
477       }
478     }
479   } else if (1 == ch) { /* grayscale */
480     if (0.0 != val_term.noise_range()) {
481       for (int yy = 0; yy < hh; ++yy) {
482         for (int xx = 0; xx < ww; ++xx, ++image_array) {
483           double val         = static_cast<double>(image_array[0]) / div_val;
484           double shift_value = 0;
485           double val_noise   = noise.val_value(xx, yy);
486           val_term.exec(val, val_noise, shift_value);
487           val += shift_value;
488           val += val_noise;
489           if (val < 0.0) {
490             val = 0.0;
491           } else if (1.0 < val) {
492             val = 1.0;
493           }
494           image_array[0] = static_cast<T>(val * mul_val);
495         }
496       }
497     }
498   }
499 }
500 }
501 }
502 
change(void * image_array,const int height,const int width,const int channels,const int bits,const int camera_x,const int camera_y,const int camera_w,const int camera_h,const double hue_range,const double sat_range,const double val_range,const double alp_range,const unsigned long random_seed,const double near_blur,const double sat_effective,const double sat_center,const int sat_type,const double val_effective,const double val_center,const int val_type,const double alp_effective,const double alp_center,const int alp_type)503 void igs::hsv_noise_in_camera::change(
504     void *image_array
505 
506     ,
507     const int height, const int width, const int channels, const int bits
508 
509     ,
510     const int camera_x, const int camera_y, const int camera_w,
511     const int camera_h
512 
513     ,
514     const double hue_range, const double sat_range, const double val_range,
515     const double alp_range, const unsigned long random_seed,
516     const double near_blur
517 
518     ,
519     const double sat_effective, const double sat_center, const int sat_type,
520     const double val_effective, const double val_center, const int val_type,
521     const double alp_effective, const double alp_center, const int alp_type) {
522   if ((0.0 == hue_range) && (0.0 == sat_range) && (0.0 == val_range) &&
523       (0.0 == alp_range)) {
524     return;
525   }
526 
527   if ((igs::image::rgba::siz != channels) &&
528       (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */
529       ) {
530     throw std::domain_error("Bad channels,Not rgba/rgb/grayscale");
531   }
532 
533   /* ノイズ参照画像を作成する */
534   noise_reference noise(width, height, hue_range, sat_range, val_range,
535                         alp_range, random_seed, near_blur, camera_x, camera_y,
536                         camera_w, camera_h);
537 
538   /* 端値を適度に調整する設定 */
539   control_term_within_limits sat_term(sat_effective, sat_effective, sat_center,
540                                       sat_type, sat_range);
541   control_term_within_limits val_term(val_effective, val_effective, val_center,
542                                       val_type, val_range);
543   control_term_within_limits alp_term(alp_effective, alp_effective, alp_center,
544                                       alp_type, alp_range);
545 
546   /* rgb(a)画像にhsv(a)でドットノイズを加える */
547   if (std::numeric_limits<unsigned char>::digits == bits) {
548     change_template_(static_cast<unsigned char *>(image_array), width, height,
549                      channels, noise, hue_range, sat_term, val_term, alp_term);
550     noise.clear(); /* ノイズ画像メモリ解放 */
551   } else if (std::numeric_limits<unsigned short>::digits == bits) {
552     change_template_(static_cast<unsigned short *>(image_array), width, height,
553                      channels, noise, hue_range, sat_term, val_term, alp_term);
554     noise.clear(); /* ノイズ画像メモリ解放 */
555   } else {
556     throw std::domain_error("Bad bits,Not uchar/ushort");
557   }
558 }
559