4

ゴール

私はBoost GILに移行して、私が実装した同様の機能を置き換えようとしていますが、これは保守可能な寿命の終わりに近づいています。

を使用して 24 BPP、8 ビット RGB 画像で動作する既存のコードがありますuint8_t*。同じインターフェイスを使用してさまざまな場所 (OpenGL バッファーなど) から画像を公開しているため、これを変更することはできません。また、既にかなり多くのコードが存在します。

したがって、ファイルを読み取り、ストレージの管理に使用できる にピクセルをバイト単位でコピーすることから始めて、GIL を小さなステップで使用しようとしていますstd::vector<uint8_t>が、それでも をuint8_t*使用して を取得し&vector[0]ます。

これは、リファクタリングが理にかなっているポイントになるまで、既存のインターフェイスの背後に透過的にドロップできます。

私が試したこと

copy_pixels()これは、適切に構築された 2 つのビューを使用する単純なケースであると考えました。

ドキュメントを調べて試してみることで達成できたことの合計を示す、最小限の完全な例をまとめました。

#include <boost/gil/rgb.hpp>
#include <boost/gil/extension/io/png_dynamic_io.hpp>
#include <stdint.h>
#include <vector>

int main() {
  std::vector<uint8_t> storage;
  {
    using namespace boost::gil;
    rgb8_image_t img;
    png_read_image("test.png", img);

    // what should replace 3 here to be more "generic"?
    storage.resize(img.width()*img.height()*3);

    // doesn't work, the type of the images aren't compatible.
    copy_pixels(const_view(img), 
                interleaved_view(img.width(), img.height(), &storage[0], 3*img.width()));
  }
}

私が立ち往生している場所

これはコンパイルされません:

error: cannot convert ‘const boost::gil::pixel<unsigned char, boost::gil::layout<boost::mpl::vector3<boost::gil::red_t, boost::gil::green_t, boost::gil::blue_t> > >’ to ‘unsigned char’ in assignment

これは一目瞭然です。RGB ピクセルは、unsigned char自動的に単一のピクセルに変換することはできません。を使用してこれを修正しようと思いましたが、これらの変換でcopy_and_convert_pixels()3:1 (つまり、ソース イメージの各ピクセルの出力に 3 つの s がある) の問題を回避する方法がわかりません。unsigned char変換は、色空間の変換 (例: RGB->HSV) またはパッキングの変更を目的としているようです。

4

4 に答える 4

5

rgb8_pixel_t の各色を個別に push_back します。

struct PixelInserter{
        std::vector<uint8_t>* storage;
        PixelInserter(std::vector<uint8_t>* s) : storage(s) {}
        void operator()(boost::gil::rgb8_pixel_t p) const {
                storage->push_back(boost::gil::at_c<0>(p));
                storage->push_back(boost::gil::at_c<1>(p));
                storage->push_back(boost::gil::at_c<2>(p));
        }
};

int main() {
  std::vector<uint8_t> storage;
  {
    using namespace boost::gil;
    rgb8_image_t img;
    png_read_image("test.png", img);
    storage.reserve(img.width() * img.height() * num_channels<rgb8_image_t>());
    for_each_pixel(const_view(img), PixelInserter(&storage));
  }
...
}

...しかし、私も GIL の専門家ではありません。

于 2011-11-29T05:13:04.303 に答える
1

これと同じ問題に遭遇しました。ここでは、将来の参考のために、自分で解決したので思いつく答えを示します。

その copy_pixels アプローチは良いです。唯一の問題は宛先タイプです。rgb8_pixel_t が 3 つの連続した uint8_t であるかのようにメモリ内でフォーマットされていることがわかっている場合は、次のようにするだけです。

boost::gil::rgba8_image_t image;
boost::gil::png_read_image(filePath, image);
auto view = boost::gil::view(image);
typedef decltype(view)::value_type pixel;
static_assert(sizeof(pixel) == 4, "the glTexImage2D call below assumes this");

pixel imageData[view.width() * view.height()];
boost::gil::copy_pixels(view, boost::gil::interleaved_view(view.width(), view.height(), imageData, view.width() * sizeof(pixel)));

gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, imageData);

それは多かれ少なかれ、私のプロジェクトからコピーされたものです。私は 32 ビット イメージを使用していますが、他の単一のハードコードされたイメージ タイプでも同じように動作するはずです。(GIL の「any_」の使用方法を学んでいないため、動的に決定される画像タイプについてコメントすることはできません。)

上記のコードでは、rgba8_pixel_t が OpenGL が "INT_8_8_8_8" などと見なすものであることを、static_assert を実行することで大雑把に確認しています。推測してアサーションで確認するよりも、GILのドキュメントからその情報を取得する方がおそらく良いと思いますが、そこに明確な声明を見つけることができないようです(私はGILも初めてです。だから多分私はそれを見逃しているだけです)。しかし、これが GIL のピクセル タイプの設計意図の一部であることは明らかです。たとえば、GIL デザイン ガイドには、「最も一般的に使用されるピクセルは、値がメモリ内にまとめられている同種のピクセルです」とある箇所で述べられています。「思い出の中で一緒に」はまさに私が探しているもののようです。その直後、ガイドは「平面」ピクセル タイプについて説明します。1 つのピクセルのカラー チャネル値が一緒にメモリに格納されていません。その区別をサポートするのと同じくらい慎重に面倒を見て、インターリーブされたピクセルタイプがそのカラー値をメモリにしっかりと詰め込むことを実際に気にしないのは奇妙です.

とにかく、私は自分のプロジェクトで、このアプローチが少なくとも私が使用している Boost のバージョン (1.57) で機能することを実証しました。将来のバージョンでこれが変更された場合、私の static_assert はほぼ確実にそれをキャッチすると主張します。

(潜在的にフォールバックする別のアプローチは、実際に先に進み、平面ピクセルを使用して、uint_8_t 配列と for_each_pixel が提供する rgb8_pixel_t の間をマッピングすることです。

boost::gil::rgba8_image_t image;
boost::gil::png_read_image(filePath, image);
auto view = boost::gil::view(image);
uint8_t data[view.width() * view.height() * view.num_channels()];

using boost::gil::rgba8_pixel_t;
uint8_t* cursor = data;
boost::gil::for_each_pixel(view, std::function<void(rgba8_pixel_t)>(
        [&cursor](rgba8_pixel_t pixel)
        {
            boost::gil::rgba8_planar_ptr_t pixelInData(cursor++, cursor++, cursor++, cursor++);
            *pixelInData = pixel;

            // if data were an array of rgba8_pixel_t's, then we could just do this and be done with it:
            // *cursor++ = pixel;
            // (but in that case we might as well use copy_pixels!)
        }));

gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, data);

しかし、これは実際には at_c 戦略よりも優れているわけではありません。それは私が推測するちょうどいい例です。*_planar_ptr_t は驚くほどスマートです!)

また、今日の C++ では、"for each" ループの本体をキャプチャするために個別の型を作成する必要がないことに注意してください。上記のように、無名関数を使用できます。(GILが関数オブジェクトの内部コピー代入などを行うと推測し、裸の無名関数が渡されるとコンパイラが怒ってしまうので、std::functionでラップします.std::functionを推測しますラッピングはここで効率を少し下げるかもしれません; 私の場合、それは重要ではないようです.)

于 2015-01-26T01:54:02.833 に答える
0

私が最終的に使用した実際のコードの完全で簡略化された形式は次のとおりです。

#include <boost/gil/rgb.hpp>
#include <boost/gil/extension/io/png_dynamic_io.hpp>
#include <vector>
#include <string>
#include <cstdint>

struct dimension {
  int w,h;
};

namespace {
struct PixelInserter {
    std::vector<uint8_t>* storage;
    PixelInserter(std::vector<uint8_t>* s) : storage(s) {}
    void operator()(boost::gil::rgb8_pixel_t p) const {
        using boost::gil::at_c;
        storage->push_back(at_c<0>(p));
        storage->push_back(at_c<1>(p));
        storage->push_back(at_c<2>(p));
    }
};

// This could probably share code with the PixelInserter
struct PixelWriter {
    const uint8_t *pixels;
    PixelWriter(const uint8_t *pixels) : pixels(pixels) {}
    void operator()(boost::gil::rgb8_pixel_t& p) {
        using boost::gil::at_c;
        at_c<0>(p) = *pixels++;
        at_c<1>(p) = *pixels++;
        at_c<2>(p) = *pixels++;
    }
};
}

void savePNG(const std::string& filename, const uint8_t *pixels, const dimension& d) {
    boost::gil::rgb8_image_t img(d.w, d.h);
    for_each_pixel(view(img), PixelWriter(pixels));
    boost::gil::png_write_view(filename, view(img));
}

std::vector<uint8_t> readPNG(const std::string& fn, dimension& d) {
    boost::gil::rgb8_image_t image_type;
    image_type img;
    png_read_image(fn, img);
    d.w = img.width();
    d.h = img.height();

    std::vector<uint8_t> storage;
    storage.reserve(d.w*d.h*boost::gil::num_channels<image_type>());
    for_each_pixel(const_view(img), PixelInserter(&storage));
    return storage;
}

int main(int argc, char **argv) {
  dimension d;
  const std::vector<uint8_t> pixels = readPNG(argv[1], d);
  savePNG(argv[2], &pixels[0], d);
}

GILヘッダーを含める前に、もともと次のものを持っていました。

#define png_infopp_NULL (png_infopp)NULL
#define int_p_NULL (int*)NULL

当時持っていたブーストのバージョンでどのような問題が修正されたのか正確にはわかりませんが、1.48 では必要ないようです。

于 2013-06-22T11:12:02.603 に答える
0

これは私がかつて使用したいくつかのコードです:

 unsigned char * buf = new unsigned char[w * h];

 boost::gil::gray8_view_t image =
          boost::gil::interleaved_view(w, h, (boost::gil::gray8_pixel_t*)buf, w);

 for (size_t i = 0; i < ...; ++i)
 {
   boost::gil::gray8_view_t::x_iterator it = image.row_begin(i);

   // use it[j] to access pixel[i][j]
 }

グレースケールのみですが、おそらくカラー版も同様です。

于 2011-11-28T18:37:29.757 に答える