Dither 算法来源
最早源自二战,当时的轰炸机飞行员使用了一个机械计算机来进行导航和 bomb 轨道计算。奇怪的是,这些计算机(由上百机械零件组成)在飞机甲板上要比在地面上工作地更为准确。工程师们意识到飞机的震动降低了它的一些活动部件所导致的错误。为了让它们更好的工作,工程师为这种计算机安装了震动马达,他们把这些马达的震动成为 dither 。
Dither 算法应用
音频领域
Dither是数字音乐处理上非常神奇的技巧,目的是通过用少数的 Bit 达到与较多 Bit 同样的听觉效果,方法是在最后一个 Bit (LSB)上动“手脚”。例如用 16Bit 记录听起来好似 20Bit 的信息,听到原先 16Bit 无法记录的微小信息。举例来说,现在我有个 20Bit 的采样信息,现在想将其存为 16Bit 的信息格式,最简单的转换方式就是直接把后面 4 个 Bit 去掉,但是这样就失去用 20Bit 录音/混音的意义。比较技巧性的方法是在第 17~20Bit 中加入一些噪音,这段噪音就叫做 Dither。这些噪音加入后,可能会进位而改变第16个Bit的信息,然后我们再把最后4个Bit删掉,这个过程我们称为redithering,用意是让后面4个Bit的数据线性地反映在第16个Bit上。由于人耳具有轻易将噪音与乐音分离的能力,所以虽然我们加入了噪音,实际上我们却听到了更多音乐的细节。
视频图像领域
Floyd-Steinberg扩散抖动算法,用在图像处理中该算法利用误差扩散实现抖动,从左到右、由上至下扫描图像的像素并将其逐个标准化(或二值化),把像素标准化后产生的误差叠加到相邻像素上,不影响已经处理过的像素。这样实现的效果是,如果某些像素向下取整,则下一个像素向上取整的可能性更大,这样使得平均量化误差最小。
后台回复[Floyd-Steinberg]可领取该算法PDF资料。
基于图像处理的抖动规则
假定在2×2 像素块中每一个像素对应一个8bit 的数据,但输出设备只能使用高6位,因此如果没有抖动过程的支持,低两位将会被丢失。考虑任意的8bit 像素值A8h(1010_1000),其高6 位用16 进制数“2A”表示,如果不用抖动,像素值A9h(1010_1001)、AAh(1010_1010)、ABh(1010_1011)将显示和A8h 同样的像素值“2Ah”。而像素值ACh(1010_1100)有不同的高六位,所以ACh 比A8h 有更高的亮度。因此如果不加抖动处理,仅能精确显示A8h 和ACh。移除低两位,这些值将分别为“2Ah”或“2Bh”。
抖动处理为“丢失”的像素值A9h、AAh、ABh 提供了显示的方法,通过显示合并的2×2 的像素块的值来加以实现,该像素块内的平均强度就是“丢失”的值,如上图所示。为了给最大强度值留有余地,ABh 不作任何变换,A8h、A9h、AAh 则通过抖动算法进行修改。
低 2 位的抖动只有四种矩阵供选择,如图上图的“情况 1”至“情况4”。抖动矩阵中“0”表示对应位置的输入值不作任何改变,“1”表示对应位置的输入值将减弱到下一个可以显示的值。可将上述四种情况综合为下图 中“2bit 抖动矩阵”的抖动矩阵,其中像素位置的数字表示低 2 位:00 = blank,01 =“1”,10 =“2”,11 =“3”。
如果输入像素值低 2 位为“00”,只有与抖动矩阵中空白处对应的像素强度值不变,其余 3 个都减弱到下一个可显示的像素值;
若输入像素值低 2 位为“01”,与抖动矩阵中空白及标有“1”的位置的像素点值保持不变,其余 1 个都减弱到下一个可显示的像素值;
若输入像素值低 2 位为“10”,与抖动矩阵中空白及标有“1”的位置的像素点值保持不变,其余 2 个都减弱到下一个可显示的像素值;
若输入像素值低 2 位为“11”,四个像素点都保持输入值不变。以上过程是 2bit 抖动的算法,对于 1bit,3bit,4bit 抖动的抖动矩阵见其他,其算法与2bit 抖动算法类似。
dither 算法实现
matlab 3bit-dither程序
clear;
clc;
I = imread(‘0001.jpg’);
img = double(I);%转换图片
[h w] = size(img(:,:,1));%取得图片的大小
d = 1;%为1时,误差从左传递到右侧,为-1时,误差从右传递到左
re = 0;
ge = 0;
be = 0;
rs = 8;%2^n, n = 3 表示将红色量化等级减少到2^5 = 32种。
gs = 8;%2^n, n = 3 表示将绿色量化等级减少到2^5 = 32种。
bs = 8;%2^n, n = 3 表示将蓝色量化等级减少到2^5 = 32种。
for i=1:h
for j=1:w
if (d == 1)
val = rs * fix(img(i,j,1) / rs);
re = img(i, j, 1) - val;
img(i, j, 1) = val;
val = gs * fix(img(i,j,2) / gs);
ge = img(i, j, 2) - val;
img(i, j, 2) = val;
val = bs * fix(img(i,j,3) / bs);
be = img(i, j, 3) - val;
img(i, j, 3) = val;
if ((j + 1) <= w)%计算误差对右侧的像素的传递
img(i, j + 1, 1) = img(i, j + 1, 1) + re * 3 / 8;
img(i, j + 1, 2) = img(i, j + 1, 2) + ge * 3 / 8;
img(i, j + 1, 3) = img(i, j + 1, 3) + be * 3 / 8;
end
if ((i + 1) <= h)%计算误差对下侧的像素传递
img(i + 1, j, 1) = img(i + 1, j, 1) + re * 3 / 8;
img(i + 1, j, 2) = img(i + 1, j, 2) + ge * 3 / 8;
img(i + 1, j, 3) = img(i + 1, j, 3) + be * 3 / 8;
end
if ((i + 1) <= h && (j + 1) <= w)%计算误差对右下侧的像素传递
img(i + 1, j + 1, 1) = img(i + 1, j + 1, 1) + re / 4;
img(i + 1, j + 1, 2) = img(i + 1, j + 1, 2) + ge / 4;
img(i + 1, j + 1, 3) = img(i + 1, j + 1, 3) + be / 4;
end
else
val = rs * fix(img(i,w - j + 1,1) / rs);
re = img(i, w - j + 1, 1) - val;
img(i, w - j + 1, 1) = val;
val = gs * fix(img(i,w - j + 1,2) / gs);
ge = img(i, w - j + 1, 2) - val;
img(i, w - j + 1, 2) = val;
val = bs * fix(img(i,w - j + 1,3) / bs);
be = img(i, w - j + 1, 3) - val;
img(i, w - j + 1, 3) = val;
if ((w - j) > 0)%计算误差对左侧的误差传递
img(i, w - j, 1) = img(i, w - j, 1) + re * 3 / 8;
img(i, w - j, 2) = img(i, w - j, 2) + ge * 3 / 8;
img(i, w - j, 3) = img(i, w - j, 3) + be * 3 / 8;
end
if (i + 1 <= h)%计算误差对下侧的像素误差传递
img(i + 1, j, 1) = img(i + 1, j, 1) + re * 3 / 8;
img(i + 1, j, 2) = img(i + 1, j, 2) + ge * 3 / 8;
img(i + 1, j, 3) = img(i + 1, j, 3) + be * 3 / 8;
end
if ((i + 1) <= h && (w - j) > 0)%计算误差对左下侧的像素误差传递
img(i + 1, w - j, 1) = img(i + 1, w - j, 1) + re / 4;
img(i + 1, w - j, 2) = img(i + 1, w - j, 2) + ge / 4;
img(i + 1, w - j, 3) = img(i + 1, w - j, 3) + be / 4;
end
end
end
d = -d;
end
out = uint8(img);
imshow(out)
FPGA 2bit-dither程序实现
1. 特点
2.关键代码
计算误差:注意奇偶行dither规则不同
2'b00 : threshold[1:0] <= 2'd0;//奇数行(左侧误差)
2'b01 : threshold[1:0] <= 2'd2;//偶数行(左侧误差)
2'b10 : threshold[1:0] <= 2'd3;//奇数行(右侧误差)
2'b11 : threshold[1:0] <= 2'd1;//偶数行(右侧误差)
//用 RGB 三通道中的 R 分量举例
//输出进行dither
if(&i_R[7:2])
i_R[5:0] <= i_R[7:2];
else if(i_R[1:0] > threshold[1:0])
i_R[5:0] <= i_R[7:2] + 1'b1;
else
i_R[5:0] <= i_R[7:2];
3.仿真结果
原图:随便截取了一张图,RGB888 格式,显示结果如下图。
将原图RGB888 的每个分量的最低 2bit 置为 0 ,显示结果如下图。
R={r_data[7:2],2'b00};
G={g_data[7:2],2'b00};
B={b_data[7:2],2'b00};
可以看出很多区域都存在偏色。
将原图 RGB888 输入给 dither 模块,效果如下图所示。
Dither Dither_inst(
.InClk (dither_InClk ) ,
.InVs (dither_InVs ) ,
.InHs (dither_InHs ) ,
.InDe (dither_InDe ) ,
.InData (dither_InData ) ,
.OutClk (dither_OutClk ) ,
.OutVs (dither_OutVs ) ,
.OutHs (dither_OutHs ) ,
.OutDe (dither_OutDe ) ,
.OutData(dither_OutData)
);
可以看出比直接丢掉最低2bit效果好很多,虽然还会存在一些瑕疵。
对比结果
参考链接
DITHER 抖动算法
https://blog.csdn.net/xxhi008/article/details/78077408
图像增强算法之去抖动算法
https://blog.csdn.net/lz0499/article/details/101622016?utm_medium=distribute.pc_relevant.none-task-blog-searchFromBaidu-3.compare&depth_1-utm_source=distribute.pc_relevant.none-task-blog-searchFromBaidu-3.compare
github 参考代码
https://github.com/freecores/video_dithering
源码参考获取