使用PNG++实现Floyd-Steinberg抖动无效,仅转为灰度图
问题原因
你的代码无法生成抖动效果的核心问题是:使用png::gray_pixel(整数类型)存储中间像素值。Floyd-Steinberg抖动需要传递小数形式的误差,但gray_pixel会自动截断小数部分,导致误差无法正确累积传递,最终只是把图像做了简单的取整灰度化,没有产生抖动的扩散效果。
修复方案
需要用浮点类型的数组临时存储像素值,确保误差的小数部分能被正确传递,处理完成后再转换回png::gray_pixel类型写入图片。
修改后的代码如下:
#include <iostream> #include "lib/png.hpp" #include <cmath> #include <vector> #include <algorithm> // 用于std::clamp int main() { // 读取原始图像 png::image<png::gray_pixel> image("input.png"); int h = image.get_height(); int w = image.get_width(); // 创建浮点型二维数组存储像素值,保留小数精度 std::vector<std::vector<double>> pixel_data(h, std::vector<double>(w)); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { pixel_data[y][x] = static_cast<double>(image[y][x]); } } // 执行Floyd-Steinberg抖动算法 for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { double old_val = pixel_data[y][x]; // 取整为0-255的灰度值 int new_val = static_cast<int>(std::round(old_val)); // 确保值在合法范围内,避免溢出 new_val = std::clamp(new_val, 0, 255); pixel_data[y][x] = new_val; double error = old_val - new_val; // 传递误差,使用浮点数组保证精度不丢失 if (x + 1 < w) { pixel_data[y][x+1] += error * 7.0 / 16.0; } if (y + 1 < h && x - 1 >= 0) { pixel_data[y+1][x-1] += error * 3.0 / 16.0; } if (y + 1 < h) { pixel_data[y+1][x] += error * 5.0 / 16.0; } if (x + 1 < w && y + 1 < h) { pixel_data[y+1][x+1] += error * 1.0 / 16.0; } } } // 将浮点数据转换回gray_pixel并写入图像 for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { int val = static_cast<int>(std::round(pixel_data[y][x])); val = std::clamp(val, 0, 255); image[y][x] = static_cast<png::gray_pixel>(val); } } image.write("output.png"); return 0; }
关键改动说明
- 新增
std::vector<std::vector<double>> pixel_data存储浮点型像素值,彻底避免误差被整数类型截断。 - 误差传递操作直接针对浮点数组,保证小数精度完整保留,实现抖动的扩散效果。
- 使用
std::clamp确保像素值始终在0-255的合法范围内,防止溢出导致的图像异常。 - 将图像变量移至
main函数内,避免全局变量初始化可能带来的潜在问题。
内容的提问来源于stack exchange,提问作者zalaxgodd
相关产品推荐
相关产品推荐

