How to read indexed / paletted image pixels in ImageMagick 7 / Magick++

69 Views Asked by At

I'm trying to read an index / paletted / colormapped PNG image with Magick++ / ImageMagick 7, but I can't find a proper example on how to do that. From the docs here I'm grasping that this code should read the pixels / indices:

imtest.cpp:

#include <iostream>

#include <Magick++.h>

int main(int argc, const char *argv[])
{
    std::cout << "Reading " << argv[1];
    Magick::Image img;
    try
    {
        img.read(argv[1]);
    }
    catch (const Magick::Exception &ex)
    {
        throw std::runtime_error("Failed to read image");
    }
    auto imgSize = img.size();
    std::cout << " -> " << imgSize.width() << "x" << imgSize.height() << ", ";
    if (img.classType() == Magick::ClassType::PseudoClass && img.type() == Magick::ImageType::PaletteType)
    {
        std::vector<uint8_t> data;
        std::cout << "paletted, " << img.colorMapSize() << " colors" << std::endl;
        // get palette indices as unsigned chars
        const auto nrOfColors = img.colorMapSize();
        // auto pixels = img.getConstPixels(0, 0, img.columns(), img.rows()); // needed to call this first for getConstMetacontent to work?
        auto indices = static_cast<const uint8_t *>(img.getConstMetacontent());
        const auto nrOfIndices = img.columns() * img.rows();
        for (std::remove_const<decltype(nrOfIndices)>::type i = 0; i < nrOfIndices; i++)
        {
            data.push_back(indices[i]);
        }
        std::cout << "Read " << data.size() << " pixels" << std::endl;
    }
    else
    {
        throw std::runtime_error("Unsupported image format");
    }
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)

find_package(PkgConfig REQUIRED)

pkg_check_modules(LIBMAGICK REQUIRED IMPORTED_TARGET
    Magick++
)

add_definitions(-DMAGICKCORE_HDRI_ENABLE=0)
add_definitions(-DMAGICKCORE_QUANTUM_DEPTH=16)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11)

add_executable(imtest imtest.cpp)
target_include_directories(imtest PRIVATE ${ImageMagick_INCLUDE_DIRS})
target_link_libraries(imtest PkgConfig::LIBMAGICK)

Run with imtest <image>

Is this correct, or am I doing anything wrong? There's a bug in getConstMetacontent() that was fixed recently, but I still have no idea if what I'm doing is correct.
In ImageMagick 6 / Magick++ the function to get indexed pixel data was called getConstIndexes(), but it does not exist anymore.

1

There are 1 best solutions below

3
On

The correct code to read the IndexChannel using Magick++ is:

#include <iostream>
#include <Magick++.h>

int main(int argc, const char *argv[])
{
    std::cout << "Reading " << argv[1];
    Magick::Image img;
    try
    {
        img.read(argv[4]);
    }
    catch (const Magick::Exception &ex)
    {
        throw std::runtime_error("Failed to read image");
    }
    auto imgSize = img.size();
    std::cout << " -> " << imgSize.width() << "x" << imgSize.height() << ", ";
    if (img.classType() == Magick::ClassType::PseudoClass && img.type() == Magick::ImageType::PaletteType)
    {
        std::vector<MagickCore::Quantum> data;
        std::cout << "paletted, " << img.colorMapSize() << " colors" << std::endl;
        // split off IndexChannel without making a deep copy
        auto temp = img.separate(MagickCore::IndexChannel);
        // get pointer to IndexChannel pixels
        auto pixels = temp.getConstPixels(0, 0, temp.columns(), temp.rows());
        for (std::size_t i = 0; i < temp.columns() * temp.rows(); i++)
        {
            auto value = *pixels++;
            std::cout << value << ", ";
            data.push_back(value);
        }
        std::cout << "Read " << data.size() << " pixels" << std::endl;
    }
    else
    {
        throw std::runtime_error("Unsupported image format");
    }
}