首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何修复抖动方法以创建颜色梯度?

如何修复抖动方法以创建颜色梯度?
EN

Stack Overflow用户
提问于 2019-01-28 20:55:00
回答 1查看 1.3K关注 0票数 1

有一个程序,为从白色到黑色的颜色梯度建立一个矩阵。然后,将抖动算法应用到矩阵中,以消除“条纹”。实现了四种抖动方法:有序法、随机法、弗洛伊德-斯坦伯格法、贾维斯-朱迪斯-宁克法。首先,创建一定大小的矩阵,将其转换为渐变,并将结果输出到文件格式.pgm中,输入P5。如果将该文件转换为.png,将得到以下图像:

然而,当你放大图像时,你可以看到条纹(如果你仔细看的话):

这是计划的结果,没有动摇。问题是,如果将其中一种抖动算法应用于矩阵,那么图像上就会保留条纹。结果表明,抖动不起作用。有什么不对的?我是否需要先使用抖动,然后建立一个梯度?或者错误是您需要创建一个浮点数或双类型的矩阵?我怎么才能修好它?

代码:

代码语言:javascript
复制
#include "stdafx.h"
#include <iostream>
#include<algorithm>
#include<iterator>
#include<fstream>
#include<vector>
#include<cassert>
#include <ctime>
#include <sstream>

using namespace std;

vector<vector<int>> make_gradient(int height, int width)
{
    assert(height > 0 && width > 0);

    int cf = height / 255;
    int color = 0;
    vector<vector<int>> result(height, vector<int>(width));
    for (int i = 0; i < height; i += cf)
    {
        for (int j = 0; j < cf; ++j)
        {
            fill(result[i + j].begin(), result[i + j].end(), color % 255);
        }
        color++;
    }
    stable_sort(result.begin(), result.end());
    return result;
}

vector<vector<int>> ordered_dither(int height, int width, vector<vector<int>> result)
{
    int ditherSize = 4;
    int diterLookup[] = { 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5 };

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int xlocal = i%ditherSize;
            int ylocal = j%ditherSize;
            int requiredShade = diterLookup[xlocal + ylocal * 4] * 255 / 16;
            if ((requiredShade > 0) && (requiredShade < 1))
            {
                if (requiredShade >= (result[i][j] % 1)) {
                    result[i][j] = floor(result[i][j]);
                }
                else {
                    result[i][j] = ceil(result[i][j]);
                }
            }
            else requiredShade = 0;
        }
    }
    return result;
}

vector<vector<int>> random_dither(int height, int width, vector<vector<int>> result)
{
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int requiredShade = (float)rand() / RAND_MAX;
            if ((requiredShade > 0) && (requiredShade < 1))
            {
                if (requiredShade >= (result[i][j] % 1)) {
                    result[i][j] = floor(result[i][j]);
                }
                else {
                    result[i][j] = ceil(result[i][j]);
                }
            }
            else requiredShade = 0;
        }
    }
    return result;
}

vector<vector<int>> fs_dither(int height, int width, vector<vector<int>> result)
{
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int oldpixel = result[i][j];
            int newpixel = round(result[i][j]);
            result[i][j] = newpixel;
            int quanterror = oldpixel - newpixel;
            if (j < width - 1) {
                result[i][j + 1] += quanterror * 7 / 16;
            }
            if (i < height - 1) {
                if (j > 0) {
                    result[i + 1][j - 1] += quanterror * 3 / 16;
                }
                result[i + 1][j] += quanterror * 5 / 16;
                if (j < width - 1) {
                    result[i + 1][j + 1] += quanterror * 1 / 16;
                }
            }
        }
    }
    return result;
}

vector<vector<int>> jjn_dither(int height, int width, vector<vector<int>> result)
{
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int oldpixel = result[i][j];
            int newpixel = round(result[i][j]);;
            result[i][j] = newpixel;
            int quanterror = oldpixel - newpixel;
            if (j < width - 1) {
                result[i][j + 1] += quanterror * 7 / 48;
                if (j<width - 2)
                    result[i][j + 2] += quanterror * 5 / 48;
            }

            if (i < height - 1) {
                if (j > 0) {
                    if (j > 1)
                        result[i + 1][j - 2] += quanterror * 3 / 48;
                    result[i + 1][j - 1] += quanterror * 5 / 48;
                }

                result[i + 1][j] += quanterror * 7 / 48;
                if (j < width - 1) {
                    result[i + 1][j + 1] += quanterror * 5 / 48;
                    if (j < width - 2)
                        result[i + 1][j + 2] += quanterror * 3 / 48;
                }
            }

            if (i < height - 2) {
                if (j > 0) {
                    if (j>1)
                        result[i + 2][j - 2] += quanterror * 1 / 48;
                    result[i + 2][j - 1] += quanterror * 3 / 48;
                }
                result[i + 2][j] += quanterror * 5 / 48;
                if (j < width - 1) {
                    result[i + 2][j + 1] += quanterror * 3 / 48;
                    if (j < width - 2)
                        result[i + 2][j + 2] += quanterror * 1 / 48;
                }
            }

        }
    }
    return result;
}

int main(int argc, char *argv[])
{
    if (argc < 5) {
        cout << "usage:" << endl << "prog.exe <filename> <width> <height> <dithering>" << endl;
        return 0;
    }
    stringstream w(argv[2]);
    stringstream h(argv[3]);
    stringstream d(argv[4]);
    int numcols, numrows, dithering;

    if (!(w >> numcols)) {
        cout << "width is not a number" << endl;
        return 0;
    }
    if (numcols < 1) {
        cout << "width must be more than zero" << endl;
        return 0;
    }

    if (!(h >> numrows)) {
        cout << "height is not a number" << endl;
        return 0;
    }
    if (numrows < 1) {
        cout << "height must be more than zero" << endl;
        return 0;
    }

    if (!(d >> dithering)) {
        cout << "dithering is not a number" << endl;
        return 0;
    }
    if (dithering < 0 || dithering>4) {
        cout << "dithering must be [0-4]" << endl;
        return 0;
    }

    srand(time(0));
    ofstream file;

    file.open(argv[1]);

    if (!file)
    {
        cout << "can't open file" << endl;
        return 0;
    }

    file << "P5" << "\n";

    file << numrows << " " << numcols << "\n";

    file << 255 << "\n";

    vector<vector<int>> pixmap{ make_gradient(numrows, numcols) };
    switch (dithering) {
    case 1:
        pixmap = ordered_dither(numrows, numcols, pixmap);
        break;
    case 2:
        pixmap = random_dither(numrows, numcols, pixmap);
        break;
    case 3:
        pixmap = fs_dither(numrows, numcols, pixmap);
        break;
    case 4:
        pixmap = jjn_dither(numrows, numcols, pixmap);
        break;
    default:
        break;
    }
    for_each(pixmap.begin(), pixmap.end(), [&](const auto& v) {
        copy(v.begin(), v.end(), ostream_iterator<char>{file, ""});
    });

    file.close();

}
EN

回答 1

Stack Overflow用户

发布于 2019-01-29 02:35:52

很有趣的是,看到你用抖动来摆脱那些你几乎看不见的乐队--在过去的日子里,我们只会在每个频道以4位左右的速度渲染时才会颤抖。

不管怎样..。您的第一个问题是,在使用抖动将梯度降到256级之前,您必须在超过256个级别上呈现它。make_gradient可能会将梯度呈现在65536级,甚至是浮点。

你的第二个问题是,在我看来,你的犹豫不决目前什么也没做。result[i][j]是一个整数,所以当您说类似于result[i][j] = floor(result[i][j]); (我猜您忽略了有关转换的编译器警告)时,它什么也不会做。如果你在浮子中生成梯度,那么这个问题也会消失。

如果您解决了这些问题,那么您的抖动可以工作,但是这些抖动方法中没有一个真正适合于在如此间隔的水平上操作。当您完成任务时,可能仍然会有一些带状的工件残留(尽管您必须仔细观察才能看到它们)。为了使你的结果看起来尽可能好,你真的应该使用一个振幅等于两个量化级别的TPDF抖动。对于粗糙的空间级别,这看起来比其他一些选择更嘈杂,但在统计上更均匀,并且当级别间隔很细时看起来会更好。

这也很容易--在对每个像素加两个随机数-0.5到0.5之间,然后再量化为整数。

这里提到了TPDF:https://en.wikipedia.org/wiki/Dither,但最重要的是它是用于信号处理的采样中使用的抖动类型,以确保量化不会导致任何一阶或二阶伪影。

编辑:

我感谢您一直致力于这方面的工作,下面是在最后一步中创建抖动梯度的代码:

代码语言:javascript
复制
vector<vector<int>> make_dithered_gradient(int height, int width)
{
    assert(height > 0 && width > 0);

    vector<vector<int>> result(height, vector<int>(width));
    for (int i = 0; i < height; ++i)
    {
        // the target shade for each line of pixels is the average
        // ideal gradient value in that line, which is the same as the
        // ideal value in the middle of the line
        double target = ((double)i+0.5)*255.0/height;
        for (int j = 0; j < width; ++j)
        {
            double dither = ((double)rand()-(double)rand())/RAND_MAX;
            int val = (int)round(target+dither);
            if (val < 0)
                val = 0;
            if (val > 255)
                val = 255;
            result[i][j] = val;
        }
    }
    return result;
}

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54410165

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档