1
/* LiquidRescaling Library
2
* Copyright (C) 2007-2009 Carlo Baldassi (the "Author") <carlobaldassi@gmail.com>.
5
* This library implements the algorithm described in the paper
6
* "Seam Carving for Content-Aware Image Resizing"
7
* by Shai Avidan and Ariel Shamir
8
* which can be found at http://www.faculty.idc.ac.il/arik/imret.pdf
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU Lesser General Public License as published by
12
* the Free Software Foundation; version 3 dated June, 2007.
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU Lesser General Public License for more details.
19
* You should have received a copy of the GNU Lesser General Public License
20
* along with this program; if not, see <http://www.gnu.org/licenses/>
29
#include <lqr/lqr_base.h>
30
#include <lqr/lqr_gradient.h>
31
#include <lqr/lqr_rwindow.h>
32
#include <lqr/lqr_energy.h>
33
#include <lqr/lqr_progress_pub.h>
34
#include <lqr/lqr_cursor_pub.h>
35
#include <lqr/lqr_vmap.h>
36
#include <lqr/lqr_vmap_list.h>
37
#include <lqr/lqr_carver_list.h>
38
#include <lqr/lqr_carver.h>
43
#endif /* __LQR_DEBUG__ */
45
/* read normalised pixel value from
46
* rgb buffer at the given index */
48
lqr_pixel_get_norm(void *rgb, gint rgb_ind, LqrColDepth col_depth)
52
return (gdouble) AS_8I(rgb)[rgb_ind] / 0xFF;
53
case LQR_COLDEPTH_16I:
54
return (gdouble) AS_16I(rgb)[rgb_ind] / 0xFFFF;
55
case LQR_COLDEPTH_32F:
56
return (gdouble) AS_32F(rgb)[rgb_ind];
57
case LQR_COLDEPTH_64F:
58
return (gdouble) AS_64F(rgb)[rgb_ind];
62
#endif /* __LQR_DEBUG__ */
67
/* write pixel from normalised value
68
* in rgb buffer at the given index */
70
lqr_pixel_set_norm(gdouble val, void *rgb, gint rgb_ind, LqrColDepth col_depth)
74
AS_8I(rgb)[rgb_ind] = AS0_8I(val * 0xFF);
76
case LQR_COLDEPTH_16I:
77
AS_16I(rgb)[rgb_ind] = AS0_16I(val * 0xFFFF);
79
case LQR_COLDEPTH_32F:
80
AS_32F(rgb)[rgb_ind] = AS0_32F(val);
82
case LQR_COLDEPTH_64F:
83
AS_64F(rgb)[rgb_ind] = AS0_64F(val);
88
#endif /* __LQR_DEBUG__ */
94
lqr_pixel_get_rgbcol(void *rgb, gint rgb_ind, LqrColDepth col_depth, LqrImageType image_type, gint channel)
96
gdouble black_fact = 0;
101
return lqr_pixel_get_norm(rgb, rgb_ind + channel, col_depth);
103
return 1. - lqr_pixel_get_norm(rgb, rgb_ind + channel, col_depth);
105
case LQR_CMYKA_IMAGE:
106
black_fact = 1 - lqr_pixel_get_norm(rgb, rgb_ind + 3, col_depth);
107
return black_fact * (1. - (lqr_pixel_get_norm(rgb, rgb_ind + channel, col_depth)));
108
case LQR_CUSTOM_IMAGE:
113
#endif /* __LQR_DEBUG__ */
119
lqr_carver_read_brightness_grey(LqrCarver *r, gint x, gint y)
121
gint now = r->raw[y][x];
122
gint rgb_ind = now * r->channels;
123
return lqr_pixel_get_norm(r->rgb, rgb_ind, r->col_depth);
127
lqr_carver_read_brightness_std(LqrCarver *r, gint x, gint y)
129
gdouble red, green, blue;
130
gint now = r->raw[y][x];
131
gint rgb_ind = now * r->channels;
133
red = lqr_pixel_get_rgbcol(r->rgb, rgb_ind, r->col_depth, r->image_type, 0);
134
green = lqr_pixel_get_rgbcol(r->rgb, rgb_ind, r->col_depth, r->image_type, 1);
135
blue = lqr_pixel_get_rgbcol(r->rgb, rgb_ind, r->col_depth, r->image_type, 2);
136
return (red + green + blue) / 3;
140
lqr_carver_read_brightness_custom(LqrCarver *r, gint x, gint y)
144
gint has_alpha = (r->alpha_channel >= 0 ? 1 : 0);
145
gint has_black = (r->black_channel >= 0 ? 1 : 0);
146
guint col_channels = r->channels - has_alpha - has_black;
148
gdouble black_fact = 0;
150
gint now = r->raw[y][x];
153
black_fact = lqr_pixel_get_norm(r->rgb, now * r->channels + r->black_channel, r->col_depth);
156
for (k = 0; k < r->channels; k++) {
157
if ((k != r->alpha_channel) && (k != r->black_channel)) {
158
gdouble col = lqr_pixel_get_norm(r->rgb, now * r->channels + k, r->col_depth);
159
sum += 1. - (1. - col) * (1. - black_fact);
172
/* read average pixel value at x, y
173
* for energy computation */
175
lqr_carver_read_brightness(LqrCarver *r, gint x, gint y)
177
gint has_alpha = (r->alpha_channel >= 0 ? 1 : 0);
178
gdouble alpha_fact = 1;
180
gint now = r->raw[y][x];
184
switch (r->image_type) {
186
case LQR_GREYA_IMAGE:
187
bright = lqr_carver_read_brightness_grey(r, x, y);
193
case LQR_CMYKA_IMAGE:
194
bright = lqr_carver_read_brightness_std(r, x, y);
196
case LQR_CUSTOM_IMAGE:
197
bright = lqr_carver_read_brightness_custom(r, x, y);
202
alpha_fact = lqr_pixel_get_norm(r->rgb, now * r->channels + r->alpha_channel, r->col_depth);
205
return bright * alpha_fact;
209
lqr_carver_read_luma_std(LqrCarver *r, gint x, gint y)
211
gdouble red, green, blue;
212
gint now = r->raw[y][x];
213
gint rgb_ind = now * r->channels;
215
red = lqr_pixel_get_rgbcol(r->rgb, rgb_ind, r->col_depth, r->image_type, 0);
216
green = lqr_pixel_get_rgbcol(r->rgb, rgb_ind, r->col_depth, r->image_type, 1);
217
blue = lqr_pixel_get_rgbcol(r->rgb, rgb_ind, r->col_depth, r->image_type, 2);
218
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
222
lqr_carver_read_luma(LqrCarver *r, gint x, gint y)
224
gint has_alpha = (r->alpha_channel >= 0 ? 1 : 0);
225
gdouble alpha_fact = 1;
227
gint now = r->raw[y][x];
231
switch (r->image_type) {
233
case LQR_GREYA_IMAGE:
234
bright = lqr_carver_read_brightness_grey(r, x, y);
240
case LQR_CMYKA_IMAGE:
241
bright = lqr_carver_read_luma_std(r, x, y);
243
case LQR_CUSTOM_IMAGE:
244
bright = lqr_carver_read_brightness_custom(r, x, y);
249
alpha_fact = lqr_pixel_get_norm(r->rgb, now * r->channels + r->alpha_channel, r->col_depth);
252
return bright * alpha_fact;
256
lqr_carver_read_rgba(LqrCarver *r, gint x, gint y, gint channel)
258
gint has_alpha = (r->alpha_channel >= 0 ? 1 : 0);
260
gint now = r->raw[y][x];
263
assert(channel >= 0 && channel < 4);
264
#endif /* __LQR_DEBUG__ */
267
switch (r->image_type) {
269
case LQR_GREYA_IMAGE:
270
return lqr_carver_read_brightness_grey(r, x, y);
275
case LQR_CMYKA_IMAGE:
276
return lqr_pixel_get_rgbcol(r->rgb, now * r->channels, r->col_depth, r->image_type, channel);
277
case LQR_CUSTOM_IMAGE:
282
#endif /* __LQR_DEBUG__ */
285
} else if (has_alpha) {
286
return lqr_pixel_get_norm(r->rgb, now * r->channels + r->alpha_channel, r->col_depth);
293
lqr_carver_read_custom(LqrCarver *r, gint x, gint y, gint channel)
295
gint now = r->raw[y][x];
297
return lqr_pixel_get_norm(r->rgb, now * r->channels + channel, r->col_depth);
301
lqr_carver_read_cached_std(LqrCarver *r, gint x, gint y)
303
gint z0 = r->raw[y][x];
305
return r->rcache[z0];
309
lqr_carver_read_cached_rgba(LqrCarver *r, gint x, gint y, gint channel)
311
gint z0 = r->raw[y][x];
313
return r->rcache[z0 * 4 + channel];
317
lqr_carver_read_cached_custom(LqrCarver *r, gint x, gint y, gint channel)
319
gint z0 = r->raw[y][x];
321
return r->rcache[z0 * r->channels + channel];
325
lqr_energy_builtin_grad_all(gint x, gint y, gint img_width, gint img_height, LqrReadingWindow *rwindow, LqrGradFunc gf)
329
gdouble (*bread_func) (LqrReadingWindow *, gint, gint);
331
switch (lqr_rwindow_get_read_t(rwindow)) {
332
case LQR_ER_BRIGHTNESS:
333
bread_func = lqr_rwindow_read_bright;
336
bread_func = lqr_rwindow_read_luma;
341
#endif /* __LQR_DEBUG__ */
346
gy = bread_func(rwindow, 0, 1) - bread_func(rwindow, 0, 0);
347
} else if (y < img_height - 1) {
348
gy = (bread_func(rwindow, 0, 1) - bread_func(rwindow, 0, -1)) / 2;
350
gy = bread_func(rwindow, 0, 0) - bread_func(rwindow, 0, -1);
354
gx = bread_func(rwindow, 1, 0) - bread_func(rwindow, 0, 0);
355
} else if (x < img_width - 1) {
356
gx = (bread_func(rwindow, 1, 0) - bread_func(rwindow, -1, 0)) / 2;
358
gx = bread_func(rwindow, 0, 0) - bread_func(rwindow, -1, 0);
365
lqr_energy_builtin_grad_norm(gint x, gint y, gint img_width, gint img_height, LqrReadingWindow *rwindow,
368
return lqr_energy_builtin_grad_all(x, y, img_width, img_height, rwindow, lqr_grad_norm);
372
lqr_energy_builtin_grad_sumabs(gint x, gint y, gint img_width, gint img_height, LqrReadingWindow *rwindow,
375
return lqr_energy_builtin_grad_all(x, y, img_width, img_height, rwindow, lqr_grad_sumabs);
379
lqr_energy_builtin_grad_xabs(gint x, gint y, gint img_width, gint img_height, LqrReadingWindow *rwindow,
382
return lqr_energy_builtin_grad_all(x, y, img_width, img_height, rwindow, lqr_grad_xabs);
386
lqr_energy_builtin_null(gint x, gint y, gint img_width, gint img_height, LqrReadingWindow *rwindow, gpointer extra_data)
393
lqr_carver_set_energy_function_builtin(LqrCarver *r, LqrEnergyFuncBuiltinType ef_ind)
396
case LQR_EF_GRAD_NORM:
397
LQR_CATCH(lqr_carver_set_energy_function(r, lqr_energy_builtin_grad_norm, 1, LQR_ER_BRIGHTNESS, NULL));
399
case LQR_EF_GRAD_SUMABS:
400
LQR_CATCH(lqr_carver_set_energy_function(r, lqr_energy_builtin_grad_sumabs, 1, LQR_ER_BRIGHTNESS, NULL));
402
case LQR_EF_GRAD_XABS:
403
LQR_CATCH(lqr_carver_set_energy_function(r, lqr_energy_builtin_grad_xabs, 1, LQR_ER_BRIGHTNESS, NULL));
405
case LQR_EF_LUMA_GRAD_NORM:
406
LQR_CATCH(lqr_carver_set_energy_function(r, lqr_energy_builtin_grad_norm, 1, LQR_ER_LUMA, NULL));
408
case LQR_EF_LUMA_GRAD_SUMABS:
409
LQR_CATCH(lqr_carver_set_energy_function(r, lqr_energy_builtin_grad_sumabs, 1, LQR_ER_LUMA, NULL));
411
case LQR_EF_LUMA_GRAD_XABS:
412
LQR_CATCH(lqr_carver_set_energy_function(r, lqr_energy_builtin_grad_xabs, 1, LQR_ER_LUMA, NULL));
415
LQR_CATCH(lqr_carver_set_energy_function(r, lqr_energy_builtin_null, 0, LQR_ER_BRIGHTNESS, NULL));
426
lqr_carver_set_energy_function(LqrCarver *r, LqrEnergyFunc en_func, gint radius,
427
LqrEnergyReaderType reader_type, gpointer extra_data)
429
LQR_CATCH_F(r->root == NULL);
432
r->nrg_radius = radius;
433
r->nrg_read_t = reader_type;
434
r->nrg_extra_data = extra_data;
438
r->nrg_uptodate = FALSE;
440
lqr_rwindow_destroy(r->rwindow);
442
if (reader_type == LQR_ER_CUSTOM) {
443
LQR_CATCH_MEM(r->rwindow = lqr_rwindow_new_custom(radius, r->use_rcache, r->channels));
445
LQR_CATCH_MEM(r->rwindow = lqr_rwindow_new(radius, reader_type, r->use_rcache));
452
lqr_carver_generate_rcache_bright(LqrCarver *r)
458
LQR_TRY_N_N(buffer = g_try_new(gdouble, r->w0 * r->h0));
460
for (y = 0; y < r->h; y++) {
461
for (x = 0; x < r->w; x++) {
463
buffer[z0] = lqr_carver_read_brightness(r, x, y);
471
lqr_carver_generate_rcache_luma(LqrCarver *r)
477
LQR_TRY_N_N(buffer = g_try_new(gdouble, r->w0 * r->h0));
479
for (y = 0; y < r->h; y++) {
480
for (x = 0; x < r->w; x++) {
482
buffer[z0] = lqr_carver_read_luma(r, x, y);
490
lqr_carver_generate_rcache_rgba(LqrCarver *r)
496
LQR_TRY_N_N(buffer = g_try_new(gdouble, r->w0 * r->h0 * 4));
498
for (y = 0; y < r->h; y++) {
499
for (x = 0; x < r->w; x++) {
501
for (k = 0; k < 4; k++) {
502
buffer[z0 * 4 + k] = lqr_carver_read_rgba(r, x, y, k);
511
lqr_carver_generate_rcache_custom(LqrCarver *r)
517
LQR_TRY_N_N(buffer = g_try_new(gdouble, r->w0 * r->h0 * r->channels));
519
for (y = 0; y < r->h; y++) {
520
for (x = 0; x < r->w; x++) {
522
for (k = 0; k < r->channels; k++) {
523
buffer[z0 * r->channels + k] = lqr_carver_read_custom(r, x, y, k);
532
lqr_carver_generate_rcache(LqrCarver *r)
535
assert(r->w == r->w_start - r->max_level + 1);
536
#endif /* __LQR_DEBUG__ */
538
switch (r->nrg_read_t) {
539
case LQR_ER_BRIGHTNESS:
540
return lqr_carver_generate_rcache_bright(r);
542
return lqr_carver_generate_rcache_luma(r);
544
return lqr_carver_generate_rcache_rgba(r);
546
return lqr_carver_generate_rcache_custom(r);
550
#endif /* __LQR_DEBUG__ */
557
lqr_carver_get_energy(LqrCarver *r, gfloat *buffer, gint orientation)
565
gfloat nrg_min = G_MAXFLOAT;
568
LQR_CATCH_F(orientation == 0 || orientation == 1);
570
LQR_CATCH_F(buffer != NULL);
572
if (r->nrg_active == FALSE) {
573
LQR_CATCH(lqr_carver_init_energy_related(r));
576
if (r->w != r->w_start - r->max_level + 1) {
579
#endif /* __LQR_DEBUG__ */
580
LQR_CATCH(lqr_carver_flatten(r));
583
buf_size = r->w * r->h;
585
if (orientation != lqr_carver_get_orientation(r)) {
586
LQR_CATCH(lqr_carver_transpose(r));
588
LQR_CATCH(lqr_carver_build_emap(r));
590
w = lqr_carver_get_width(r);
591
h = lqr_carver_get_height(r);
593
for (y = 0; y < h; y++) {
594
for (x = 0; x < w; x++) {
595
data = orientation == 0 ? r->raw[y][x] : r->raw[x][y];
596
/* nrg = tanhf(r->en[data]); */
597
nrg = LQR_SATURATE(r->en[data]);
598
nrg_max = MAX(nrg_max, nrg);
599
nrg_min = MIN(nrg_min, nrg);
604
if (nrg_max > nrg_min) {
605
for (z0 = 0; z0 < buf_size; z0++) {
606
buffer[z0] = (buffer[z0] - nrg_min) / (nrg_max - nrg_min);
615
lqr_carver_get_true_energy(LqrCarver *r, gfloat *buffer, gint orientation)
623
LQR_CATCH_F(orientation == 0 || orientation == 1);
625
LQR_CATCH_F(buffer != NULL);
627
if (r->nrg_active == FALSE) {
628
LQR_CATCH(lqr_carver_init_energy_related(r));
631
if (r->w != r->w_start - r->max_level + 1) {
634
#endif /* __LQR_DEBUG__ */
635
LQR_CATCH(lqr_carver_flatten(r));
638
buf_size = r->w * r->h;
640
if (orientation != lqr_carver_get_orientation(r)) {
641
LQR_CATCH(lqr_carver_transpose(r));
643
LQR_CATCH(lqr_carver_build_emap(r));
645
w = lqr_carver_get_width(r);
646
h = lqr_carver_get_height(r);
648
for (y = 0; y < h; y++) {
649
for (x = 0; x < w; x++) {
650
data = orientation == 0 ? r->raw[y][x] : r->raw[x][y];
651
/* nrg = tanhf(r->en[data]); */
652
buffer[z0++] = r->en[data];
661
lqr_carver_get_energy_image(LqrCarver *r, void *buffer, gint orientation, LqrColDepth col_depth,
662
LqrImageType image_type)
670
gfloat nrg_min = G_MAXFLOAT;
675
gint alpha_channel, black_channel;
676
gboolean has_alpha, has_black, col_model_is_additive;
678
LQR_CATCH_F(orientation == 0 || orientation == 1);
680
LQR_CATCH_F(buffer != NULL);
682
switch (image_type) {
687
col_model_is_additive = TRUE;
689
case LQR_GREYA_IMAGE:
693
col_model_is_additive = TRUE;
699
col_model_is_additive = TRUE;
705
col_model_is_additive = TRUE;
711
col_model_is_additive = FALSE;
717
col_model_is_additive = FALSE;
719
case LQR_CMYKA_IMAGE:
723
col_model_is_additive = FALSE;
725
case LQR_CUSTOM_IMAGE:
730
has_alpha = (alpha_channel >= 0 ? TRUE : FALSE);
731
has_black = (black_channel >= 0 ? TRUE : FALSE);
733
if (r->nrg_active == FALSE) {
734
LQR_CATCH(lqr_carver_init_energy_related(r));
737
if (r->w != r->w_start - r->max_level + 1) {
740
#endif /* __LQR_DEBUG__ */
741
LQR_CATCH(lqr_carver_flatten(r));
744
buf_size = r->w * r->h;
746
LQR_CATCH_MEM(aux_buffer = g_try_new(gfloat, buf_size));
748
if (orientation != lqr_carver_get_orientation(r)) {
749
LQR_CATCH(lqr_carver_transpose(r));
751
LQR_CATCH(lqr_carver_build_emap(r));
753
w = lqr_carver_get_width(r);
754
h = lqr_carver_get_height(r);
756
for (y = 0; y < h; y++) {
757
for (x = 0; x < w; x++) {
758
data = orientation == 0 ? r->raw[y][x] : r->raw[x][y];
759
/* nrg = tanhf(r->en[data]); */
760
nrg = LQR_SATURATE(r->en[data]);
761
nrg_max = MAX(nrg_max, nrg);
762
nrg_min = MIN(nrg_min, nrg);
763
aux_buffer[z0++] = nrg;
767
for (z0 = 0; z0 < buf_size; z0++) {
768
if (nrg_max > nrg_min) {
769
nrg = (aux_buffer[z0] - nrg_min) / (nrg_max - nrg_min);
773
if (col_model_is_additive) {
774
for (k = 0; k < channels; k++) {
775
if (k != alpha_channel) {
776
lqr_pixel_set_norm(nrg, buffer, z0 * channels + k, col_depth);
782
lqr_pixel_set_norm(nrg, buffer, z0 * channels + black_channel, col_depth);
783
for (k = 0; k < channels; k++) {
784
if ((k != alpha_channel) && (k != black_channel)) {
785
lqr_pixel_set_norm(0.0, buffer, z0 * channels + k, col_depth);
789
for (k = 0; k < channels; k++) {
790
if ((k != alpha_channel) && (k != black_channel)) {
791
lqr_pixel_set_norm(nrg, buffer, z0 * channels + k, col_depth);
797
lqr_pixel_set_norm(1.0, buffer, z0 * channels + alpha_channel, col_depth);