转自:http://blog.csdn.net/panda1234lee/article/details/52245353
OpenCV的效果图:
用OpenCV实现起来也是非常简单的!
intmain(intargc,char** argv )
{
Mat rawImg;
Mat dithImg;
intimgWidth;
intimgHeight;
uint8_t** imgPtr;
if(argc != 2)
{
printf("> Usage: ./dithering [path_to_image]\n");
return-1;
}
rawImg = imread(argv[1]);
cvtColor(rawImg, dithImg, CV_BGR2GRAY);
/* 获得图像尺寸 */
imgWidth = dithImg.cols;
imgHeight = dithImg.rows;
dbg("> Image width: %d\r\n",imgWidth);
dbg("> Image height: %d\r\n",imgHeight);
/* Floyd-Steinberg 抖动算法 */
interr;
int8_t a,b,c,d;
for(inti=0; i
{
for(intj=0; j
{
if(dithImg.at(i,j) > 127)// 127 = (255+0)/2
{
err = dithImg.at(i,j) - 255;
dithImg.at(i,j) = 255;// 新值为白色
}
else
{
err = dithImg.at(i,j) - 0;
dithImg.at(i,j) = 0;// 新值为黑色
}
// 误差传递
a = (err * 7) / 16;
b = (err * 1) / 16;
c = (err * 5) / 16;
d = (err * 3) / 16;
if((i != (imgHeight-1)) && (j != 0) && (j != (imgWidth - 1)))
{
dithImg.at(i+0,j+1) = saturated_add(dithImg.at(i+0,j+1),a);
dithImg.at(i+1,j+1) = saturated_add(dithImg.at(i+1,j+1),b);
dithImg.at(i+1,j+0) = saturated_add(dithImg.at(i+1,j+0),c);
dithImg.at(i+1,j-1) = saturated_add(dithImg.at(i+1,j-1),d);
}
}
}
/* Show results. */
imshow("Raw Image",rawImg);
imshow("Dithered Image",dithImg);
printf("> Press any key to exit ...\n");
waitKey(0);
return0;
}
但是这么简单的算法用GLSL来写的话,就没有这么简单了!因为GLSL是并行的,决定当前像素的 error 不仅仅来自于vec2(-1., -1.), vec2(0., -1.), vec2(-1., 0.), vec2(1., -1.)这四个 offset 方向上的像素,这四个像素还受其本身四个方向 offset 的像素影响,所以会陷入“递归”的问题,而GLSL又不支持递归。那么只能换一种思路来近似抖动效果了。
GLSL代码:
[cpp]view plaincopy
#ifdef GL_ES
precision mediumpfloat;
precision mediumpint;
#endif
uniform sampler2D texture;
vec2 sketchSize = vec2(512., 512.);
constintlookupSize = 64;// 查找表的大小
constfloaterrorCarry = 0.5;// 误差传输率
floatgetGrayscale(vec2 coords)
{
vec2 uv = coords / sketchSize.xy;
// processing is already using inverted y coordinates
// uv.y = 1.0-uv.y;
vec3 sourcePixel = texture2D(texture, uv).rgb;
returnlength(sourcePixel * vec3(0.2126,0.7152,0.0722));
}
voidmain()
{
floatxError = 0.0;
for(intxLook=0; xLook
{
floatgrayscale = getGrayscale(gl_FragCoord.xy + vec2(-lookupSize+xLook,0));//
grayscale += xError;// 在X方向上的误差传播(从最左端到当前像素)
floatbit = (grayscale >= 0.5) ? 1.0 : 0.0;// t = (w+b)/2.
xError = (grayscale - bit)*errorCarry;
}
floatyError = 0.0;
for(intyLook=0; yLook
{
floatgrayscale = getGrayscale(gl_FragCoord.xy + vec2(0,-lookupSize+yLook));
grayscale += yError;// 在Y方向上的误差传播 (从最上端到当前像素)
floatbit = (grayscale >= 0.5) ? 1.0 : 0.0;// t = (w+b)/2.
yError = (grayscale - bit)*errorCarry;
}
floatfinalGrayscale = getGrayscale(gl_FragCoord.xy);
finalGrayscale += xError * .5;// X,Y方向的误差各占一半
finalGrayscale += yError * .5;
finalGrayscale = clamp(finalGrayscale, 0., 1.);
floatfinalBit = finalGrayscale >= 0.5 ? 1.0 : 0.0;
gl_FragColor = vec4(finalBit, finalBit, finalBit, 1.);
}
voidmain(void) {
vec2 uv = gl_FragCoord.xy / sketchSize.xy;
uv.y = uv.y;
vec3 sourcePixel = texture2D(texture, uv).rgb;
floatgrayscale = length(sourcePixel*vec3(0.2126,0.7152,0.0722));
vec3 ditherPixel = texture2D(noiseTexture, vec2(fract(gl_FragCoord.xy/noiseSketchSize))).xyz;
floatditherGrayscale = (ditherPixel.x + ditherPixel.y + ditherPixel.z) / 3.0;
ditherGrayscale -= 0.6;
floatditheredResult = grayscale + ditherGrayscale;
floatbit = ditheredResult >= 0.5 ? 1.0 : 0.0;
gl_FragColor = vec4(bit,bit,bit,1);
}
效果如图
显然效果会更接近于正统的Floyd Steinberg,而且计算量也较小,噪声纹理。