Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C/ C ++ version of the parameter configuration problem? #119

Open
patui opened this issue Jan 9, 2025 · 2 comments
Open

C/ C ++ version of the parameter configuration problem? #119

patui opened this issue Jan 9, 2025 · 2 comments

Comments

@patui
Copy link

patui commented Jan 9, 2025

when using libimagequant to compress images, why does the Rust version produce smoother compression quality without artifacts or banding

c/c++:

bool PngQuantCompressor::compress(const std::string& inputPath, const std::string& outputPath,
                                const CompressConfig& config)
{
    const auto& options = dynamic_cast<const PngQuantConfig&>(config);

    if (inputPath.empty() || outputPath.empty()) {
        return false;
    }

    std::vector<unsigned char> raw_rgba_pixels;
    int width, height;
    if (!m_pngHandler.loadPNG(inputPath, raw_rgba_pixels, width, height)) {
        return false;
    }

    std::unique_ptr<liq_attr, decltype(&liq_attr_destroy)> handle(
        liq_attr_create(), liq_attr_destroy);
    if (!handle) {
        return false;
    }

    liq_set_last_index_transparent(handle.get(), -1);

    if (liq_set_quality(handle.get(), 0, 90) != LIQ_OK) {
        return false;
    }

    if (liq_set_speed(handle.get(), 1) != LIQ_OK) {
        return false;
    }

    std::unique_ptr<liq_image, decltype(&liq_image_destroy)> input_image(
        liq_image_create_rgba(handle.get(), raw_rgba_pixels.data(), width, height, 0.0),
        liq_image_destroy);
    if (!input_image) {
        return false;
    }


    if (liq_set_min_posterization(handle.get(), 0) != LIQ_OK) {
        return false;
    }

    liq_result* quantization_result = nullptr;
    if (liq_image_quantize(input_image.get(), handle.get(), &quantization_result) != LIQ_OK) {
        return false;
    }
    std::unique_ptr<liq_result, decltype(&liq_result_destroy)> result(
        quantization_result, liq_result_destroy);

  
    if (liq_set_dithering_level(result.get(), 1.0) != LIQ_OK) {
        return false;
    }

    std::vector<unsigned char> raw_8bit_pixels(width * height);
    if (liq_write_remapped_image(result.get(), input_image.get(),
                                raw_8bit_pixels.data(), raw_8bit_pixels.size()) != LIQ_OK) {
        return false;
    }

    const liq_palette* liq_pal = liq_get_palette(result.get());
    std::vector<PNGHandler::PaletteEntry> palette(liq_pal->count);
    for (int i = 0; i < liq_pal->count; ++i) {
        palette[i].r = liq_pal->entries[i].r;
        palette[i].g = liq_pal->entries[i].g;
        palette[i].b = liq_pal->entries[i].b;
        palette[i].a = liq_pal->entries[i].a;
    }

    return m_pngHandler.savePNG(outputPath, raw_8bit_pixels, width, height, palette);
}

m_pngHandler use libpng;

Rust

fn lossy(in_file: Vec<u8>, parameters: &CSParameters) -> Result<Vec<u8>, CaesiumError> {
    let rgba_bitmap = lodepng::decode32(in_file).map_err(|e| CaesiumError {
        message: e.to_string(),
        code: 20204,
    })?;

    let mut liq = imagequant::new();
    liq.set_quality(0, 90)
        .map_err(|e| CaesiumError {
            message: e.to_string(),
            code: 20205,
        })?;

    let mut liq_image = liq
        .new_image(
            rgba_bitmap
.buffer.as_slice(),
            rgba_bitmap.width,
            rgba_bitmap.height,
            0.0,
        )
        .map_err(|e| CaesiumError {
            message: e.to_string(),
            code: 20206,
        })?;

    let mut quantization = liq.quantize(&mut liq_image).map_err(|e| CaesiumError {
        message: e.to_string(),
        code: 20207,
    })?;

    let (palette, pixels) = quantization
        .remapped(&mut liq_image)
        .map_err(|e| CaesiumError {
            message: e.to_string(),
            code: 20208,
        })?;

    let mut encoder = lodepng::Encoder::new();
    encoder
        .set_palette(palette.as_slice())
        .map_err(|e| CaesiumError {
            message: e.to_string(),
            code: 20212,
        })?;
    let png_vec = encoder
        .encode(pixels.as_slice(), rgba_bitmap.width, rgba_bitmap.height)
        .map_err(|e| CaesiumError {
            message: e.to_string(),
            code: 20209,
        })?;

    Ok(png_vec)
}

TEST_IMAGE.zip

What's the difference? Which parameter is wrong?

@patui
Copy link
Author

patui commented Jan 9, 2025

impl Attributes {
/// New handle for library configuration
///
/// See also [Attributes::new_image()]
#[inline]
#[must_use]
pub fn new() -> Self {
let mut attr = Self {
target_mse: 0.,
max_mse: None,
max_colors: MAX_COLORS as PalLen,
last_index_transparent: false,
kmeans_iteration_limit: 0.,
max_histogram_entries: 0,
min_posterization_output: 0,
min_posterization_input: 0,
kmeans_iterations: 0,
feedback_loop_trials: 0,
use_contrast_maps: false,
use_dither_map: DitherMapMode::None,
single_threaded_dithering: false,
speed: 0,
progress_stage1: 0,
progress_stage2: 0,
progress_stage3: 0,
progress_callback: None,
log_callback: None,
log_flush_callback: None,
};
let _ = attr.set_speed(4);
attr
}

let _ = attr.set_speed(4);,

 if (liq_set_speed(handle.get(), 4) != LIQ_OK) {
    return false;
}

 Whether the default value of speed is different, I just changed the c++ default value to 4, it is OK.

@kornelski
Copy link
Member

The C++ wrapper hasn't been developed by me, so I don't know what it's doing.
Setting the speed to a lower value will improve images.
Also make sure to set max quality to 100 to allow best gradients. Lower quality limits may posterize.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants