1
/* ============================================================
3
* This file is a part of digiKam project
4
* http://www.digikam.org
7
* Description : Content aware resizer class.
9
* Copyright (C) 2009 by Julien Pontabry <julien dot pontabry at ulp dot u-strasbg dot fr>
10
* Copyright (C) 2009-2010 by Gilles Caulier <caulier dot gilles at gmail dot com>
11
* Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
13
* This program is free software; you can redistribute it
14
* and/or modify it under the terms of the GNU General
15
* Public License as published by the Free Software Foundation;
16
* either version 2, or (at your option)
19
* This program is distributed in the hope that it will be useful,
20
* but WITHOUT ANY WARRANTY; without even the implied warranty of
21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
* GNU General Public License for more details.
24
* ============================================================ */
26
#include "contentawarefilter.h"
32
// Liquid rescale library include
44
LqrRetVal s_carverProgressInit(const gchar* init_message);
45
LqrRetVal s_carverProgressUpdate(gdouble percentage);
46
LqrRetVal s_carverProgressEnd(const gchar* end_message);
50
/** Resizement is decomposed in 2 stages: horizontal and vertical.
53
bool s_wResize = false;
54
bool s_hResize = false;
56
ContentAwareFilter* s_resiser = 0;
58
static LqrEnergyFuncBuiltinType toLqrEnergy(ContentAwareContainer::EnergyFunction func)
62
case ContentAwareContainer::GradientNorm:
64
return LQR_EF_GRAD_NORM;
65
case ContentAwareContainer::SumOfAbsoluteValues:
66
return LQR_EF_GRAD_SUMABS;
67
case ContentAwareContainer::XAbsoluteValue:
68
return LQR_EF_GRAD_XABS;
69
case ContentAwareContainer::LumaGradientNorm:
70
return LQR_EF_LUMA_GRAD_NORM;
71
case ContentAwareContainer::LumaSumOfAbsoluteValues:
72
return LQR_EF_LUMA_GRAD_SUMABS;
73
case ContentAwareContainer::LumaXAbsoluteValue:
74
return LQR_EF_LUMA_GRAD_XABS;
78
static LqrResizeOrder toLqrOrder(Qt::Orientation direction)
84
return LQR_RES_ORDER_HOR;
86
return LQR_RES_ORDER_VERT;
90
class ContentAwareFilterPriv
94
ContentAwareFilterPriv()
100
ContentAwareContainer settings;
103
LqrProgress* progress;
107
ContentAwareFilter::ContentAwareFilter(QObject* parent)
108
: DImgThreadedFilter(parent),
109
d(new ContentAwareFilterPriv)
114
ContentAwareFilter::ContentAwareFilter(DImg* orgImage, QObject* parent, const ContentAwareContainer& settings)
115
: DImgThreadedFilter(orgImage, parent, "ContentAwareFilter"),
116
d(new ContentAwareFilterPriv)
122
d->settings = settings;
123
d->carver = lqr_carver_new_ext(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(), 4,
124
m_orgImage.sixteenBit() ? LQR_COLDEPTH_16I : LQR_COLDEPTH_8I);
128
// Non null carver object operations
130
// Ask Lqr library to preserve our picture
131
lqr_carver_set_preserve_input_image(d->carver);
133
// Initialize the carver object
134
lqr_carver_init(d->carver, d->settings.step, d->settings.rigidity);
136
// Create a progress object
137
d->progress = lqr_progress_new();
138
lqr_progress_set_init(d->progress, s_carverProgressInit);
139
lqr_progress_set_update(d->progress, s_carverProgressUpdate);
140
lqr_progress_set_end(d->progress, s_carverProgressEnd);
141
lqr_carver_set_progress(d->carver, d->progress);
143
lqr_carver_set_side_switch_frequency(d->carver, d->settings.side_switch_freq);
145
// Set enlargement steps as suggested by Carlo Baldassi
146
lqr_carver_set_enl_step(d->carver, 1.5);
148
// Choose a gradient function
149
lqr_carver_set_energy_function_builtin(d->carver, toLqrEnergy(d->settings.func));
151
// Choose the resize order
152
lqr_carver_set_resize_order(d->carver, toLqrOrder(d->settings.resize_order));
154
// Set a bias if any mask
155
if (!d->settings.mask.isNull())
157
buildBias(d->settings.mask);
160
// Set skin tone mask if option is activated
161
if (d->settings.preserve_skin_tones)
168
ContentAwareFilter::~ContentAwareFilter()
174
lqr_carver_destroy(d->carver);
180
void ContentAwareFilter::getEnergyImage()
187
int w = lqr_carver_get_width(d->carver);
188
int h = lqr_carver_get_height(d->carver);
189
guchar* buff = (guchar*) malloc(w*h*3*sizeof(guchar));
191
lqr_carver_get_energy_image(d->carver, buff, 1, LQR_COLDEPTH_8I, LQR_RGBA_IMAGE);
194
void ContentAwareFilter::filterImage()
206
s_wResize = (m_orgImage.width() == d->settings.width) ? false : true;
207
s_hResize = (m_orgImage.height() == d->settings.height) ? false : true;
210
lqr_carver_resize(d->carver, d->settings.width, d->settings.height);
217
// Create a new image
218
w = lqr_carver_get_width(d->carver);
219
h = lqr_carver_get_height(d->carver);
220
m_destImage = DImg(w, h, m_orgImage.sixteenBit());
222
// Write pixels in the DImg structure image
223
lqr_carver_scan_reset(d->carver);
227
unsigned short* rgbOut16=0;
229
if (m_orgImage.sixteenBit())
231
while (runningFlag() && lqr_carver_scan_ext(d->carver, (gint*)&x, (gint*)&y, &rgb))
233
rgbOut16 = (unsigned short*)rgb;
234
m_destImage.setPixelColor(x, y, DColor(rgbOut16[2], rgbOut16[1], rgbOut16[0], 65535, true));
239
while (runningFlag() && lqr_carver_scan_ext(d->carver, (gint*)&x, (gint*)&y, &rgb))
241
rgbOut8 = (uchar*)rgb;
242
m_destImage.setPixelColor(x, y, DColor(rgbOut8[2], rgbOut8[1], rgbOut8[0], 255, false));
247
void ContentAwareFilter::progressCallback(int progress)
251
postProgress( progress );
254
//kDebug() << "Content Aware Resizing: " << progress << " %";
257
void ContentAwareFilter::cancelFilter()
259
// Handle cancel operations with lqr library.
260
kDebug() << "Stop LibLqr computation...";
261
lqr_carver_cancel(d->carver);
262
DImgThreadedFilter::cancelFilter();
265
bool ContentAwareFilter::isSkinTone(const DColor& color)
267
// NOTE: color is previously converted to eight bits.
268
double R = color.red() / 255.0;
269
double G = color.green() / 255.0;
270
double B = color.blue() / 255.0;
271
double S = R + G + B;
273
return( (B/G < 1.249) &&
275
(1.0/3.0-B/S > 0.014) &&
280
void ContentAwareFilter::buildSkinToneBias()
284
for (uint x=0; x < m_orgImage.width(); ++x)
286
for (uint y=0; y < m_orgImage.height(); ++y)
288
c = m_orgImage.getPixelColor(x, y);
289
c.convertToEightBit();
290
gdouble bias = 10000*isSkinTone(c);
291
lqr_carver_bias_add_xy(d->carver,bias,x,y);
296
void ContentAwareFilter::buildBias(const QImage& mask)
301
for (int x=0; x < mask.width(); ++x)
303
for (int y=0; y < mask.height(); ++y)
305
pixColor = QColor::fromRgba(mask.pixel(x, y));
306
pixColor.getRgb(&r, &g, &b, &a);
319
lqr_carver_bias_add_xy(d->carver,bias, x, y);
324
FilterAction ContentAwareFilter::filterAction()
326
bool isReproducible = d->settings.mask.isNull();
327
DefaultFilterAction<ContentAwareFilter> action(isReproducible);
329
action.addParameter("height", d->settings.height);
330
action.addParameter("preserve_skin_tones", d->settings.preserve_skin_tones);
331
action.addParameter("rigidity", d->settings.rigidity);
332
action.addParameter("side_switch_freq", d->settings.side_switch_freq);
333
action.addParameter("step", d->settings.step);
334
action.addParameter("width", d->settings.width);
335
action.addParameter("func", d->settings.func);
336
action.addParameter("resize_order", d->settings.resize_order);
341
void ContentAwareFilter::readParameters(const FilterAction& action)
343
d->settings.height = action.parameter("height").toUInt();
344
d->settings.preserve_skin_tones = action.parameter("preserve_skin_tones").toBool();
345
d->settings.rigidity = action.parameter("rigidity").toDouble();
346
d->settings.side_switch_freq = action.parameter("side_switch_freq").toInt();
347
d->settings.step = action.parameter("step").toInt();
348
d->settings.width = action.parameter("width").toUInt();
349
d->settings.func = (ContentAwareContainer::EnergyFunction)action.parameter("func").toInt();
350
d->settings.resize_order = (Qt::Orientation)action.parameter("resize_order").toInt();
353
// ------------------------------------------------------------------------------------
356
LqrRetVal s_carverProgressInit(const gchar* /*init_message*/)
360
s_resiser->progressCallback(0);
364
s_resiser->progressCallback(50);
370
LqrRetVal s_carverProgressUpdate(gdouble percentage)
376
if (!s_wResize || !s_hResize)
378
progress = (int)(percentage*100.0);
382
progress = (int)(percentage*50.0);
387
progress = (int)(50.0 + percentage*50.0);
390
s_resiser->progressCallback(progress);
394
LqrRetVal s_carverProgressEnd(const gchar* /*end_message*/)
398
if (!s_wResize || !s_hResize)
400
s_resiser->progressCallback(100);
404
s_resiser->progressCallback(50);
411
s_resiser->progressCallback(100);
417
} // namespace Digikam