Skip to content

Commit ad568cf

Browse files
committed
Partially fix the pixel shift within reduce (#703)
1 parent 834234c commit ad568cf

File tree

4 files changed

+60
-85
lines changed

4 files changed

+60
-85
lines changed

libvips/resample/reduceh.cpp

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@ typedef struct _VipsReduceh {
6666
*/
6767
VipsKernel kernel;
6868

69-
/* Use centre rather than corner sampling convention.
70-
*/
71-
gboolean centre;
72-
7369
/* Number of points in kernel.
7470
*/
7571
int n_point;
@@ -81,6 +77,10 @@ typedef struct _VipsReduceh {
8177
int *matrixi[VIPS_TRANSFORM_SCALE + 1];
8278
double *matrixf[VIPS_TRANSFORM_SCALE + 1];
8379

80+
/* Deprecated.
81+
*/
82+
gboolean centre;
83+
8484
} VipsReduceh;
8585

8686
typedef VipsResampleClass VipsReducehClass;
@@ -194,13 +194,11 @@ reduceh_signed_int_tab( VipsReduceh *reduceh,
194194
for( int z = 0; z < bands; z++ ) {
195195
int sum;
196196

197-
sum = reduce_sum<T, int>( in, bands, cx, n );
197+
sum = reduce_sum<T, int>( in + z, bands, cx, n );
198198
sum = signed_fixed_round( sum );
199199
sum = VIPS_CLIP( min_value, sum, max_value );
200200

201201
out[z] = sum;
202-
203-
in += 1;
204202
}
205203
}
206204

@@ -216,10 +214,8 @@ reduceh_float_tab( VipsReduceh *reduceh,
216214
const T* restrict in = (T *) pin;
217215
const int n = reduceh->n_point;
218216

219-
for( int z = 0; z < bands; z++ ) {
220-
out[z] = reduce_sum<T, double>( in, bands, cx, n );
221-
in += 1;
222-
}
217+
for( int z = 0; z < bands; z++ )
218+
out[z] = reduce_sum<T, double>( in + z, bands, cx, n );
223219
}
224220

225221
/* 32-bit int output needs a double intermediate.
@@ -238,10 +234,8 @@ reduceh_unsigned_int32_tab( VipsReduceh *reduceh,
238234
for( int z = 0; z < bands; z++ ) {
239235
double sum;
240236

241-
sum = reduce_sum<T, double>( in, bands, cx, n );
242-
out[z] = VIPS_CLIP( 0, sum, max_value );
243-
244-
in += 1;
237+
sum = reduce_sum<T, double>( in + z, bands, cx, n );
238+
out[z] = VIPS_CLIP( 0, sum, max_value );
245239
}
246240
}
247241

@@ -258,11 +252,9 @@ reduceh_signed_int32_tab( VipsReduceh *reduceh,
258252
for( int z = 0; z < bands; z++ ) {
259253
double sum;
260254

261-
sum = reduce_sum<T, double>( in, bands, cx, n );
255+
sum = reduce_sum<T, double>( in + z, bands, cx, n );
262256
sum = VIPS_CLIP( min_value, sum, max_value );
263257
out[z] = sum;
264-
265-
in += 1;
266258
}
267259
}
268260

@@ -282,11 +274,8 @@ reduceh_notab( VipsReduceh *reduceh,
282274

283275
vips_reduce_make_mask( cx, reduceh->kernel, reduceh->hshrink, x );
284276

285-
for( int z = 0; z < bands; z++ ) {
286-
out[z] = reduce_sum<T, double>( in, bands, cx, n );
287-
288-
in += 1;
289-
}
277+
for( int z = 0; z < bands; z++ )
278+
out[z] = reduce_sum<T, double>( in + z, bands, cx, n );
290279
}
291280

292281
/* Tried a vector path (see reducev) but it was slower. The vectors for
@@ -319,8 +308,6 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
319308
s.top = r->top;
320309
s.width = r->width * reduceh->hshrink + reduceh->n_point;
321310
s.height = r->height;
322-
if( reduceh->centre )
323-
s.width += 1;
324311
if( vips_region_prepare( ir, &s ) )
325312
return( -1 );
326313

@@ -334,9 +321,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
334321

335322
q = VIPS_REGION_ADDR( out_region, r->left, r->top + y );
336323

337-
X = r->left * reduceh->hshrink;
338-
if( reduceh->centre )
339-
X += 0.5;
324+
X = (r->left + 0.5) * reduceh->hshrink - 0.5;
340325

341326
/* We want p0 to be the start (ie. x == 0) of the input
342327
* scanline we are reading from. We can then calculate the p we
@@ -351,11 +336,10 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
351336
ir->valid.left * ps;
352337

353338
for( int x = 0; x < r->width; x++ ) {
354-
int ix = (int) X;
339+
const int ix = (int) X;
355340
VipsPel *p = p0 + ix * ps;
356-
const int sx = X * VIPS_TRANSFORM_SCALE * 2;
357-
const int six = sx & (VIPS_TRANSFORM_SCALE * 2 - 1);
358-
const int tx = (six + 1) >> 1;
341+
const int sx = X * VIPS_TRANSFORM_SCALE;
342+
const int tx = sx & (VIPS_TRANSFORM_SCALE - 1);
359343
const int *cxi = reduceh->matrixi[tx];
360344
const double *cxf = reduceh->matrixf[tx];
361345

@@ -441,7 +425,6 @@ vips_reduceh_build( VipsObject *object )
441425
vips_object_local_array( object, 2 );
442426

443427
VipsImage *in;
444-
int width;
445428

446429
if( VIPS_OBJECT_CLASS( vips_reduceh_parent_class )->build( object ) )
447430
return( -1 );
@@ -499,15 +482,10 @@ vips_reduceh_build( VipsObject *object )
499482
in = t[0];
500483

501484
/* Add new pixels around the input so we can interpolate at the edges.
502-
* In centre mode, we read 0.5 pixels more to the right, so we must
503-
* enlarge a little further.
504485
*/
505-
width = in->Xsize + reduceh->n_point - 1;
506-
if( reduceh->centre )
507-
width += 1;
508486
if( vips_embed( in, &t[1],
509487
reduceh->n_point / 2 - 1, 0,
510-
width, in->Ysize,
488+
in->Xsize + reduceh->n_point - 1, in->Ysize,
511489
"extend", VIPS_EXTEND_COPY,
512490
(void *) NULL ) )
513491
return( -1 );
@@ -581,13 +559,6 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
581559
G_STRUCT_OFFSET( VipsReduceh, kernel ),
582560
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
583561

584-
VIPS_ARG_BOOL( reduceh_class, "centre", 7,
585-
_( "Centre" ),
586-
_( "Use centre sampling convention" ),
587-
VIPS_ARGUMENT_OPTIONAL_INPUT,
588-
G_STRUCT_OFFSET( VipsReduceh, centre ),
589-
FALSE );
590-
591562
/* Old name.
592563
*/
593564
VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3,
@@ -597,6 +568,15 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
597568
G_STRUCT_OFFSET( VipsReduceh, hshrink ),
598569
1, 1000000, 1 );
599570

571+
/* We used to let people pick centre or corner, but it's automatic now.
572+
*/
573+
VIPS_ARG_BOOL( reduceh_class, "centre", 7,
574+
_( "Centre" ),
575+
_( "Use centre sampling convention" ),
576+
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
577+
G_STRUCT_OFFSET( VipsReduceh, centre ),
578+
FALSE );
579+
600580
}
601581

602582
static void

libvips/resample/reducev.cpp

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,6 @@ typedef struct _VipsReducev {
104104
*/
105105
VipsKernel kernel;
106106

107-
/* Use centre rather than corner sampling convention.
108-
*/
109-
gboolean centre;
110-
111107
/* Number of points in kernel.
112108
*/
113109
int n_point;
@@ -128,6 +124,10 @@ typedef struct _VipsReducev {
128124
int n_pass;
129125
Pass pass[MAX_PASS];
130126

127+
/* Deprecated.
128+
*/
129+
gboolean centre;
130+
131131
} VipsReducev;
132132

133133
typedef VipsResampleClass VipsReducevClass;
@@ -534,22 +534,20 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
534534
s.top = r->top * reducev->vshrink;
535535
s.width = r->width;
536536
s.height = r->height * reducev->vshrink + reducev->n_point;
537-
if( reducev->centre )
538-
s.height += 1;
539537
if( vips_region_prepare( ir, &s ) )
540538
return( -1 );
541539

542540
VIPS_GATE_START( "vips_reducev_gen: work" );
543541

542+
double Y = (r->top + 0.5) * reducev->vshrink - 0.5;
543+
544544
for( int y = 0; y < r->height; y ++ ) {
545545
VipsPel *q =
546546
VIPS_REGION_ADDR( out_region, r->left, r->top + y );
547-
const double Y = (r->top + y) * reducev->vshrink +
548-
(reducev->centre ? 0.5 : 0.0);
549-
VipsPel *p = VIPS_REGION_ADDR( ir, r->left, (int) Y );
550-
const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
551-
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
552-
const int ty = (siy + 1) >> 1;
547+
const int py = (int) Y;
548+
VipsPel *p = VIPS_REGION_ADDR( ir, r->left, py );
549+
const int sy = Y * VIPS_TRANSFORM_SCALE;
550+
const int ty = sy & (VIPS_TRANSFORM_SCALE - 1);
553551
const int *cyi = reducev->matrixi[ty];
554552
const double *cyf = reducev->matrixf[ty];
555553
const int lskip = VIPS_REGION_LSKIP( ir );
@@ -606,13 +604,15 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
606604
case VIPS_FORMAT_DPCOMPLEX:
607605
case VIPS_FORMAT_DOUBLE:
608606
reducev_notab<double>( reducev,
609-
q, p, ne, lskip, Y - (int) Y );
607+
q, p, ne, lskip, Y - py );
610608
break;
611609

612610
default:
613611
g_assert_not_reached();
614612
break;
615613
}
614+
615+
Y += reducev->vshrink;
616616
}
617617

618618
VIPS_GATE_STOP( "vips_reducev_gen: work" );
@@ -647,8 +647,6 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
647647
s.top = r->top * reducev->vshrink;
648648
s.width = r->width;
649649
s.height = r->height * reducev->vshrink + reducev->n_point;
650-
if( reducev->centre )
651-
s.height += 1;
652650
if( vips_region_prepare( ir, &s ) )
653651
return( -1 );
654652

@@ -663,15 +661,14 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
663661

664662
VIPS_GATE_START( "vips_reducev_vector_gen: work" );
665663

664+
double Y = (r->top + 0.5) * reducev->vshrink - 0.5;
665+
666666
for( int y = 0; y < r->height; y ++ ) {
667667
VipsPel *q =
668668
VIPS_REGION_ADDR( out_region, r->left, r->top + y );
669-
const double Y = (r->top + y) * reducev->vshrink +
670-
(reducev->centre ? 0.5 : 0.0);
671-
const int py = (int) Y;
672-
const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
673-
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
674-
const int ty = (siy + 1) >> 1;
669+
const int py = (int) Y;
670+
const int sy = Y * VIPS_TRANSFORM_SCALE;
671+
const int ty = sy & (VIPS_TRANSFORM_SCALE - 1);
675672
const int *cyo = reducev->matrixo[ty];
676673

677674
#ifdef DEBUG_PIXELS
@@ -682,7 +679,7 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
682679
printf( "first column of pixel values:\n" );
683680
for( int i = 0; i < reducev->n_point; i++ )
684681
printf( "\t%d - %d\n", i,
685-
*VIPS_REGION_ADDR( ir, r->left, r->top + y + i ) );
682+
*VIPS_REGION_ADDR( ir, r->left, py ) );
686683
#endif /*DEBUG_PIXELS*/
687684

688685
/* We run our n passes to generate this scanline.
@@ -709,6 +706,8 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
709706
printf( "pixel result:\n" );
710707
printf( "\t%d\n", *q );
711708
#endif /*DEBUG_PIXELS*/
709+
710+
Y += reducev->vshrink;
712711
}
713712

714713
VIPS_GATE_STOP( "vips_reducev_vector_gen: work" );
@@ -787,7 +786,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in, VipsImage **out )
787786

788787
*out = vips_image_new();
789788
if( vips_image_pipelinev( *out,
790-
VIPS_DEMAND_STYLE_FATSTRIP, in, (void *) NULL ) )
789+
VIPS_DEMAND_STYLE_THINSTRIP, in, (void *) NULL ) )
791790
return( -1 );
792791

793792
/* Size output. We need to always round to nearest, so round(), not
@@ -830,7 +829,6 @@ vips_reducev_build( VipsObject *object )
830829
VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 );
831830

832831
VipsImage *in;
833-
int height;
834832

835833
if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) )
836834
return( -1 );
@@ -863,12 +861,9 @@ vips_reducev_build( VipsObject *object )
863861

864862
/* Add new pixels around the input so we can interpolate at the edges.
865863
*/
866-
height = in->Ysize + reducev->n_point - 1;
867-
if( reducev->centre )
868-
height += 1;
869864
if( vips_embed( in, &t[1],
870865
0, reducev->n_point / 2 - 1,
871-
in->Xsize, height,
866+
in->Xsize, in->Ysize + reducev->n_point - 1,
872867
"extend", VIPS_EXTEND_COPY,
873868
(void *) NULL ) )
874869
return( -1 );
@@ -939,13 +934,6 @@ vips_reducev_class_init( VipsReducevClass *reducev_class )
939934
G_STRUCT_OFFSET( VipsReducev, kernel ),
940935
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
941936

942-
VIPS_ARG_BOOL( reducev_class, "centre", 7,
943-
_( "Centre" ),
944-
_( "Use centre sampling convention" ),
945-
VIPS_ARGUMENT_OPTIONAL_INPUT,
946-
G_STRUCT_OFFSET( VipsReducev, centre ),
947-
FALSE );
948-
949937
/* Old name.
950938
*/
951939
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
@@ -955,6 +943,15 @@ vips_reducev_class_init( VipsReducevClass *reducev_class )
955943
G_STRUCT_OFFSET( VipsReducev, vshrink ),
956944
1, 1000000, 1 );
957945

946+
/* We used to let people pick centre or corner, but it's automatic now.
947+
*/
948+
VIPS_ARG_BOOL( reducev_class, "centre", 7,
949+
_( "Centre" ),
950+
_( "Use centre sampling convention" ),
951+
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
952+
G_STRUCT_OFFSET( VipsReducev, centre ),
953+
FALSE );
954+
958955
}
959956

960957
static void

libvips/resample/resize.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ vips_resize_build( VipsObject *object )
232232
g_info( "residual reducev by %g", vscale );
233233
if( vips_reducev( in, &t[2], 1.0 / vscale,
234234
"kernel", resize->kernel,
235-
"centre", TRUE,
236235
NULL ) )
237236
return( -1 );
238237
in = t[2];
@@ -243,7 +242,6 @@ vips_resize_build( VipsObject *object )
243242
hscale );
244243
if( vips_reduceh( in, &t[3], 1.0 / hscale,
245244
"kernel", resize->kernel,
246-
"centre", TRUE,
247245
NULL ) )
248246
return( -1 );
249247
in = t[3];

libvips/resample/templates.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ calculate_coefficients_triangle( double *c,
328328

329329
sum = 0;
330330
for( i = 0; i < n_points; i++ ) {
331-
double xp = (i - (shrink - 0.5) - x) / shrink;
331+
double xp = (i - (n_points - 2) / 2 - x) / shrink;
332332

333333
double l;
334334

@@ -365,7 +365,7 @@ calculate_coefficients_cubic( double *c,
365365

366366
sum = 0;
367367
for( i = 0; i < n_points; i++ ) {
368-
const double xp = (i - (2 * shrink - 1) - x) / shrink;
368+
const double xp = (i - (n_points - 2) / 2 - x) / shrink;
369369
const double axp = VIPS_FABS( xp );
370370
const double axp2 = axp * axp;
371371
const double axp3 = axp2 * axp;

0 commit comments

Comments
 (0)