热敏打印机是一种在打印头上安装有半导体加热元件,打印头加热并接触热敏打印纸后就可以打印出需要的图案的便携式打印机,其原理与热敏式传真机类似,图像是通过加热,在膜中产生化学反应而生成的,常温下可保存长达几年之久。热敏打印具有速度快、噪音低,使用方便的优点,但是热敏打印机打印的是黑白图片,且打印的像素宽度有限,在打印一些内容复杂的图像时效果并不理想,需要对图像进行前期的算法处理。
Floyd-Steinberg Dithering
Floyd-Steinberg Dithering 是一种抖动的误差扩散法,它将一个像素点的量化误差传递到相邻的像素。如下矩阵,用星号(*)表示的像素表示当前正在扫描的像素,并且空白像素是先前扫描的像素。该算法从左到右,从上到下扫描图像,逐个量化像素值。每次量化误差被传送到相邻像素,同时不影响已经量化的像素。因此,如果向下舍入多个像素,则下一个像素向上舍入的可能性更大,使得平均来说量化误差接近于零。
最简单的理解就是将上述矩阵理解成一个算子,用这个算子对图像进行滤波。
代码实现
我们首先使用matlab对算法进行了验证,之后根据编写的matlab算法将其编写为C语言代码,以便于部署到嵌入式设备上。
matlab
function I=my_dither(I,height,width)
for i=1:height-1
for j=1:width-1
oldPixel = I(i,j);
if oldPixel > 128
newPixel = 255;
else
newPixel = 0;
end
if newPixel == 0
quantError = oldPixel;
I(i,j) = newPixel;
I(i,j+1) = I(i,j+1) + quantError* 7/16;
if j-1 >0
I(i+1,j-1) = I(i+1,j-1) + quantError* 3/16;
end
I(i+1,j) = I(i+1,j) + quantError* 5/16;
I(i+1,j+1) = I(i+1,j+1) + quantError* 1/16;
else
quantError = newPixel - oldPixel;
I(i,j) = newPixel;
I(i,j+1) = I(i,j+1) - quantError* 7/16;
if j-1 >0
I(i+1,j-1) = I(i+1,j-1) - quantError* 3/16;
end
I(i+1,j) = I(i+1,j) - quantError* 5/16;
I(i+1,j+1) = I(i+1,j+1) - quantError* 1/16;
end
end
end
c语言
在进行C语言实现的时候,使用定点数进行存储和计算比用浮点数要快得多,但在使用时要注意数据类型能表示的数值范围,避免溢出从而导致的未知错误。
void dither2(uint8_t *image) {
uint16_t i=0,j=0;
uint8_t oldPixel,newPixel,errPixel;
uint8_t add_flag = 0;
for(i=0;i<HEIGHT-1;i++)
{
for(j=0;j<WIDTH-1;j++)
{
oldPixel = image[i * WIDTH + j];
if (oldPixel > 128){
newPixel = 255;
errPixel=255 - oldPixel;
add_flag = 0;
}
else{
newPixel = 0;
errPixel=oldPixel;
add_flag = 1;
}
image[i * WIDTH + j] = newPixel;
if(add_flag){
if( image[i * WIDTH + j+1] > 255 - errPixel* 7/16){
image[i * WIDTH + j+1] = 255;
}else{
image[i * WIDTH + j+1] = image[i * WIDTH + j+1] + errPixel* 7/16;
}
if(image[(i+1) * WIDTH + j] > 255 - errPixel* 5/16){
image[(i+1) * WIDTH + j] = 255;
}else{
image[(i+1) * WIDTH + j] = image[(i+1) * WIDTH + j] + errPixel* 5/16;
}
if(image[(i+1) * WIDTH + j+1] > 255 - errPixel* 1/16){
image[(i+1) * WIDTH + j+1] = 255;
}else{
image[(i+1) * WIDTH + j+1] = image[(i+1) * WIDTH + j+1] + errPixel* 1/16;
}
if(j>0){
if(image[(i+1) * WIDTH + j-1] > 255 - errPixel* 3/16){
image[(i+1) * WIDTH + j-1] = 255;
}else{
image[(i+1) * WIDTH + j-1] = image[(i+1) * WIDTH + j-1] + errPixel* 3/16;
}
}
}else{
if( image[i * WIDTH + j+1] < errPixel* 7/16){
image[i * WIDTH + j+1] = 0;
}else{
image[i * WIDTH + j+1] = image[i * WIDTH + j+1] - errPixel* 7/16;
}
if(image[(i+1) * WIDTH + j] < errPixel* 5/16){
image[(i+1) * WIDTH + j] = 0;
}else{
image[(i+1) * WIDTH + j] = image[(i+1) * WIDTH + j] - errPixel* 5/16;
}
if(image[(i+1) * WIDTH + j+1] < errPixel* 1/16){
image[(i+1) * WIDTH + j+1] = 0;
}else{
image[(i+1) * WIDTH + j+1] = image[(i+1) * WIDTH + j+1] - errPixel* 1/16;
}
if(j>0){
if(image[(i+1) * WIDTH + j-1] < errPixel* 3/16){
image[(i+1) * WIDTH + j-1] = 0;
}else{
image[(i+1) * WIDTH + j-1] = image[(i+1) * WIDTH + j-1] - errPixel* 3/16;
}
}
}
}
}
}
这里使用了add_flag标志位来判断是进行减操作还是加操作。因为我们这里使用的数据类型为uint8_t,即unsigned char类型,它能够表示的范围为0~255。在进行减操作的时候,我们需要保证减完得到的值不是负数,如果是负数,就将其置零。而在进行加操作的时候,需要判断得到的结果是否会大于255,如果大于255,就将其置为255。
实验结果
matlab
原图
处理后得到的图像
C
处理后得到的图像
#用热敏打印头的打印效果
Bilibili:通信电子小白
微信公众号:
下面是我创建的QQ群,欢迎大家进群交流呀!
QQ群:752236617