- メインページ
- モジュール
- ネームスペース
- データ構造
- ファイル

- ファイル一覧
- グローバル

poisson_blending.h
説明を見る。
1 //
2 // Copyright (c) 2003-2011, MIST Project, Nagoya University
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // 3. Neither the name of the Nagoya University nor the names of its contributors
16 // may be used to endorse or promote products derived from this software
17 // without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
20 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //
28
33 #ifndef __INCLUDE_POISSON_BLENDING__
34 #define __INCLUDE_POISSON_BLENDING__
35
36
37 #ifndef __INCLUDE_MIST_VECTOR__
38 #include "vector.h"
39 #endif
40
41 #ifndef __INCLUDE_NUMERIC__
42 #include "numeric.h"
43 #endif
44
45 #ifndef __INCLUDE_CONVERTER__
46 #include <mist/converter.h>
47 #endif
48
49 #ifndef __INCLUDE_MIST_MORPHOLOGY__
50 #include <mist/filter/morphology.h>
51 #endif
52
53
54
55 #include <map>
56 #include <stdio.h>
57
58
59
60 // mist名前空間の始まり
61 _MIST_BEGIN
62
63
64 namespace __poissonblender__
65 {
66 typedef unsigned char mono_type;
67 typedef rgb< unsigned char > color_type;
68 typedef rgb< double > differencial_type;
69 typedef array2< mono_type > mono_image_type;
70 typedef array2< color_type > color_image_type;
71 typedef array2< differencial_type > differencial_image_type;
72 typedef vector2< int > point_type; // ( x, y )
73 typedef vector2< point_type > rectangle_type; // ( ( tl_x, tl_y ), ( br_x, br_y ) )
74
75 template <typename T>
76 inline void _recTrim( array2< T > &out, const array2< T > &in, const rectangle_type &rec )
77 {
78 in.trim ( out, rec.x.x, rec.x.y, ( rec.y.x - rec.x.x ), ( rec.y.y - rec.x.y ) );
79 }
80
81 template <typename T>
82 inline void _recTrim( array2< T > &in, const rectangle_type &rec )
83 {
84 in.trim ( rec.x.x, rec.x.y, ( rec.y.x - rec.x.x ), ( rec.y.y - rec.x.y ) );
85 }
86
87 // clipping
88 template <typename T>
89 inline unsigned char _saturate_cast( T num )
90 {
91 if ( num > 255 ) return 255;
92 else if ( num < 0 ) return 0;
93 else return ( static_cast< unsigned char >(num) );
94 }
95
96 // calc differencial
97 template <typename T> // return im1 -= im2
98 inline void _differencial( array2< T > &im1, const array2< T > &im2, const bool integral = false )
99 {
100 unsigned int w = im1.width() < im2.width() ? im1.width() : im2.width();
101 unsigned int h = im1.height() < im2.height() ? im1.height() : im2.height();
102 array2< T > tmp = im1;
103 im1.resize( w, h );
104 for ( unsigned int j = 0; j < h; j++ ) {
105 for ( unsigned int i = 0; i < w; i++ ) {
106 if ( integral ) im1( i, j ) = tmp( i, j ) + im2( i, j );
107 else im1( i, j ) = tmp( i, j ) - im2( i, j );
108 }
109 }
110 }
111
112 // build matrix as linear system
113 template <typename T>
114 inline bool _buildMatrix( matrix< T > &A, matrix< T > &b, matrix< T > &u,
115 const unsigned int &color,
116 const mono_image_type &_mask1,
117 std::map<int, int> &_mp,
118 differencial_image_type &_drvxy,
119 const color_image_type &_target1 )
120 {
121 int w = _mask1.width();
122 int h = _mask1.height();
123
124 if ( w == 0 || h == 0 ) {
125 std::cerr << "mask size error w:" << w << ",h:" << h << std::endl;
126 return false;
127 }
128
129 unsigned int nz=0;
130 unsigned int max_row = 0;
131 _mp.clear();
132 for ( int y = 0; y < h - 1; ++y )
133 {
134 for ( int x = 0; x < w - 1; ++x )
135 {
136 if ( _mask1( x, y ) == 0 ) continue;
137
138 int id = y * w + x;
139 _mp[ id ] = nz++;
140 if ( _mp.count( (y - 1) * w + x ) != 0)
141 {
142 unsigned int diff = _mp[ id ] - _mp[ (y - 1) * w + x ];
143 if ( diff > max_row ) max_row = diff;
144 }
145 }
146 }
147 if ( !A.resize( max_row + 1, nz ) ) {
148 std::cerr << "cant allocate matrix( "<< max_row + 1 <<", "<< nz <<" )" << std::endl;
149 std::cerr << "mask size is too large." << std::endl;
150 return false;
151 }
152 if ( !b.resize( nz, 1 ) ) {
153 std::cerr << "mask size is too large." << std::endl;
154 return false;
155 }
156 if ( !u.resize( nz, 1 ) ){
157 std::cerr << "mask size is too large." << std::endl;
158 return false;
159 }
160
161 int rowA = 0;
162
163 for ( int y = 1; y < h - 1; ++y )
164 {
165 for ( int x = 1; x < w - 1; ++x )
166 {
167 if ( _mask1( x, y ) == 0 ) continue;
168 differencial_type &drv = _drvxy( x, y );
169
170 int id = y * w + x;
171 int tidx = id - w, lidx = id - 1;
172
173 // to omtimize insertion
174 unsigned char tlrb = 15; // 0b1111
175 if ( _mask1( x, y - 1 ) == 0 ) {
176 if ( color == 0 ) drv.r -= static_cast< double >( _target1( x, y - 1 ).r );
177 if ( color == 1 ) drv.g -= static_cast< double >( _target1( x, y - 1 ).g );
178 if ( color == 2 ) drv.b -= static_cast< double >( _target1( x, y - 1 ).b );
179 tlrb &= 7; //0b0111
180 }
181 if ( _mask1( x - 1, y ) == 0 ) {
182 if ( color == 0 ) drv.r -= static_cast< double >( _target1( x - 1, y ).r );
183 if ( color == 1 ) drv.g -= static_cast< double >( _target1( x - 1, y ).g );
184 if ( color == 2 ) drv.b -= static_cast< double >( _target1( x - 1, y ).b );
185 tlrb &= 11; //0b1011
186 }
187 if ( _mask1( x + 1, y ) == 0 ) {
188 if ( color == 0 ) drv.r -= static_cast< double >( _target1( x + 1, y ).r );
189 if ( color == 1 ) drv.g -= static_cast< double >( _target1( x + 1, y ).g );
190 if ( color == 2 ) drv.b -= static_cast< double >( _target1( x + 1, y ).b );
191 tlrb &= 13; //0b1101
192 }
193 if ( _mask1( x, y + 1 ) == 0 ) {
194 if ( color == 0 ) drv.r -= static_cast< double >( _target1( x, y + 1 ).r );
195 if ( color == 1 ) drv.g -= static_cast< double >( _target1( x, y + 1 ).g );
196 if ( color == 2 ) drv.b -= static_cast< double >( _target1( x, y + 1 ).b );
197 tlrb &= 14; //0b1110
198 }
199
200 // transform upper triangle matrix
201 if ( tlrb & 8 ) A( _mp[ tidx ] - rowA + ( max_row ), rowA ) = -1.0; // top
202 if ( tlrb & 4 ) A( _mp[ lidx ] - rowA + ( max_row ), rowA ) = -1.0; // left
203 A( _mp[ id ] - rowA + ( max_row ), rowA ) = 4.0; // center
204
205 if ( color == 0 ) b( rowA, 0 ) = drv.r;
206 if ( color == 1 ) b( rowA, 0 ) = drv.g;
207 if ( color == 2 ) b( rowA, 0 ) = drv.b;
208
209 rowA++;
210
211 }
212 }
213
214 if ( nz != static_cast< unsigned int >( rowA ) ){
215 std::cerr << "unexpected error!" << std::endl;
216 return false;
217 }
218
219 return true;
220 }
221
222 // solver sparse linear system
223 template < typename T >
224 inline bool _solve(const matrix< T > &A, matrix< T > &b, matrix< T > &u)
225 {
226 try
227 {
228 u = solve( A, b, matrix_style::pb );
229 }
230 catch( numerical_exception &e )
231 {
232 std::cerr << "solving failed" << std::endl;
233 std::cerr << e.message << std::endl;
234 return false;
235 }
236
237 return true;
238 }
239
240 template <typename T>
241 inline bool _copyResult(matrix< T > &u, const unsigned int &color, const mono_image_type &_mask1,
242 std::map< int, int > &_mp, color_image_type &_dst1, const color_image_type &_src1 )
243 {
244 int w = _mask1.width();
245 int h = _mask1.height();
246 for ( int y = 1; y < h - 1; ++y ) {
247 for ( int x = 1; x < w - 1; ++x )
248 {
249 if( _mask1( x, y ) == 0 ) continue;
250 else
251 {
252 int idx = _mp[ y * w + x ];
253 color_type &pd = _dst1( x, y );
254 if ( color == 0 ) pd.r = _saturate_cast( -u( idx, 0 ) );
255 if ( color == 1 ) pd.g = _saturate_cast( -u( idx, 0 ) );
256 if ( color == 2 ) pd.b = _saturate_cast( -u( idx, 0 ) );
257 }
258 }
259 }
260
261 return true;
262 }
263 }
264
265
273
274 template< class Array1, class Array2, class Array3 >
275 inline bool __seamlessCloning(
276 const Array1 &src, const Array2 &target, Array3 &out,
277 const array2< unsigned char > &mask,
278 const int offx = 0, const int offy = 0, const bool mix = false, const bool ill_change = false, const double alpha = 0.2, const double beta = 0.2 )
279 {
280 __poissonblender__::mono_image_type _mask = mask;
281 if ( src.empty() || target.empty() || _mask.empty() ) return false;
282
283 //
284 __poissonblender__::point_type offset( offx, offy );
285 __poissonblender__::point_type tl( _mask.size1(), _mask.size2() ), br( -1, -1 );
286
287 // binary transform
288 for ( unsigned int y = 0; y < _mask.height(); ++y ) {
289 for ( unsigned int x = 0; x < _mask.width(); ++x )
290 {
291 if ( _mask( x, y ) < 50 ) _mask( x, y ) = 0;
292 else _mask( x, y ) = 255;
293 }
294 }
295
296 // calc bounding box
297 for ( unsigned int y = 0; y < _mask.height(); ++y ) {
298 for ( unsigned int x = 0; x < _mask.width(); ++x )
299 {
300 if ( _mask( x, y ) == 0 ) continue;
301 if ( tl.x > (int)x ) tl.x = (int)x;
302 if ( tl.y > (int)y ) tl.y = (int)y;
303 if ( br.x < (int)x ) br.x = (int)x;
304 if ( br.y < (int)y ) br.y = (int)y;
305 }
306 }
307 br.x += 1;
308 br.y += 1;
309
310 // add borders
311 __poissonblender__::rectangle_type mask_roi ( tl, br );
312 __poissonblender__::rectangle_type mask_roi1( tl - __poissonblender__::point_type( 1, 1 ), br + __poissonblender__::point_type( 1, 1 ) );
313 __poissonblender__::rectangle_type mask_roi2( tl - __poissonblender__::point_type( 2, 2 ), br + __poissonblender__::point_type( 2, 2 ) );
314
315 marray< __poissonblender__::color_image_type > _srcUP( src.width(), src.height(), 2 ), _targetUP( target.width(), target.height(), 2 ), _dstUP;
316 marray< __poissonblender__::mono_image_type > _maskUP( _mask.width(), _mask.height(), 1 );
317 _srcUP = src;
318 _targetUP = target;
319 _maskUP = _mask;
320
321 // allocate destination image
322 __poissonblender__::color_image_type dst = target;
323 _dstUP = _targetUP;
324
325 __poissonblender__::mono_image_type _mask1;
326 __poissonblender__::color_image_type _dst1;
327 __poissonblender__::color_image_type _target1;
328 __poissonblender__::color_image_type _src1;
329
330 __poissonblender__::_recTrim( _mask1, _maskUP, __poissonblender__::rectangle_type( mask_roi1.x + __poissonblender__::point_type( 1, 1 ), mask_roi1.y + __poissonblender__::point_type( 1, 1 ) ) );
331 __poissonblender__::_recTrim( _target1, _targetUP, __poissonblender__::rectangle_type( mask_roi1.x + offset + __poissonblender__::point_type( 2, 2 ), mask_roi1.y + offset + __poissonblender__::point_type( 2, 2 ) ) );
332 __poissonblender__::_recTrim( _src1, _srcUP, __poissonblender__::rectangle_type( mask_roi1.x + offset + __poissonblender__::point_type( 2, 2 ), mask_roi1.y + offset + __poissonblender__::point_type( 2, 2 ) ) );
333 __poissonblender__::_recTrim( _dst1, _dstUP, __poissonblender__::rectangle_type( mask_roi1.x + offset + __poissonblender__::point_type( 2, 2 ), mask_roi1.y + offset + __poissonblender__::point_type( 2, 2 ) ) );
334
335 __poissonblender__::color_image_type _src, _target, _dst;
336 __poissonblender__::_recTrim( _src, _srcUP, __poissonblender__::rectangle_type( mask_roi2.x + offset + __poissonblender__::point_type( 2, 2 ), mask_roi2.y + offset + __poissonblender__::point_type( 2, 2 ) ) );
337 __poissonblender__::_recTrim( _target, _targetUP, __poissonblender__::rectangle_type( mask_roi2.x + offset + __poissonblender__::point_type( 2, 2 ), mask_roi2.y + offset + __poissonblender__::point_type( 2, 2 ) ) );
338 __poissonblender__::_recTrim( _dst, _dstUP, __poissonblender__::rectangle_type( mask_roi2.x + offset + __poissonblender__::point_type( 2, 2 ), mask_roi2.y + offset + __poissonblender__::point_type( 2, 2 ) ) );
339
340 if ( _src.height() != _dst.height() || _src.width() != _dst.width() ) return false;
341
342 // calc differential image
343 __poissonblender__::differencial_image_type src64, target64;
344 __poissonblender__::differencial_image_type _src64_00, _target64_00;
345
346 int pw = mask_roi2.y.x - mask_roi2.x.x - 1, ph = mask_roi2.y.y - mask_roi2.x.y -1;
347 src64 = _src;
348 target64 = _target;
349 __poissonblender__::rectangle_type roi00( __poissonblender__::point_type( 0, 0 ), __poissonblender__::point_type( pw, ph ) );
350 __poissonblender__::rectangle_type roi10( __poissonblender__::point_type( 1, 0 ), __poissonblender__::point_type( pw, ph ) );
351 __poissonblender__::rectangle_type roi01( __poissonblender__::point_type( 0, 1 ), __poissonblender__::point_type( pw, ph ) );
352 __poissonblender__::_recTrim( _src64_00, src64, roi00 );
353 __poissonblender__::_recTrim( _target64_00, target64, roi00 );
354
355 __poissonblender__::differencial_image_type src_dx, src_dy, target_dx, target_dy;
356 __poissonblender__::_recTrim( src_dx, src64, roi10 ); __poissonblender__::_differencial( src_dx, _src64_00 );
357 __poissonblender__::_recTrim( src_dy, src64, roi01 ); __poissonblender__::_differencial( src_dy, _src64_00 );
358 __poissonblender__::_recTrim( target_dx, target64, roi10 ); __poissonblender__::_differencial( target_dx, _target64_00 );
359 __poissonblender__::_recTrim( target_dy, target64, roi01 ); __poissonblender__::_differencial( target_dy, _target64_00 );
360
361 // gradient mixture
362 __poissonblender__::differencial_image_type Dx, Dy;
363 if ( mix )
364 { // with gradient mixture
365 unsigned int wx = src_dx.width();
366 unsigned int hx = src_dx.height();
367 Dx.resize( wx, hx );
368 unsigned int wy = src_dy.width();
369 unsigned int hy = src_dy.height();
370 Dy.resize( wy, hy );
371
372 for ( unsigned int j = 0; j < hx; j++ ) {
373 for ( unsigned int i = 0; i < wx; i++ )
374 { //
375 Dx( i, j ).r = abs( src_dx( i, j ).r ) > abs( target_dx( i, j ).r ) ? src_dx( i, j ).r : target_dx( i, j ).r;
376 Dx( i, j ).g = abs( src_dx( i, j ).g ) > abs( target_dx( i, j ).g ) ? src_dx( i, j ).g : target_dx( i, j ).g;
377 Dx( i, j ).b = abs( src_dx( i, j ).b ) > abs( target_dx( i, j ).b ) ? src_dx( i, j ).b : target_dx( i, j ).b;
378 }
379 }
380 for ( unsigned int j = 0; j < hy; j++ ) {
381 for ( unsigned int i = 0; i < wy; i++ )
382 { //
383 Dy( i, j ).r = abs( src_dy( i, j ).r ) > abs( target_dy( i, j ).r ) ? src_dy( i, j ).r : target_dy( i, j ).r;
384 Dy( i, j ).g = abs( src_dy( i, j ).g ) > abs( target_dy( i, j ).g ) ? src_dy( i, j ).g : target_dy( i, j ).g;
385 Dy( i, j ).b = abs( src_dy( i, j ).b ) > abs( target_dy( i, j ).b ) ? src_dy( i, j ).b : target_dy( i, j ).b;
386 }
387 }
388 }
389 else
390 { // without gradient mixture
391 Dx = src_dx;
392 Dy = src_dy;
393 }
394
395 if ( ill_change )
396 { //Local illumination change
397 //wx+1 = wy, hx = hy+1の関係
398 unsigned int wx = target_dx.width();
399 unsigned int hx = target_dx.height();
400 Dx.resize( wx, hx );
401 unsigned int wy = target_dy.width();
402 unsigned int hy = target_dy.height();
403 Dy.resize( wy, hy );
404
405 double alpha = 0.2f, beta = 0.2f;
406 double norm;
407
408 for ( unsigned int j = 0; j < hy; j++ ) {
409 for ( unsigned int i = 0; i < wx; i++ )
410 { //
411 norm = sqrt( target_dx( i, j ).r * target_dx( i, j ).r + target_dy( i, j ).r * target_dy( i, j ).r );
412 if( norm == 0 ) {
413 Dx( i, j ).r = 0;
414 Dy( i, j ).r = 0;
415 } else {
416 Dx( i, j ).r = pow( alpha / norm, beta ) * target_dx( i, j ).r;
417 Dy( i, j ).r = pow( alpha / norm, beta ) * target_dy( i, j ).r;
418 }
419 //
420 norm = sqrt( target_dx( i, j ).g * target_dx( i, j ).g + target_dy( i, j ).g * target_dy( i, j ).g );
421 if( norm == 0 ) {
422 Dx( i, j ).g = 0;
423 Dy( i, j ).g = 0;
424 } else {
425 Dx( i, j ).g = pow( alpha / norm, beta ) * target_dx( i, j ).g;
426 Dy( i, j ).g = pow( alpha / norm, beta ) * target_dy( i, j ).g;
427 }
428 //
429 norm = sqrt( target_dx( i, j ).b * target_dx( i, j ).b + target_dy( i, j ).b * target_dy( i, j ).b );
430 if( norm == 0 ){
431 Dx( i, j ).b = 0;
432 Dy( i, j ).b = 0;
433 } else {
434 Dx( i, j ).b = pow( alpha / norm, beta ) * target_dx( i, j ).b;
435 Dy( i, j ).b = pow( alpha / norm, beta ) * target_dy( i, j ).b;
436 }
437 }
438 }
439
440
441 }
442
443 // lapilacian
444 int w = pw-1, h = ph-1;
445 __poissonblender__::differencial_image_type Dx_00 = Dx, Dy_00 = Dy;
446 __poissonblender__::differencial_image_type _drvxy;
447
448 __poissonblender__::_recTrim( Dx, __poissonblender__::rectangle_type( __poissonblender__::point_type( 1, 0 ), __poissonblender__::point_type( w, h ) ) );
449 __poissonblender__::_recTrim( Dx_00, __poissonblender__::rectangle_type( __poissonblender__::point_type( 0, 0 ), __poissonblender__::point_type( w, h ) ) );
450 __poissonblender__::_differencial( Dx, Dx_00 );
451 __poissonblender__::_recTrim( Dy, __poissonblender__::rectangle_type( __poissonblender__::point_type( 0, 1 ), __poissonblender__::point_type( w, h ) ) );
452 __poissonblender__::_recTrim( Dy_00, __poissonblender__::rectangle_type( __poissonblender__::point_type( 0, 0 ), __poissonblender__::point_type( w, h ) ) );
453 __poissonblender__::_differencial( Dy, Dy_00 );
454 __poissonblender__::_differencial( Dx, Dy, true );
455 _drvxy = Dx;
456
457 //
458 // solve an poisson's equation
459 //
460 matrix< double > A[ 3 ], b[ 3 ], u[ 3 ];
461 std::map<int,int> _mp;
462
463 for ( unsigned int i = 0; i < 3; i++ )
464 {
465 // build right-hand and left-hand matrix
466 if ( !__poissonblender__::_buildMatrix( A[ i ], b[ i ], u[ i ], i, _mask1, _mp, _drvxy, _target1 ) ) {
467 std::cerr << "cant build matrix." << std::endl;
468 return false;
469 }
470
471 // solve sparse linear system
472 if ( !__poissonblender__::_solve(A[ i ], b[ i ], u[ i ]) ) {
473 std::cerr << "cant solve poissson matrix." << std::endl;
474 return false;
475 }
476
477 // copy computed result to destination image
478 if ( !__poissonblender__::_copyResult( u[ i ], i, _mask1, _mp, _dst1, _src1 ) ) {
479 std::cerr << "cant reconstruct cloned image." << std::endl;
480 return false;
481 }
482 }
483
484 // reconstruct whole image
485 for ( unsigned int j = 0; j < _dst1.height(); j++ ) {
486 for ( unsigned int i = 0; i < _dst1.width(); i++ ) {
487 int _x = i + mask_roi1.x.x + offset.x;
488 int _y = j + mask_roi1.x.y + offset.y;
489 if ( _x > -1 && _y > -1 && _x < static_cast< int >( dst.width() ) && _y < static_cast< int >( dst.height() ))
490 dst( _x, _y ) = _dst1( i, j );
491 }
492 }
493
494 // convert to "out" color type
495 convert( dst, out );
496
497 return true;
498 }
499
516 template< class Array1, class Array2, class Array3 >
517 inline bool seamlessCloning(
518 const Array1 &src, const Array2 &target, Array3 &out,
519 const array2< unsigned char > &mask,
520 const int offx = 0, const int offy = 0, const bool mix = false )
521 {
522 return __seamlessCloning( src, target, out, mask, offx, offy, mix );
523 }
524
536 template< class Array1, class Array2 >
537 inline bool localColorChange( const Array1 &in, Array2 &out, const array2< unsigned char > &mask )
538 {
539 if ( in.empty() || mask.empty() ) return false;
540
541 __poissonblender__::mono_image_type _target;
542 convert( in, _target );
543
544 return __seamlessCloning( in, _target, out, mask );
545 }
546
559 template< class Array1, class Array2 >
560 inline bool localColorChange( const Array1 &in, Array2 &out, const array2< unsigned char > &mask,
561 const rgb< double > multiplier )
562 {
563 if ( in.empty() || mask.empty() ) return false;
564
565 __poissonblender__::color_image_type _src = in;
566 for ( unsigned int j = 0; j < _src.height(); j++ ) {
567 for ( unsigned int i = 0; i < _src.width(); i++ )
568 {
569 _src( i, j ).r = __poissonblender__::_saturate_cast( in( i, j ).r * multiplier.r );
570 _src( i, j ).g = __poissonblender__::_saturate_cast( in( i, j ).g * multiplier.g );
571 _src( i, j ).b = __poissonblender__::_saturate_cast( in( i, j ).b * multiplier.b );
572 }
573 }
574
575 return __seamlessCloning( _src, in, out, mask );
576 }
577
578 //local illumination changes
579 template< class Array1, class Array2 >
580 inline bool localIlluminationChange( const Array1 &in, Array2 &out, const array2< unsigned char > &mask,
581 const double alpha = 0.2, const double beta = 0.2 )
582 {
583 if ( in.empty() || mask.empty() ) return false;
584
585 __poissonblender__::color_image_type _target = in;
586 return __seamlessCloning( in, _target, out, mask, 0, 0, false, true, alpha, beta );
587 }
588
590 // PoissonImageEditingグループの終わり
591
592 _MIST_END
593
594 #endif /*__INCLUDE_POISSON_BLENDING__*/