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