Floyd-Steinberg抖动算法简直量身为价签这种低颜色呈现设备准备的。由于价签的墨水屏能够呈现的色彩非常有限,根据型号不同,有的只能显示黑白,有的只能显示黑白红,有的只能显示黑白黄,所以对于一张普通图片,需要将其转换为两色或者三色才能比较好地在价签上展示出来。
上篇是C++,这篇改成C的
在这里插入代码片
#include <iostream>
#include <opencv.hpp>
#include <opencv\highgui.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
using namespace cv;
using namespace std;
#define M_COUNT 255
typedef struct RGBNODE
{
byte m_b;
byte m_g;
byte m_r;
}RGBNODE;
RGBNODE palette[8] = {
{0,0,0},
{0,0,255},
{0,255,0},
{0,255,255},
{255,0,0},
{255,0,255} ,
{255,255,0},
{255,255,255}
};
typedef struct NODE
{
uchar m_b;
uchar m_g;
uchar m_r;
uchar m_a;
}NODE;
byte plus_truncate_uchar(byte a, int b)
{
if ((a & 0xff) + b < 0)
{
return 0;
}
else if ((a & 0xff) + b > 255)
{
return (byte)255;
}
else
{
return (byte)(a + b);
}
}
byte findNearestColor(RGBNODE color)
{
int minDistanceSquared = 255 * 255 + 255 * 255 + 255 * 255 + 1;
byte bestIndex = 0;
for (byte i = 0; i < sizeof(palette) / sizeof(RGBNODE); i++)
{
int Rdiff = (color.m_r & 0xff) - (palette[i].m_r & 0xff);
int Gdiff = (color.m_g & 0xff) - (palette[i].m_g & 0xff);
int Bdiff = (color.m_b & 0xff) - (palette[i].m_b & 0xff);
int distanceSquared = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
if (distanceSquared < minDistanceSquared)
{
minDistanceSquared = distanceSquared;
bestIndex = i;
}
}
return bestIndex;
}
void floydSteinbergDither_D(uchar* p_data, uchar* p_outdata, int p_height, int p_width)
{
NODE* p = (NODE*)p_data;
NODE* p_out = (NODE*)p_outdata;
int mul1 = 7;
int mul2 = 5;
int mul3 = 3;
int mul4 = 1;
for (int i = 0; i < p_height; i++)
{
NODE* lp = p + i * p_width;
NODE* lp_out = p_out + i * p_width;
for (int j = 0; j < p_width; j++)
{
RGBNODE currentPixel;
currentPixel.m_r = lp[j].m_r;
currentPixel.m_g = lp[j].m_g;
currentPixel.m_b = lp[j].m_b;
byte index = findNearestColor(currentPixel);
lp_out[j].m_b = palette[index].m_b;
lp_out[j].m_g = palette[index].m_g;
lp_out[j].m_r = palette[index].m_r;
int error_b = (currentPixel.m_b & 0xff) - (palette[index].m_b & 0xff);
int error_g = (currentPixel.m_g & 0xff) - (palette[index].m_g & 0xff);
int error_r = (currentPixel.m_r & 0xff) - (palette[index].m_r & 0xff);
if (j < p_width - 1)
{
lp[j + 1].m_b = plus_truncate_uchar(lp[j + 1].m_b, (error_b * mul1) / 16);
lp[j + 1].m_g = plus_truncate_uchar(lp[j + 1].m_g, (error_g * mul1) / 16);
lp[j + 1].m_r = plus_truncate_uchar(lp[j + 1].m_r, (error_r * mul1) / 16);
}
/* else
{
int m = 10;
}*/
if (i< p_height-1)
{
NODE* lpl = p + (i + 1) * (p_width);
lpl[j - 1].m_b = plus_truncate_uchar(lpl[j - 1].m_b, (error_b * mul2) / 16);
lpl[j - 1].m_g = plus_truncate_uchar(lpl[j - 1].m_g, (error_g * mul2) / 16);
lpl[j - 1].m_r = plus_truncate_uchar(lpl[j - 1].m_r, (error_r * mul2) / 16);
lpl[j].m_b = plus_truncate_uchar(lpl[j].m_b, (error_b * mul3) / 16);
lpl[j].m_g = plus_truncate_uchar(lpl[j].m_g, (error_g * mul3) / 16);
lpl[j].m_r = plus_truncate_uchar(lpl[j].m_r, (error_r * mul3) / 16);
if (j < p_width - 1)
{
lpl[j + 1].m_b = plus_truncate_uchar(lpl[j + 1].m_b, (error_b * mul4) / 16);
lpl[j + 1].m_g = plus_truncate_uchar(lpl[j + 1].m_g, (error_g * mul4) / 16);
lpl[j + 1].m_r = plus_truncate_uchar(lpl[j + 1].m_r, (error_r * mul4) / 16);
}
/*else
{
int m = 100;
}*/
}
/*else
{
int m = 100;
}*/
}
}
}
int main()
{
Mat img = imread("E:\\test.png");
if (img.empty())
{
cout << "读取原图错误!" << endl;
return -1;
}
Mat result = img.clone();
cvtColor(img, result, CV_BGR2BGRA);
namedWindow("原图", WINDOW_NORMAL);
cvResizeWindow("原图", img.rows / 3, img.cols / 3);
imshow("原图", img);
int width = img.cols;//图片宽度
int height = img.rows;//图片高度
Mat des = result.clone();
floydSteinbergDither_D(result.data, des.data, result.rows, result.cols);
namedWindow("out", WINDOW_NORMAL);
cvResizeWindow("out", des.rows / 3, des.cols / 3);
imshow("out", des);
imwrite("E:\\out.png", des);
waitKey();
return 0;
}