1 #include <iostream>
2 #include <vector>
3 #include <limits>  // std::numeric_limits
4 #include "igs_color_rgb_hsv.h"
5 #include "igs_math_random.h"
6 #include "igs_ifx_common.h"  // igs::image::rgba
7 namespace {
8 /*------ バッファをまとめて確保(return or throwで自動解放) ------*/
9 class noise_reference_ {
10 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)11   noise_reference_(const int ww, const int hh, const double hue_range,
12                    const double sat_range, const double val_range,
13                    const double alp_range, const unsigned long random_seed,
14                    const double near_blur, const int camera_x,
15                    const int camera_y, const int camera_w, const int camera_h)
16       : w_(ww), h_(hh), nblur_(near_blur) {
17     if (0 == ww) {
18       return;
19     }
20     if (0 == hh) {
21       return;
22     }
23 
24     /* 枚数ゼロ(rangeが全部ゼロ)なのでノイズかけない */
25     if ((0.0 == hue_range) && (0.0 == sat_range) && (0.0 == val_range) &&
26         (0.0 == alp_range)) {
27       return;
28     }
29 
30     /* memory確保 */
31     const int sz = ww * hh;
32     if (0.0 != hue_range) {
33       this->hue_array_.resize(sz);
34     }
35     if (0.0 != sat_range) {
36       this->sat_array_.resize(sz);
37     }
38     if (0.0 != val_range) {
39       this->val_array_.resize(sz);
40     }
41     if (0.0 != alp_range) {
42       this->alp_array_.resize(sz);
43     }
44 
45     /* タネを設定 */
46     igs::math::random hue_rand, sat_rand, val_rand, alp_rand;
47     unsigned long step = 0;
48     if (0.0 != hue_range) {
49       hue_rand.seed(random_seed + step++);
50     }
51     if (0.0 != sat_range) {
52       sat_rand.seed(random_seed + step++);
53     }
54     if (0.0 != val_range) {
55       val_rand.seed(random_seed + step++);
56     }
57     if (0.0 != alp_range) {
58       alp_rand.seed(random_seed + step++);
59     }
60     igs::math::random hue_rand_ext, sat_rand_ext, val_rand_ext, alp_rand_ext;
61     if (0.0 != hue_range) {
62       hue_rand_ext.seed(random_seed + step++);
63     }
64     if (0.0 != sat_range) {
65       sat_rand_ext.seed(random_seed + step++);
66     }
67     if (0.0 != val_range) {
68       val_rand_ext.seed(random_seed + step++);
69     }
70     if (0.0 != alp_range) {
71       alp_rand_ext.seed(random_seed + step++);
72     }
73 
74     /* ノイズを書く */
75     /* -???_range/2〜???_range/2 */
76     const int x1 = camera_x;
77     const int y1 = camera_y;
78     const int x2 = camera_x + camera_w - 1;
79     const int y2 = camera_y + camera_h - 1;
80 
81     /* マージンあるかないかでノイズパターンが変わってしまうため、
82 near_blur用に広げてはいけない */
83     // if (0.0 != near_blur) { x1-=1; x2+=1; y1-=1; y2+=1; }
84 
85     if (0.0 != hue_range) {
86       int pos = 0;
87       for (int yy = 0; yy < hh; ++yy) {
88         for (int xx = 0; xx < ww; ++xx, ++pos) {
89           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
90             /* 計算用マージンがあったらそこは別のパターン */
91             this->hue_array_[pos] = hue_range * (hue_rand_ext.next_d() - 0.5);
92           } else {
93             /* 計算用マージン幅が変化してもその中のノイズパターンは
94 変わらないようにマージン内は別計算 */
95             this->hue_array_[pos] = hue_range * (hue_rand.next_d() - 0.5);
96           }
97         }
98       }
99     }
100     if (0.0 != sat_range) {
101       int pos = 0;
102       for (int yy = 0; yy < hh; ++yy) {
103         for (int xx = 0; xx < ww; ++xx, ++pos) {
104           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
105             this->sat_array_[pos] = sat_range * (sat_rand_ext.next_d() - 0.5);
106           } else {
107             this->sat_array_[pos] = sat_range * (sat_rand.next_d() - 0.5);
108           }
109         }
110       }
111     }
112     if (0.0 != val_range) {
113       int pos = 0;
114       for (int yy = 0; yy < hh; ++yy) {
115         for (int xx = 0; xx < ww; ++xx, ++pos) {
116           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
117             this->val_array_[pos] = val_range * (val_rand_ext.next_d() - 0.5);
118           } else {
119             this->val_array_[pos] = val_range * (val_rand.next_d() - 0.5);
120           }
121         }
122       }
123     }
124     if (0.0 != alp_range) {
125       int pos = 0;
126       for (int yy = 0; yy < hh; ++yy) {
127         for (int xx = 0; xx < ww; ++xx, ++pos) {
128           if ((xx < x1) || (x2 < xx) || (yy < y1) || (y2 < yy)) {
129             this->alp_array_[pos] = alp_range * (alp_rand_ext.next_d() - 0.5);
130           } else {
131             this->alp_array_[pos] = alp_range * (alp_rand.next_d() - 0.5);
132           }
133         }
134       }
135     }
136   }
hue_value(const int xx,const int yy)137   double hue_value(const int xx, const int yy) {
138     return this->noise_value_(this->hue_array_, this->w_, this->h_, xx, yy,
139                               this->nblur_);
140   }
sat_value(const int xx,const int yy)141   double sat_value(const int xx, const int yy) {
142     return this->noise_value_(this->sat_array_, this->w_, this->h_, xx, yy,
143                               this->nblur_);
144   }
val_value(const int xx,const int yy)145   double val_value(const int xx, const int yy) {
146     return this->noise_value_(this->val_array_, this->w_, this->h_, xx, yy,
147                               this->nblur_);
148   }
alp_value(const int xx,const int yy)149   double alp_value(const int xx, const int yy) {
150     return this->noise_value_(this->alp_array_, this->w_, this->h_, xx, yy,
151                               this->nblur_);
152   }
clear()153   void clear() {
154     this->alp_array_.clear();
155     this->val_array_.clear();
156     this->sat_array_.clear();
157     this->hue_array_.clear();
158   }
~noise_reference_()159   ~noise_reference_() { this->clear(); }
160 
161 private:
162   const int w_;
163   const int h_;
164   const double nblur_;
165   std::vector<double> hue_array_;
166   std::vector<double> sat_array_;
167   std::vector<double> val_array_;
168   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)169   void accum_in_(const double *noise_array, const int ww, const int hh,
170                  const int xx, const int yy, double &accum_val,
171                  int &accum_count) {
172     if ((0 <= xx) && (xx < ww) && (0 <= yy) && (yy < hh)) {
173       accum_val += noise_array[yy * ww + xx];
174       ++accum_count;
175     }
176   }
noise_value_(const std::vector<double> & noise_vector,const int ww,const int hh,const int xx,const int yy,const double near_blur)177   double noise_value_(const std::vector<double> &noise_vector, const int ww,
178                       const int hh, const int xx, const int yy,
179                       const double near_blur) {
180     if (noise_vector.size() <= 0) {
181       return 0.0;
182     }
183     const double *noise_array = &noise_vector.at(0);
184     if (0.0 == near_blur) {
185       return noise_array[yy * ww + xx];
186     }
187 
188     double accum_val = 0.0;
189     int accum_count  = 0;
190     this->accum_in_(noise_array, ww, hh, xx - 1, yy - 1, accum_val,
191                     accum_count);
192     this->accum_in_(noise_array, ww, hh, xx, yy - 1, accum_val, accum_count);
193     this->accum_in_(noise_array, ww, hh, xx + 1, yy - 1, accum_val,
194                     accum_count);
195     this->accum_in_(noise_array, ww, hh, xx - 1, yy, accum_val, accum_count);
196     this->accum_in_(noise_array, ww, hh, xx + 1, yy, 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, yy + 1, accum_val, accum_count);
200     this->accum_in_(noise_array, ww, hh, xx + 1, yy + 1, accum_val,
201                     accum_count);
202     if (accum_count <= 0) {
203       return noise_array[yy * ww + xx];
204     }
205     accum_val /= static_cast<double>(accum_count);
206 
207     /* 中心Pixelと廻り(1Pixel幅)の平均値とのバランスを返す */
208     return (near_blur * accum_val) +
209            ((1.0 - near_blur) * noise_array[yy * ww + xx]);
210     /*
211 ノイズをなじませるために、
212 8近傍ピクセルと対象ピクセルとの重み付けをする処理
213 +-----+-----+-----+
214 | p00 | p01 | p02 |
215 +-----+-----+-----+
216 | p10 | p11 | p12 |
217 +-----+-----+-----+
218 | p20 | p21 | p22 |
219 +-----+-----+-----+
220 対象ピクセル = p11
221 8近傍ピクセル = p00,p01,p02,p10,p12,p20,p21,p22
222 重み付け = near_blur = nb = 0...1
223 注意:対象ピクセルが画像のエッジにあると、8近傍ピクセルには、
224     存在しないピクセルがあるのでそれは除いて計算する
225 p00+p01+p02+p10+p12+p20+p21+p22
226 ------------------------------- x nb + p22 x (1 - nb)
227             8
228                                   nb
229 = (p00+p01+p02+p10+p12+p20+p21+p22) x -- + p22 x (1 - nb)
230                                    8
231 nb == 0のとき
232     --> 対象ピクセルの値
233     = p22
234 nb == 0.5のとき
235     --> 対象が半分、8近傍平均値が半分
236     = (p00+p01+p02+p10+p12+p20+p21+p22) / 16 + p22 / 2
237 nb == 0.888...(8/9)のとき
238     --> 対象と8近傍クセルの平均値
239     = (p00+p01+p02+p10+p12+p20+p21+p22) x 8 / 72 + p22 / 9
240 nb == 1のとき
241     --> 8近傍ピクセルのみの平均値
242     = (p00+p01+p02+p10+p12+p20+p21+p22) / 8
243 */
244   }
245 
246   /* copy constructorを無効化 */
247   noise_reference_(const noise_reference_ &);
248 
249   /* 代入演算子を無効化 */
250   noise_reference_ &operator=(const noise_reference_ &);
251 };
252 /*------ 端値を適度に調整する ------*/
253 class control_term_within_limits_ {
254 public:
control_term_within_limits_(const double effective_low,const double effective_high,const double center,const int type,const double noise_range)255   control_term_within_limits_(const double effective_low /* = 0.0 */
256                               ,
257                               const double effective_high /* = 0.0 */
258                               ,
259                               const double center /* = 0.5 */
260                               ,
261                               const int type /* = 0   */
262                               ,
263                               const double noise_range /* = 0.0 */
264                               )
265       : effective_low_(effective_low)
266       , effective_high_(effective_high)
267       , center_(center)
268       , type_(static_cast<term_type_>(type))
269       , noise_range_(noise_range) {}
exec(const double current_value,double & noise,double & shift_value)270   void exec(const double current_value /* 0...1 */
271             ,
272             double &noise /* -noise_range/2...noise_range/2 */
273             ,
274             double &shift_value) {
275     if ((0.0 < this->effective_low_) && (current_value < this->center_)) {
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_low_;
280       switch (this->type_) {
281       case shift_all_:
282         shift_value = ((cen - val) / cen) * (ran / 2.0) * eff;
283         break;
284       case shift_term_:
285         if (val < ran) {
286           shift_value = (((cen < ran) ? cen : ran) - val) / 2.0 * eff;
287         }
288         break;
289       case decrease_all_: {
290         const double tmp = (cen - val) / cen * eff;
291         if (0.0 < tmp) {
292           noise *= 1.0 - tmp;
293         }
294       } break;
295       case decrease_term_:
296         if (val < (ran / 2.0)) {
297           const double stop = (cen < (ran / 2.0)) ? cen : ran / 2.0;
298           const double tmp  = (stop - val) / stop * eff;
299           if (0.0 < tmp) {
300             noise *= 1.0 - tmp;
301           }
302         }
303         break;
304       }
305     }
306 
307     if ((0.0 < this->effective_high_) && (this->center_ < current_value)) {
308       const double cen = this->center_;
309       const double val = current_value;
310       const double ran = this->noise_range_;
311       const double eff = this->effective_high_;
312       switch (this->type_) {
313       case shift_all_:
314         shift_value = ((cen - val) / (1.0 - cen)) * (ran / 2.0) * eff;
315         break;
316       case shift_term_:
317         if ((1.0 - ran) < val) {
318           const double ira = 1.0 - ran;
319           shift_value      = (((cen < ira) ? ira : cen) - val) / 2.0 * eff;
320         }
321         break;
322       case decrease_all_: {
323         const double tmp = (val - cen) / (1.0 - cen) * eff;
324         if (0.0 < tmp) {
325           noise *= 1.0 - tmp;
326         }
327       } break;
328       case decrease_term_:
329         if ((1.0 - (ran / 2.0)) < val) {
330           const double rpos = 1.0 - (ran / 2.0);
331           const double stop = (cen < rpos) ? rpos : cen;
332           const double tmp  = (val - stop) / (1.0 - stop) * eff;
333           if (0.0 < tmp) {
334             noise *= 1.0 - tmp;
335           }
336         }
337         break;
338       }
339     }
340   }
noise_range(void) const341   double noise_range(void) const { return this->noise_range_; }
342 
343 private:
344   /* low,high両方ゼロ =exec()内処理せず =端値はカット =default */
345   const double effective_low_;
346   const double effective_high_;
347 
348   const double center_;
349 
350   enum term_type_ {                 /* 端値の調整方法 */
351                     shift_all_ = 0, /* 0.全体的にノイズ位置がずれる */
352                     shift_term_, /* 1.端のみでノイズ位置がずれる */
353                     decrease_all_,  /* 2.全体的にノイズ幅が減る */
354                     decrease_term_, /* 3.端のみでノイズ幅が減る */
355   };
356   const term_type_ type_;
357 
358   const double noise_range_;
359 
360   /* copy constructorを無効化 */
361   control_term_within_limits_(const control_term_within_limits_ &);
362 
363   /* 代入演算子を無効化 */
364   control_term_within_limits_ &operator=(const control_term_within_limits_ &);
365 };
366 /*------ RGB値にノイズをのせる ------*/
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)367 void pixel_rgb_(const double red_in, const double gre_in, const double blu_in,
368                 const double alp_in, const double hue_noise,
369                 const double sat_noise, const double val_noise,
370                 control_term_within_limits_ &sat_term,
371                 control_term_within_limits_ &val_term, double &red_out,
372                 double &gre_out, double &blu_out) {
373   if (0.0 == alp_in) {
374     red_out = red_in;
375     gre_out = gre_in;
376     blu_out = blu_in;
377     return;
378   }
379   double hue, sat, val;
380   igs::color::rgb_to_hsv(red_in, gre_in, blu_in, hue, sat, val);
381   if (0.0 != hue_noise) {
382     hue += 360.0 * hue_noise * alp_in;
383     while (hue < 0.0) {
384       hue += 360.0;
385     }
386     while (360.0 <= hue) {
387       hue -= 360.0;
388     }
389   }
390   if (0.0 != sat_term.noise_range()) {
391     double shift_value = 0;
392     double satnoise    = sat_noise;
393     sat_term.exec(sat, satnoise, shift_value);
394     sat += shift_value * alp_in;
395     sat += satnoise * alp_in;
396     if (sat < 0.0) {
397       sat = 0.0;
398     } else if (1.0 < sat) {
399       sat = 1.0;
400     }
401     // if( 0.0 == sat ) hue = -1.0; // hsv_to_rgb(-)
402   }
403   if (0.0 != val_term.noise_range()) {
404     double shift_value = 0;
405     double valnoise    = val_noise;
406     val_term.exec(val, valnoise, shift_value);
407     val += shift_value * alp_in;
408     val += valnoise * alp_in;
409     if (val < 0.0) {
410       val = 0.0;
411     } else if (1.0 < val) {
412       val = 1.0;
413     }
414   }
415   igs::color::hsv_to_rgb(hue, sat, val, red_out, gre_out, blu_out);
416 }
417 /*------ Alpha値にノイズをのせる ------*/
pixel_a_(const double alp_in,const double alp_noise,control_term_within_limits_ & alp_term,double & alp_out)418 void pixel_a_(const double alp_in, const double alp_noise,
419               control_term_within_limits_ &alp_term, double &alp_out) {
420   // if (0.0 == alp_in) { return; }
421   double alpin = alp_in;
422   if (0.0 != alp_term.noise_range()) {
423     double shift_value = 0.0;
424     double alpnoise    = alp_noise;
425     alp_term.exec(alpin, alpnoise, shift_value);
426     const double mask = alpin;
427     alpin += shift_value * mask;
428     alpin += alpnoise * mask;
429     if (alpin < 0.0) {
430       alpin = 0.0;
431     } else if (1.0 < alpin) {
432       alpin = 1.0;
433     }
434   }
435   alp_out = alpin;
436 }
437 /*------ raster画像にノイズをのせるtemplate ------*/
438 template <class IT, class RT>
change_template_(IT * image_array,const int width,const int height,const int channels,const RT * ref,const int ref_mode,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,const bool add_blend_sw)439 void change_template_(
440     IT *image_array, const int width, const int height, const int channels
441 
442     ,
443     const RT *ref /* 求める画像(out)と同じ高さ、幅、チャンネル数 */
444     ,
445     const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */
446 
447     ,
448     noise_reference_ &noise, const double hue_range,
449     control_term_within_limits_ &sat_term,
450     control_term_within_limits_ &val_term, control_term_within_limits_ &alp_term
451 
452     ,
453     const bool add_blend_sw) {
454   const int t_max      = std::numeric_limits<IT>::max();
455   const double div_val = static_cast<double>(t_max);
456   const double mul_val = static_cast<double>(t_max) + 0.999999;
457   const int r_max      = std::numeric_limits<RT>::max();
458   if (igs::image::rgba::siz == channels) {
459     using namespace igs::image::rgba;
460     for (int yy = 0; yy < height; ++yy) {
461       for (int xx = 0; xx < width; ++xx, image_array += channels) {
462         /* 変化量初期値 */
463         double refv = 1.0;
464 
465         /* 参照画像あればピクセル単位の画像変化量を得る */
466         if (ref != 0) {
467           refv *= igs::color::ref_value(ref, channels, r_max, ref_mode);
468           ref += channels; /* continue;の前に行うこと */
469         }
470         /* 加算合成で、Alpha値ゼロならRGB値を計算する必要はない */
471         if (add_blend_sw && (0 == image_array[alp])) {
472           continue;
473         }
474         /* 加算合成でなくAlpha合成の時は、
475 Alpha値がゼロでもRGB値は存在する(してもよい) */
476 
477         /* マスクSWがON、なら変化をMask */
478         if (add_blend_sw && (image_array[alp] < t_max)) {
479           refv *= static_cast<double>(image_array[alp]) / div_val;
480         }
481 
482         if (((0.0 != hue_range) || (0.0 != val_term.noise_range()) ||
483              (0.0 !=
484               sat_term.noise_range())) /* ノイズがhsvのどれか一つはある */
485             ) {
486           double rr1 = static_cast<double>(image_array[red]) / div_val,
487                  gg1 = static_cast<double>(image_array[gre]) / div_val,
488                  bb1 = static_cast<double>(image_array[blu]) / div_val,
489                  aa1 = static_cast<double>(image_array[alp]) / div_val;
490           double rr2 = 0, gg2 = 0, bb2 = 0;
491           pixel_rgb_(rr1, gg1, bb1, aa1, noise.hue_value(xx, yy),
492                      noise.sat_value(xx, yy), noise.val_value(xx, yy), sat_term,
493                      val_term, rr2, gg2, bb2);
494           if (refv != 1.0) {
495             rr2 = (rr2 - rr1) * refv + rr1;
496             gg2 = (gg2 - gg1) * refv + gg1;
497             bb2 = (bb2 - bb1) * refv + bb1;
498           }
499           image_array[red] = static_cast<IT>(rr2 * mul_val);
500           image_array[gre] = static_cast<IT>(gg2 * mul_val);
501           image_array[blu] = static_cast<IT>(bb2 * mul_val);
502         }
503         if (0.0 != alp_term.noise_range()) {
504           double aa1 = static_cast<double>(image_array[alp]) / div_val;
505           double aa2 = 0;
506           pixel_a_(aa1, noise.alp_value(xx, yy), alp_term, aa2);
507           if (refv != 1.0) {
508             aa2 = (aa2 - aa1) * refv + aa1;
509           }
510           image_array[alp] = static_cast<IT>(aa2 * mul_val);
511         }
512       }
513     }
514   } else if (igs::image::rgb::siz == channels) {
515     using namespace igs::image::rgb;
516     if (((0.0 != hue_range) || (0.0 != sat_term.noise_range()) ||
517          (0.0 != val_term.noise_range())) /* ノイズがhsvのどれか一つはある */
518         ) {
519       for (int yy = 0; yy < height; ++yy) {
520         for (int xx = 0; xx < width; ++xx, image_array += channels) {
521           /* 変化量初期値 */
522           double refv = 1.0;
523 
524           /* 参照画像あればピクセル単位の画像変化量を得る */
525           if (ref != 0) {
526             refv *= igs::color::ref_value(ref, channels, r_max, ref_mode);
527             ref += channels; /* continue;の前に行うこと */
528           }
529 
530           double rr1 = static_cast<double>(image_array[red]) / div_val,
531                  gg1 = static_cast<double>(image_array[gre]) / div_val,
532                  bb1 = static_cast<double>(image_array[blu]) / div_val;
533           double rr2 = 0, gg2 = 0, bb2 = 0;
534           pixel_rgb_(rr1, gg1, bb1, 1.0, noise.hue_value(xx, yy),
535                      noise.sat_value(xx, yy), noise.val_value(xx, yy), sat_term,
536                      val_term, rr2, gg2, bb2);
537           if (refv != 1.0) {
538             rr2 = (rr2 - rr1) * refv + rr1;
539             gg2 = (gg2 - gg1) * refv + gg1;
540             bb2 = (bb2 - bb1) * refv + bb1;
541           }
542           image_array[red] = static_cast<IT>(rr2 * mul_val);
543           image_array[gre] = static_cast<IT>(gg2 * mul_val);
544           image_array[blu] = static_cast<IT>(bb2 * mul_val);
545         }
546       }
547     }
548   } else if (1 == channels) { /* grayscale */
549     if (0.0 != val_term.noise_range()) {
550       for (int yy = 0; yy < height; ++yy) {
551         for (int xx = 0; xx < width; ++xx, ++image_array) {
552           /* 変化量初期値 */
553           double refv = 1.0;
554 
555           /* 参照画像あればピクセル単位の画像変化量を得る */
556           if (ref != 0) {
557             refv *= igs::color::ref_value(ref, channels, r_max, ref_mode);
558             ref += channels; /* continue;の前に行うこと */
559           }
560 
561           double va1         = static_cast<double>(image_array[0]) / div_val;
562           double shift_value = 0;
563           double val_noise   = noise.val_value(xx, yy);
564           val_term.exec(va1, val_noise, shift_value);
565 
566           double va2 = va1;
567           va2 += shift_value;
568           va2 += val_noise;
569           va2 = (va2 < 0.0) ? 0.0 : ((1.0 < va2) ? 1.0 : va2);
570 
571           if (refv != 1.0) {
572             va2 = va1 + (va2 - va1) * refv;
573           }
574 
575           image_array[0] = static_cast<IT>(va2 * mul_val);
576         }
577       }
578     }
579   }
580 }
581 }
582 //--------------------------------------------------------------------
583 
584 #include <stdexcept>  // std::domain_error
585 #include "igs_hsv_noise.h"
change(unsigned char * image_array,const int height,const int width,const int channels,const int bits,const unsigned char * ref,const int ref_bits,const int ref_mode,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,const bool add_blend_sw)586 void igs::hsv_noise::change(
587     unsigned char *image_array
588 
589     ,
590     const int height, const int width, const int channels, const int bits
591 
592     ,
593     const unsigned char *ref /* 求める画像と同じ高、幅、channels数 */
594     ,
595     const int ref_bits /* refがゼロのときはここもゼロ */
596     ,
597     const int ref_mode /* 0=R,1=G,2=B,3=A,4=Luminance,5=Nothing */
598 
599     /* image_arrayに余白が変化してもノイズパターンが変わらない
600             ようにするためにカメラエリアを指定する */
601     ,
602     const int camera_x, const int camera_y, const int camera_w,
603     const int camera_h
604 
605     ,
606     const double hue_range, const double sat_range, const double val_range,
607     const double alp_range, const unsigned long random_seed,
608     const double near_blur
609 
610     ,
611     const double sat_effective, const double sat_center, const int sat_type,
612     const double val_effective, const double val_center, const int val_type,
613     const double alp_effective, const double alp_center, const int alp_type
614 
615     ,
616     const bool add_blend_sw) {
617   if ((0.0 == hue_range) && (0.0 == sat_range) && (0.0 == val_range) &&
618       (0.0 == alp_range)) {
619     return;
620   }
621 
622   if ((igs::image::rgba::siz != channels) &&
623       (igs::image::rgb::siz != channels) && (1 != channels) /* grayscale */
624       ) {
625     throw std::domain_error("Bad channels,Not rgba/rgb/grayscale");
626   }
627 
628   /* ノイズ参照画像を作成する */
629   noise_reference_ noise(width, height, hue_range, sat_range, val_range,
630                          alp_range, random_seed, near_blur, camera_x, camera_y,
631                          camera_w, camera_h);
632 
633   /* 端値を適度に調整する設定 */
634   control_term_within_limits_ sat_term(sat_effective, sat_effective, sat_center,
635                                        sat_type, sat_range);
636   control_term_within_limits_ val_term(val_effective, val_effective, val_center,
637                                        val_type, val_range);
638   control_term_within_limits_ alp_term(alp_effective, alp_effective, alp_center,
639                                        alp_type, alp_range);
640 
641   /* rgb(a)画像にhsv(a)でドットノイズを加える */
642   if ((std::numeric_limits<unsigned char>::digits == bits) &&
643       ((std::numeric_limits<unsigned char>::digits == ref_bits) ||
644        (0 == ref_bits))) {
645     change_template_(image_array, width, height, channels, ref, ref_mode, noise,
646                      hue_range, sat_term, val_term, alp_term, add_blend_sw);
647     noise.clear(); /* ノイズ画像メモリ解放 */
648   } else if ((std::numeric_limits<unsigned short>::digits == bits) &&
649              ((std::numeric_limits<unsigned char>::digits == ref_bits) ||
650               (0 == ref_bits))) {
651     change_template_(reinterpret_cast<unsigned short *>(image_array), width,
652                      height, channels, ref, ref_mode, noise, hue_range,
653                      sat_term, val_term, alp_term, add_blend_sw);
654     noise.clear(); /* ノイズ画像メモリ解放 */
655   } else if ((std::numeric_limits<unsigned short>::digits == bits) &&
656              (std::numeric_limits<unsigned short>::digits == ref_bits)) {
657     change_template_(
658         reinterpret_cast<unsigned short *>(image_array), width, height,
659         channels, reinterpret_cast<const unsigned short *>(ref), ref_mode,
660         noise, hue_range, sat_term, val_term, alp_term, add_blend_sw);
661     noise.clear(); /* ノイズ画像メモリ解放 */
662   } else if ((std::numeric_limits<unsigned char>::digits == bits) &&
663              (std::numeric_limits<unsigned short>::digits == ref_bits)) {
664     change_template_(image_array, width, height, channels,
665                      reinterpret_cast<const unsigned short *>(ref), ref_mode,
666                      noise, hue_range, sat_term, val_term, alp_term,
667                      add_blend_sw);
668     noise.clear(); /* ノイズ画像メモリ解放 */
669   } else {
670     throw std::domain_error("Bad bits,Not uchar/ushort");
671   }
672 }
673