2009年4月1日水曜日

boost::gil で bicubic

 昨日、gil で挑戦してみてはいかが?と書いたが、必要になって挑戦するはめになった。
libs/gil/example/resize.cpp というのを見つけて、これはもしや?と思ったが、boost/gil/extension/numeric/* が、ごっそりと抜け落ちていてコンパイルできない…。どこにあるんだべーと探したら、GIL本家のダウンロードページにあった(現在、ここからサルベージするしかない)。

さっそく bicubic が落ちていないか探したが、bilinear までしか実装されていないようだ。チャレンジしようと思ったが、gil をよく知らないので苦労した。チュートリアルの日本語訳があって、非常に助かりました。
という訳で、csampler.hpp です。
/*************************************************************************************************/

#ifndef GIL_CSAMPLER_HPP
#define GIL_CSAMPLER_HPP

#include "../../extension/dynamic_image/dynamic_image_all.hpp"
#include "pixel_numeric_operations.hpp"
#include "sampler.hpp"

namespace boost { namespace gil {

struct truncate_channel_fn {
    template <typename SrcChannel, typename DstChannel>
    void operator()(const SrcChannel& src, DstChannel& dst) {
        typedef typename channel_traits<DstChannel>::value_type dst_value_t;
        if(std::numeric_limits<dst_value_t>::max() <= src)
          dst = std::numeric_limits<dst_value_t>::max();
        else if( src < 0 )  dst = dst_value_t();
        else  dst = dst_value_t(src);
    }
};

template <typename SrcPixel, typename DstPixel>
void truncate_pixel(const SrcPixel& src, DstPixel& dst) {
    static_for_each(src,dst,truncate_channel_fn());
}


/// \brief A sampler that sets the destination pixel as the bicubic interpolation of the four closest pixels from the source. 
/// If outside the bounds, it doesn't change the destination
/// \ingroup ImageAlgorithms
struct bicubic_sampler {};

template <typename DstP, typename SrcView, typename F>
bool sample(bicubic_sampler, const SrcView& src, const point2<F>& p, DstP& result) {
    typedef typename SrcView::value_type SrcP;
    point2<int> p0(ifloor(p)); // the closest integer coordinate top left from p
    if (p0.x < 0 || p0.y < 0 || p0.x>=src.width() || p0.y>=src.height()) return false;

    pixel<F,devicen_layout_t<num_channels<SrcView>::value> > mp(0);                     // suboptimal
    typename SrcView::xy_locator loc=src.xy_at(p0.x,p0.y);

    for( int y = -1; y < 3; ++y ) {
      for( int x = -1; x < 3; ++x ) {
        point2<F> frac(std::abs(p.x-static_cast<F>(p0.x+x)),std::abs(p.y-static_cast<F>(p0.y+y)));
        F wx, wy;
        if( frac.x < static_cast<F>(1) )    wx = (frac.x - 1) * (frac.x * frac.x - frac.x - 1);
        else                                wx = (1 - frac.x) * (frac.x - 2) * (frac.x - 2);
        if( frac.y < static_cast<F>(1) )    wy = (frac.y - 1) * (frac.y * frac.y - frac.y - 1);
        else                                wy = (1 - frac.y) * (frac.y - 2) * (frac.y - 2);
        int offx = ( p0.x+x < 0 || p0.x+x>=src.width() ) ? 0 : x;
        int offy = ( p0.y+y < 0 || p0.y+y>=src.height() ) ? 0 : y;
        detail::add_dst_mul_src<SrcP,F,pixel<F,devicen_layout_t<num_channels<SrcView>::value> > >()( loc(offx,offy), wx * wy, mp );
      }
    }

    // Convert from floating point average value to the source type
    SrcP src_result;
    truncate_pixel(mp,src_result);

    color_convert(src_result, result);
    return true;
}

} }  // namespace boost::gil

#endif

お試しコードは、resize.cpp をちょいと改造するだけ。せっかくの bicubic なので拡大してみました。
#include <boost/gil/image.hpp>
#include <boost/gil/typedefs.hpp>
#include <boost/gil/extension/io/jpeg_io.hpp>
#include <boost/gil/extension/numeric/sampler.hpp>
#include <boost/gil/extension/numeric/csampler.hpp>
#include <boost/gil/extension/numeric/resample.hpp>

int main() {
    using namespace boost::gil;

    rgb8_image_t img;
    jpeg_read_image("test.jpg",img);

    // test resize_view
    // Scale the image to 100x100 pixels using bilinear resampling
    rgb8_image_t square100x100(600,600);
    resize_view(const_view(img), view(square100x100), bicubic_sampler());
    jpeg_write_view("out-resize.jpg",const_view(square100x100));

    return 0;
}

0 件のコメント: