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

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

这是计划的结果,没有动摇。问题是,如果将其中一种抖动算法应用于矩阵,那么图像上就会保留条纹。结果表明,抖动不起作用。有什么不对的?我是否需要先使用抖动,然后建立一个梯度?或者错误是您需要创建一个浮点数或双类型的矩阵?我怎么才能修好它?
代码:
#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();
}发布于 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,但最重要的是它是用于信号处理的采样中使用的抖动类型,以确保量化不会导致任何一阶或二阶伪影。
编辑:
我感谢您一直致力于这方面的工作,下面是在最后一步中创建抖动梯度的代码:
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;
}

https://stackoverflow.com/questions/54410165
复制相似问题