Skip to content

Commit 332575b

Browse files
committed
Inline storage of scales
1 parent dd0cf68 commit 332575b

File tree

1 file changed

+85
-53
lines changed

1 file changed

+85
-53
lines changed

src/dssim.c

Lines changed: 85 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ typedef struct dssim_chan dssim_chan;
5454
struct dssim_chan {
5555
int width, height;
5656
dssim_px_t *img, *mu, *img_sq_blur;
57-
dssim_chan *next_half;
5857
bool is_chroma;
5958
};
6059

60+
typedef struct dssim_image_chan {
61+
dssim_chan scales[MAX_SCALES];
62+
int num_scales;
63+
} dssim_image_chan;
64+
6165
struct dssim_image {
62-
dssim_chan chan[MAX_CHANS];
66+
dssim_image_chan chan[MAX_CHANS];
6367
int num_channels;
6468
};
6569

@@ -150,16 +154,14 @@ static void dealloc_chan(dssim_chan *chan) {
150154
free(chan->img);
151155
free(chan->mu);
152156
free(chan->img_sq_blur);
153-
if (chan->next_half) {
154-
dealloc_chan(chan->next_half);
155-
free(chan->next_half);
156-
}
157157
}
158158

159159
void dssim_dealloc_image(dssim_image *img)
160160
{
161161
for (int ch = 0; ch < img->num_channels; ch++) {
162-
dealloc_chan(&img->chan[ch]);
162+
for (int s = 0; s < img->chan[ch].num_scales; s++) {
163+
dealloc_chan(&img->chan[ch].scales[s]);
164+
}
163165
}
164166
free(img);
165167
}
@@ -252,6 +254,11 @@ static void transpose(dssim_px_t *restrict src, dssim_px_t *restrict dst, const
252254
static void regular_1d_blur(const dssim_px_t *src, dssim_px_t *restrict tmp1, dssim_px_t *dst, const int width, const int height)
253255
{
254256
const int runs = 2;
257+
assert(src);
258+
assert(tmp1);
259+
assert(dst);
260+
assert(width > 4);
261+
assert(height > 4);
255262

256263
// tmp1 is expected to hold at least two lines
257264
dssim_px_t *restrict tmp2 = tmp1 + width;
@@ -297,6 +304,9 @@ static void regular_1d_blur(const dssim_px_t *src, dssim_px_t *restrict tmp1, ds
297304
static void blur(const dssim_px_t *restrict src, dssim_px_t *restrict tmp, dssim_px_t *restrict dst,
298305
const int width, const int height)
299306
{
307+
assert(src);
308+
assert(dst);
309+
assert(tmp);
300310
#ifdef USE_COCOA
301311
vImage_Buffer srcbuf = {
302312
.width = width,
@@ -378,8 +388,9 @@ static void subsampled_copy(dssim_chan *new_chan, const int dest_y_offset, const
378388

379389
static void convert_image_subsampled(dssim_image *img, dssim_row_callback cb, void *callback_user_data)
380390
{
381-
const int width = img->chan[0].width;
382-
const int height = img->chan[0].height;
391+
dssim_chan *chan = &img->chan[0].scales[0];
392+
const int width = chan->width;
393+
const int height = chan->height;
383394
dssim_px_t *row_tmp0[img->num_channels];
384395
dssim_px_t *row_tmp1[img->num_channels];
385396

@@ -389,15 +400,15 @@ static void convert_image_subsampled(dssim_image *img, dssim_row_callback cb, vo
389400
}
390401

391402
for(int y = 0; y < height; y += 2) {
392-
row_tmp0[0] = &img->chan[0].img[width * y]; // Luma can be written directly (it's unscaled)
393-
row_tmp1[0] = &img->chan[0].img[width * MIN(height-1, y+1)];
403+
row_tmp0[0] = &chan->img[width * y]; // Luma can be written directly (it's unscaled)
404+
row_tmp1[0] = &chan->img[width * MIN(height-1, y+1)];
394405

395406
cb(row_tmp0, img->num_channels, y, width, callback_user_data);
396407
cb(row_tmp1, img->num_channels, MIN(height-1, y+1), width, callback_user_data);
397408

398409
if (y < height-1) {
399410
for(int ch = 1; ch < img->num_channels; ch++) { // Chroma is downsampled
400-
subsampled_copy(&img->chan[ch], y/2, 1, row_tmp0[ch], width);
411+
subsampled_copy(&img->chan[ch].scales[0], y/2, 1, row_tmp0[ch], width);
401412
}
402413
}
403414
}
@@ -409,13 +420,14 @@ static void convert_image_subsampled(dssim_image *img, dssim_row_callback cb, vo
409420

410421
static void convert_image_simple(dssim_image *img, dssim_row_callback cb, void *callback_user_data)
411422
{
412-
const int width = img->chan[0].width;
413-
const int height = img->chan[0].height;
423+
dssim_chan *chan = &img->chan[0].scales[0];
424+
const int width = chan->width;
425+
const int height = chan->height;
414426
dssim_px_t *row_tmp[img->num_channels];
415427

416428
for(int y = 0; y < height; y++) {
417429
for(int ch = 0; ch < img->num_channels; ch++) {
418-
row_tmp[ch] = &img->chan[ch].img[width * y];
430+
row_tmp[ch] = &img->chan[ch].scales[0].img[width * y];
419431
}
420432
cb(row_tmp, img->num_channels, y, width, callback_user_data);
421433
}
@@ -537,20 +549,7 @@ dssim_image *dssim_create_image(dssim_attr *attr, unsigned char *const *const ro
537549
return dssim_create_image_float_callback(attr, num_channels, width, height, converter, (void*)&im);
538550
}
539551

540-
dssim_chan *create_chan(const int width, const int height, const bool is_chroma) {
541-
assert(width > 0 && height > 0);
542-
543-
dssim_chan *const chan = malloc(sizeof(chan[0]));
544-
*chan = (dssim_chan){
545-
.width = width,
546-
.height = height,
547-
.is_chroma = is_chroma,
548-
.img = malloc(width * height * sizeof(chan->img[0])),
549-
};
550-
return chan;
551-
}
552-
553-
static void dssim_preprocess_channel(dssim_chan *chan, dssim_px_t *tmp, int depth);
552+
static void dssim_preprocess_channel(dssim_chan *chan, dssim_px_t *tmp);
554553

555554
dssim_image *dssim_create_image_float_callback(dssim_attr *attr, const int num_channels, const int width, const int height, dssim_row_callback cb, void *callback_user_data)
556555
{
@@ -567,10 +566,29 @@ dssim_image *dssim_create_image_float_callback(dssim_attr *attr, const int num_c
567566

568567
for (int ch = 0; ch < img->num_channels; ch++) {
569568
const bool is_chroma = ch > 0;
570-
img->chan[ch] = *create_chan(
571-
subsample_chroma && is_chroma ? width/2 : width,
572-
subsample_chroma && is_chroma ? height/2 : height,
573-
is_chroma);
569+
int chan_width = subsample_chroma && is_chroma ? width/2 : width;
570+
int chan_height = subsample_chroma && is_chroma ? height/2 : height;
571+
int s = 0;
572+
for(; s < attr->num_scales; s++) {
573+
img->chan[ch].scales[s] = (dssim_chan){
574+
.width = chan_width,
575+
.height = chan_height,
576+
.is_chroma = is_chroma,
577+
.img = malloc(chan_width * chan_height * sizeof(img->chan[ch].scales[s].img[0])),
578+
};
579+
chan_width /= 2;
580+
chan_height /= 2;
581+
if (chan_width < 8 || chan_height < 8) {
582+
break;
583+
}
584+
}
585+
img->chan[ch].num_scales = s;
586+
}
587+
588+
for (int ch = 0; ch < img->num_channels; ch++) {
589+
for (int s = 0; s < img->chan[ch].num_scales; s++) {
590+
assert(img->chan[ch].scales[s].img);
591+
}
574592
}
575593

576594
if (subsample_chroma && img->num_channels > 1) {
@@ -579,26 +597,39 @@ dssim_image *dssim_create_image_float_callback(dssim_attr *attr, const int num_c
579597
convert_image_simple(img, cb, callback_user_data);
580598
}
581599

600+
601+
for (int ch = 0; ch < img->num_channels; ch++) {
602+
for (int s = 0; s < img->chan[ch].num_scales; s++) {
603+
assert(img->chan[ch].scales[s].img);
604+
}
605+
}
606+
582607
dssim_px_t *tmp = dssim_get_tmp(attr, width * height * sizeof(tmp[0]));
583608
for (int ch = 0; ch < img->num_channels; ch++) {
584-
dssim_preprocess_channel(&img->chan[ch], tmp, attr->num_scales);
609+
const dssim_chan *prev_chan = &img->chan[ch].scales[0];
610+
for (int s = 1; s < img->chan[ch].num_scales; s++) {
611+
dssim_chan *new_chan = &img->chan[ch].scales[s];
612+
subsampled_copy(new_chan, 0, new_chan->height, prev_chan->img, prev_chan->width);
613+
prev_chan = new_chan;
614+
}
615+
for (int s = 0; s < img->chan[ch].num_scales; s++) {
616+
dssim_preprocess_channel(&img->chan[ch].scales[s], tmp);
617+
}
585618
}
586619

587620
return img;
588621
}
589622

590-
static void dssim_preprocess_channel(dssim_chan *chan, dssim_px_t *tmp, int num_scales)
623+
static void dssim_preprocess_channel(dssim_chan *chan, dssim_px_t *tmp)
591624
{
625+
assert(chan);
626+
assert(tmp);
627+
assert(chan->img);
628+
assert(!chan->mu);
629+
assert(!chan->img_sq_blur);
592630
const int width = chan->width;
593631
const int height = chan->height;
594632

595-
if (num_scales > 1 && chan->width >= 8 && chan->height >= 8) {
596-
dssim_chan *new_chan = create_chan(chan->width/2, chan->height/2, chan->is_chroma);
597-
chan->next_half = new_chan;
598-
subsampled_copy(new_chan, 0, new_chan->height, chan->img, chan->width);
599-
dssim_preprocess_channel(new_chan, tmp, num_scales-1);
600-
}
601-
602633
if (chan->is_chroma) {
603634
blur(chan->img, tmp, chan->img, width, height);
604635
}
@@ -621,6 +652,9 @@ static dssim_px_t *get_img1_img2_blur(const dssim_chan *restrict original, dssim
621652
dssim_px_t *restrict img1 = original->img;
622653
dssim_px_t *restrict img2 = modified->img; modified->img = NULL; // img2 is turned in-place into blur(img1*img2)
623654

655+
assert(img1);
656+
assert(img2);
657+
624658
for (int j = 0; j < width*height; j++) {
625659
img2[j] *= img1[j];
626660
}
@@ -653,19 +687,22 @@ double dssim_compare(dssim_attr *attr, const dssim_image *restrict original_imag
653687
const int channels = MIN(original_image->num_channels, modified_image->num_channels);
654688
assert(channels > 0);
655689

656-
dssim_px_t *tmp = dssim_get_tmp(attr, original_image->chan[0].width * original_image->chan[0].height * sizeof(tmp[0]));
690+
dssim_px_t *tmp = dssim_get_tmp(attr, original_image->chan[0].scales[0].width * original_image->chan[0].scales[0].height * sizeof(tmp[0]));
657691
assert(tmp);
658692

659693
double ssim_sum = 0;
660694
double weight_sum = 0;
661695
for (int ch = 0; ch < channels; ch++) {
662696

663-
const dssim_chan *original = &original_image->chan[ch];
664-
dssim_chan *modified = &modified_image->chan[ch];
665-
assert(original);
666-
assert(modified);
697+
const dssim_image_chan *original_scales = &original_image->chan[ch];
698+
dssim_image_chan *modified_scales = &modified_image->chan[ch];
699+
700+
int num_scales = MIN(original_scales->num_scales, modified_scales->num_scales);
701+
702+
for(int n=0; n < num_scales; n++) {
703+
const dssim_chan *original = &original_scales->scales[n];
704+
dssim_chan *modified = &modified_scales->scales[n];
667705

668-
for(int n=0; n < attr->num_scales; n++) {
669706
const double weight = (original->is_chroma ? attr->color_weight : 1.0) * attr->scale_weights[n];
670707

671708
const bool save_maps = attr->save_maps_scales > n && attr->save_maps_channels > ch;
@@ -677,11 +714,6 @@ double dssim_compare(dssim_attr *attr, const dssim_image *restrict original_imag
677714
assert(modified);
678715
ssim_sum += weight * dssim_compare_channel(original, modified, tmp, &attr->ssim_maps[ch].scales[n], save_maps);
679716
weight_sum += weight;
680-
original = original->next_half;
681-
modified = modified->next_half;
682-
if (!original || !modified) {
683-
break;
684-
}
685717
}
686718
}
687719

0 commit comments

Comments
 (0)