~ubuntu-branches/ubuntu/saucy/digikam/saucy

« back to all changes in this revision

Viewing changes to core/libs/dimg/filters/transform/contentawarefilter.cpp

  • Committer: Package Import Robot
  • Author(s): Felix Geyer, Rohan Garg, Philip Muškovac, Felix Geyer
  • Date: 2011-09-23 18:18:55 UTC
  • mfrom: (1.2.36 upstream)
  • Revision ID: package-import@ubuntu.com-20110923181855-ifs67wxkugshev9k
Tags: 2:2.1.1-0ubuntu1
[ Rohan Garg ]
* New upstream release (LP: #834190)
  - debian/control
    + Build with libqtwebkit-dev
 - debian/kipi-plugins-common
    + Install libkvkontakte required by kipi-plugins
 - debian/digikam
    + Install panoramagui

[ Philip Muškovac ]
* New upstream release
  - debian/control:
    + Add libcv-dev, libcvaux-dev, libhighgui-dev, libboost-graph1.46-dev,
      libksane-dev, libxml2-dev, libxslt-dev, libqt4-opengl-dev, libqjson-dev,
      libgpod-dev and libqca2-dev to build-deps
    + Add packages for kipi-plugins, libmediawiki, libkface, libkgeomap and
      libkvkontakte
  - debian/rules:
    + Don't build with gphoto2 since it doesn't build with it.
  - Add kubuntu_fix_test_linking.diff to fix linking of the dngconverter test
  - update install files
  - update kubuntu_01_mysqld_executable_name.diff for new cmake layout
    and rename to kubuntu_mysqld_executable_name.diff
* Fix typo in digikam-data description (LP: #804894)
* Fix Vcs links

[ Felix Geyer ]
* Move library data files to the new packages libkface-data, libkgeomap-data
  and libkvkontakte-data.
* Override version of the embedded library packages to 1.0~digikam<version>.
* Exclude the library packages from digikam-dbg to prevent file conflicts in
  the future.
* Call dh_install with --list-missing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ============================================================
 
2
 *
 
3
 * This file is a part of digiKam project
 
4
 * http://www.digikam.org
 
5
 *
 
6
 * Date        : 2009-02-01
 
7
 * Description : Content aware resizer class.
 
8
 *
 
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>
 
12
 *
 
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)
 
17
 * any later version.
 
18
 *
 
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.
 
23
 *
 
24
 * ============================================================ */
 
25
 
 
26
#include "contentawarefilter.h"
 
27
 
 
28
// Qt includes
 
29
 
 
30
#include <QColor>
 
31
 
 
32
// Liquid rescale library include
 
33
 
 
34
#include "lqr.h"
 
35
 
 
36
// KDE includes
 
37
 
 
38
#include <kdebug.h>
 
39
 
 
40
namespace Digikam
 
41
{
 
42
 
 
43
// Static methods.
 
44
LqrRetVal s_carverProgressInit(const gchar* init_message);
 
45
LqrRetVal s_carverProgressUpdate(gdouble percentage);
 
46
LqrRetVal s_carverProgressEnd(const gchar* end_message);
 
47
 
 
48
// Static members.
 
49
 
 
50
/** Resizement is decomposed in 2 stages: horizontal and vertical.
 
51
 */
 
52
bool s_stage                   = false;
 
53
bool s_wResize                 = false;
 
54
bool s_hResize                 = false;
 
55
 
 
56
ContentAwareFilter* s_resiser = 0;
 
57
 
 
58
static LqrEnergyFuncBuiltinType toLqrEnergy(ContentAwareContainer::EnergyFunction func)
 
59
{
 
60
    switch (func)
 
61
    {
 
62
        case ContentAwareContainer::GradientNorm:
 
63
        default:
 
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;
 
75
    }
 
76
}
 
77
 
 
78
static LqrResizeOrder toLqrOrder(Qt::Orientation direction)
 
79
{
 
80
    switch (direction)
 
81
    {
 
82
        case Qt::Horizontal:
 
83
        default:
 
84
            return LQR_RES_ORDER_HOR;
 
85
        case Qt::Vertical:
 
86
            return LQR_RES_ORDER_VERT;
 
87
    }
 
88
}
 
89
 
 
90
class ContentAwareFilterPriv
 
91
{
 
92
public:
 
93
 
 
94
    ContentAwareFilterPriv()
 
95
    {
 
96
        carver   = 0;
 
97
        progress = 0;
 
98
    }
 
99
 
 
100
    ContentAwareContainer settings;
 
101
 
 
102
    LqrCarver*            carver;
 
103
    LqrProgress*          progress;
 
104
 
 
105
};
 
106
 
 
107
ContentAwareFilter::ContentAwareFilter(QObject* parent)
 
108
    : DImgThreadedFilter(parent),
 
109
      d(new ContentAwareFilterPriv)
 
110
{
 
111
    initFilter();
 
112
}
 
113
 
 
114
ContentAwareFilter::ContentAwareFilter(DImg* orgImage, QObject* parent, const ContentAwareContainer& settings)
 
115
    : DImgThreadedFilter(orgImage, parent, "ContentAwareFilter"),
 
116
      d(new ContentAwareFilterPriv)
 
117
{
 
118
    initFilter();
 
119
 
 
120
    s_stage     = false;
 
121
    s_resiser   = this;
 
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);
 
125
 
 
126
    if (d->carver)
 
127
    {
 
128
        // Non null carver object operations
 
129
 
 
130
        // Ask Lqr library to preserve our picture
 
131
        lqr_carver_set_preserve_input_image(d->carver);
 
132
 
 
133
        // Initialize the carver object
 
134
        lqr_carver_init(d->carver, d->settings.step, d->settings.rigidity);
 
135
 
 
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);
 
142
 
 
143
        lqr_carver_set_side_switch_frequency(d->carver, d->settings.side_switch_freq);
 
144
 
 
145
        // Set enlargement steps as suggested by Carlo Baldassi
 
146
        lqr_carver_set_enl_step(d->carver, 1.5);
 
147
 
 
148
        // Choose a gradient function
 
149
        lqr_carver_set_energy_function_builtin(d->carver, toLqrEnergy(d->settings.func));
 
150
 
 
151
        // Choose the resize order
 
152
        lqr_carver_set_resize_order(d->carver, toLqrOrder(d->settings.resize_order));
 
153
 
 
154
        // Set a bias if any mask
 
155
        if (!d->settings.mask.isNull())
 
156
        {
 
157
            buildBias(d->settings.mask);
 
158
        }
 
159
 
 
160
        // Set skin tone mask if option is activated
 
161
        if (d->settings.preserve_skin_tones)
 
162
        {
 
163
            buildSkinToneBias();
 
164
        }
 
165
    }
 
166
}
 
167
 
 
168
ContentAwareFilter::~ContentAwareFilter()
 
169
{
 
170
    cancelFilter();
 
171
 
 
172
    if (d->carver)
 
173
    {
 
174
        lqr_carver_destroy(d->carver);
 
175
    }
 
176
 
 
177
    delete d;
 
178
}
 
179
 
 
180
void ContentAwareFilter::getEnergyImage()
 
181
{
 
182
    if (!d->carver)
 
183
    {
 
184
        return;
 
185
    }
 
186
 
 
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));
 
190
 
 
191
    lqr_carver_get_energy_image(d->carver, buff, 1, LQR_COLDEPTH_8I, LQR_RGBA_IMAGE);
 
192
}
 
193
 
 
194
void ContentAwareFilter::filterImage()
 
195
{
 
196
    if (!d->carver)
 
197
    {
 
198
        return;
 
199
    }
 
200
 
 
201
    uint  x   = 0;
 
202
    uint  y   = 0;
 
203
    uint  w   = 0;
 
204
    uint  h   = 0;
 
205
 
 
206
    s_wResize = (m_orgImage.width()  == d->settings.width)  ? false : true;
 
207
    s_hResize = (m_orgImage.height() == d->settings.height) ? false : true;
 
208
 
 
209
    // Liquid rescale
 
210
    lqr_carver_resize(d->carver, d->settings.width, d->settings.height);
 
211
 
 
212
    if (!runningFlag())
 
213
    {
 
214
        return;
 
215
    }
 
216
 
 
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());
 
221
 
 
222
    // Write pixels in the DImg structure image
 
223
    lqr_carver_scan_reset(d->carver);
 
224
 
 
225
    void*           rgb=0;
 
226
    uchar*          rgbOut8=0;
 
227
    unsigned short* rgbOut16=0;
 
228
 
 
229
    if (m_orgImage.sixteenBit())
 
230
    {
 
231
        while (runningFlag() && lqr_carver_scan_ext(d->carver, (gint*)&x, (gint*)&y, &rgb))
 
232
        {
 
233
            rgbOut16 = (unsigned short*)rgb;
 
234
            m_destImage.setPixelColor(x, y, DColor(rgbOut16[2], rgbOut16[1], rgbOut16[0], 65535, true));
 
235
        }
 
236
    }
 
237
    else
 
238
    {
 
239
        while (runningFlag() && lqr_carver_scan_ext(d->carver, (gint*)&x, (gint*)&y, &rgb))
 
240
        {
 
241
            rgbOut8 = (uchar*)rgb;
 
242
            m_destImage.setPixelColor(x, y, DColor(rgbOut8[2], rgbOut8[1], rgbOut8[0], 255, false));
 
243
        }
 
244
    }
 
245
}
 
246
 
 
247
void ContentAwareFilter::progressCallback(int progress)
 
248
{
 
249
    if (progress%5 == 0)
 
250
    {
 
251
        postProgress( progress );
 
252
    }
 
253
 
 
254
    //kDebug() << "Content Aware Resizing: " << progress << " %";
 
255
}
 
256
 
 
257
void ContentAwareFilter::cancelFilter()
 
258
{
 
259
    // Handle cancel operations with lqr library.
 
260
    kDebug() << "Stop LibLqr computation...";
 
261
    lqr_carver_cancel(d->carver);
 
262
    DImgThreadedFilter::cancelFilter();
 
263
}
 
264
 
 
265
bool ContentAwareFilter::isSkinTone(const DColor& color)
 
266
{
 
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;
 
272
 
 
273
    return( (B/G         < 1.249) &&
 
274
            (S/3.0*R     > 0.696) &&
 
275
            (1.0/3.0-B/S > 0.014) &&
 
276
            (G/(3.0*S)   < 0.108)
 
277
          );
 
278
}
 
279
 
 
280
void ContentAwareFilter::buildSkinToneBias()
 
281
{
 
282
    DColor c;
 
283
 
 
284
    for (uint x=0; x < m_orgImage.width(); ++x)
 
285
    {
 
286
        for (uint y=0; y < m_orgImage.height(); ++y)
 
287
        {
 
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);
 
292
        }
 
293
    }
 
294
}
 
295
 
 
296
void ContentAwareFilter::buildBias(const QImage& mask)
 
297
{
 
298
    QColor pixColor;
 
299
    int    r,g,b,a;
 
300
 
 
301
    for (int x=0; x < mask.width(); ++x)
 
302
    {
 
303
        for (int y=0; y < mask.height(); ++y)
 
304
        {
 
305
            pixColor = QColor::fromRgba(mask.pixel(x, y));
 
306
            pixColor.getRgb(&r, &g, &b, &a);
 
307
            gdouble bias=0.0;
 
308
 
 
309
            if (g == 255)
 
310
            {
 
311
                bias=1000000.0;
 
312
            }
 
313
 
 
314
            if (r == 255)
 
315
            {
 
316
                bias=-1000000.0;
 
317
            }
 
318
 
 
319
            lqr_carver_bias_add_xy(d->carver,bias, x, y);
 
320
        }
 
321
    }
 
322
}
 
323
 
 
324
FilterAction ContentAwareFilter::filterAction()
 
325
{
 
326
    bool isReproducible = d->settings.mask.isNull();
 
327
    DefaultFilterAction<ContentAwareFilter> action(isReproducible);
 
328
 
 
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);
 
337
 
 
338
    return action;
 
339
}
 
340
 
 
341
void ContentAwareFilter::readParameters(const FilterAction& action)
 
342
{
 
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();
 
351
}
 
352
 
 
353
// ------------------------------------------------------------------------------------
 
354
// Static methods.
 
355
 
 
356
LqrRetVal s_carverProgressInit(const gchar* /*init_message*/)
 
357
{
 
358
    if (!s_stage)
 
359
    {
 
360
        s_resiser->progressCallback(0);
 
361
    }
 
362
    else
 
363
    {
 
364
        s_resiser->progressCallback(50);
 
365
    }
 
366
 
 
367
    return LQR_OK;
 
368
}
 
369
 
 
370
LqrRetVal s_carverProgressUpdate(gdouble percentage)
 
371
{
 
372
    int progress;
 
373
 
 
374
    if (!s_stage)
 
375
    {
 
376
        if (!s_wResize || !s_hResize)
 
377
        {
 
378
            progress = (int)(percentage*100.0);
 
379
        }
 
380
        else
 
381
        {
 
382
            progress = (int)(percentage*50.0);
 
383
        }
 
384
    }
 
385
    else
 
386
    {
 
387
        progress = (int)(50.0 + percentage*50.0);
 
388
    }
 
389
 
 
390
    s_resiser->progressCallback(progress);
 
391
    return LQR_OK;
 
392
}
 
393
 
 
394
LqrRetVal s_carverProgressEnd(const gchar* /*end_message*/)
 
395
{
 
396
    if (!s_stage)
 
397
    {
 
398
        if (!s_wResize || !s_hResize)
 
399
        {
 
400
            s_resiser->progressCallback(100);
 
401
        }
 
402
        else
 
403
        {
 
404
            s_resiser->progressCallback(50);
 
405
        }
 
406
 
 
407
        s_stage = true;
 
408
    }
 
409
    else
 
410
    {
 
411
        s_resiser->progressCallback(100);
 
412
    }
 
413
 
 
414
    return LQR_OK;
 
415
}
 
416
 
 
417
} // namespace Digikam