误差扩散法

https://blog.csdn.net/weixin_34406061/article/details/92425842 代码是C#,我把他改成C++

Floyd-Steinberg抖动算法简直量身为价签这种低颜色呈现设备准备的。由于价签的墨水屏能够呈现的色彩非常有限,根据型号不同,有的只能显示黑白,有的只能显示黑白红,有的只能显示黑白黄,所以对于一张普通图片,需要将其转换为两色或者三色才能比较好地在价签上展示出来。

左边图是主函数调用传入参数1 , 右边是传入参数3
在这里插入图片描述


#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
class  RGBTriple
{
public:
	byte* channels;
	int m_size;
	RGBTriple() { channels = new byte[3]; }
	RGBTriple(int R, int G, int B)
	{
		channels = new byte[]{ (byte)R, (byte)G, (byte)B };
	}
};

class BMPConverter
{
public:
	RGBTriple* getPalette(int deviceType)
	{
		RGBTriple* palette = NULL;
		if (deviceType == 0)
		{
			//黑白价签
			palette = new RGBTriple[]{
					RGBTriple(0, 0, 0),
					RGBTriple(255, 255, 255)
			};
			palette->m_size = 2;
		}
		else if (deviceType == 1) {
			//黑白红价签
			palette = new RGBTriple[]{
					 RGBTriple(0, 0, 0),
					 RGBTriple(255, 255, 255),
					 RGBTriple(0, 0,255)
			};
			palette->m_size = 3;
		}
		else if (deviceType == 2) 
		{
			//黑白黄价签
			palette = new RGBTriple[]{
					 RGBTriple(0, 0, 0),
					 RGBTriple(255, 255, 255),
					 RGBTriple(255, 255, 0)
			};
			palette->m_size = 3;
		}
		else if (deviceType == 3)
		{ //RGB
			palette = new RGBTriple[]
			{
					RGBTriple(0 * M_COUNT, 0 * M_COUNT, 0 * M_COUNT),
					RGBTriple(0 * M_COUNT, 0 * M_COUNT, 1 * M_COUNT),
					RGBTriple(0 * M_COUNT, 1 * M_COUNT, 0 * M_COUNT),
					RGBTriple(0 * M_COUNT, 1 * M_COUNT, 1 * M_COUNT),
					RGBTriple(1 * M_COUNT, 0 * M_COUNT, 0 * M_COUNT),
					RGBTriple(1 * M_COUNT, 0 * M_COUNT, 1 * M_COUNT),
					RGBTriple(1 * M_COUNT, 1 * M_COUNT, 0 * M_COUNT),
					RGBTriple(1 * M_COUNT, 1 * M_COUNT, 1 * M_COUNT)			
			};
			palette->m_size = 8;
		}
		return palette;
	};

	void  floydSteinbergDither(Mat  image, RGBTriple palette[])
	{
		//byte result = new byte[image.length][image[0].length];
		Mat result = Mat::zeros(image.rows, image.cols, CV_8UC3);//先生成空的目标图片
	   //result = image;
		uchar* p_img;
		Mat matb, matg, matr, matr_dst, matg_dst, matb_dst;

		uchar* p_img_b;
		uchar* p_img_g;
		uchar* p_img_r;

		uchar* p_img_b_dst;
		uchar* p_img_g_dst;
		uchar* p_img_r_dst;

		uchar* p_lw_b;
		uchar* p_lw_g;
		uchar* p_lw_r;

		//1、先进行通道分解
		Mat planes[3];
		split(image, planes);
		matb = planes[0];
		matg = planes[1];
		matr = planes[2];

		Mat planes_dst[3];
		split(result, planes_dst);
		matb_dst = planes_dst[0];
		matg_dst = planes_dst[1];
		matr_dst = planes_dst[2];


		for (int i = 0; i < image.rows - 2; i++)
		{
			//p_img_b = matb.ptr<uchar>(i);
			//p_img_g = matg.ptr<uchar>(i);
			//p_img_r = matr.ptr<uchar>(i);

			p_img = result.ptr<uchar>(i);

			p_img_b = matb.ptr<uchar>(i);
			p_img_g = matg.ptr<uchar>(i);
			p_img_r = matr.ptr<uchar>(i);

			p_img_b_dst = matb_dst.ptr<uchar>(i);
			p_img_g_dst = matg_dst.ptr<uchar>(i);
			p_img_r_dst = matr_dst.ptr<uchar>(i);

			p_lw_b = matb.ptr<uchar>(i + 1);
			p_lw_g = matg.ptr<uchar>(i + 1);
			p_lw_r = matr.ptr<uchar>(i + 1);

			for (int j = 0; j < image.cols - 2; j++)
			{
				RGBTriple currentPixel;// = (RGBTriple)(matb_dst.ptr<uchar>(x), 1, 1);
				currentPixel.channels[0] = p_img_b[j];
				currentPixel.channels[1] = p_img_g[j];
				currentPixel.channels[2] = p_img_r[j];
				byte index = findNearestColor(currentPixel, palette);
				//p_img[j] = index;
#if 1
				switch (index)
				{
				case 0:
					p_img_b_dst[j] = 0* M_COUNT;
					p_img_g_dst[j] = 0*M_COUNT;
					p_img_r_dst[j] = 0* M_COUNT;
					break;
				case 1:
					p_img_b_dst[j] = 0* M_COUNT;
					p_img_g_dst[j] = 0* M_COUNT;
					p_img_r_dst[j] = 1* M_COUNT;
					break;
				case 2:
					p_img_b_dst[j] = 0* M_COUNT;
					p_img_g_dst[j] = 1* M_COUNT;
					p_img_r_dst[j] = 0* M_COUNT;
					break;
				case 3:
					p_img_b_dst[j] = 0* M_COUNT;
					p_img_g_dst[j] = 1* M_COUNT;
					p_img_r_dst[j] = 1* M_COUNT;
					break;
				case 4:
					p_img_b_dst[j] = 1* M_COUNT;
					p_img_g_dst[j] = 0* M_COUNT;
					p_img_r_dst[j] = 0* M_COUNT;
					break;
				case 5:
					p_img_b_dst[j] = 1* M_COUNT;
					p_img_g_dst[j] = 0* M_COUNT;
					p_img_r_dst[j] = 1* M_COUNT;
					break;
				case 6:
					p_img_b_dst[j] = 1* M_COUNT;
					p_img_g_dst[j] = 1* M_COUNT;
					p_img_r_dst[j] = 0* M_COUNT;
					break;
				case 7:
					p_img_b_dst[j] = 1* M_COUNT;
					p_img_g_dst[j] = 1* M_COUNT;
					p_img_r_dst[j] = 1* M_COUNT;
					break;
				default:
					p_img_b_dst[j] = 1* M_COUNT;
					p_img_g_dst[j] = 1* M_COUNT;
					p_img_r_dst[j] = 1* M_COUNT;
					break;
				}
#endif


				// for (int m = 0; m < 3; m++)
				{
					int error_b = (currentPixel.channels[0] & 0xff) - (palette[index].channels[0] & 0xff);
					int error_g = (currentPixel.channels[1] & 0xff) - (palette[index].channels[1] & 0xff);
					int error_r = (currentPixel.channels[2] & 0xff) - (palette[index].channels[2] & 0xff);

					//planes_dst[i + 0][j + 1] = plus_truncate_uchar(image[y + 0][x + 1].channels[i], (error * 7) >> 4);
#if 0
					p_img_b[j + 1] = plus_truncate_uchar(p_img_b[j + 1], (error_b * 7) >> 4);
					p_img_g[j + 1] = plus_truncate_uchar(p_img_g[j + 1], (error_g * 7) >> 4);
					p_img_r[j + 1] = plus_truncate_uchar(p_img_r[j + 1], (error_r * 7) >> 4);

					p_lw_b[j - 1] = plus_truncate_uchar(p_lw_b[j - 1], (error_b * 3) >> 4);
					p_lw_g[j - 1] = plus_truncate_uchar(p_lw_g[j - 1], (error_g * 3) >> 4);
					p_lw_r[j - 1] = plus_truncate_uchar(p_lw_r[j - 1], (error_r * 3) >> 4);

					p_lw_b[j] = plus_truncate_uchar(p_lw_b[j], (error_b * 5) >> 4);
					p_lw_g[j] = plus_truncate_uchar(p_lw_g[j], (error_g * 5) >> 4);
					p_lw_r[j] = plus_truncate_uchar(p_lw_r[j], (error_r * 5) >> 4);

					p_lw_b[j + 1] = plus_truncate_uchar(p_lw_b[j], (error_b * 1) >> 4);
					p_lw_g[j + 1] = plus_truncate_uchar(p_lw_g[j], (error_g * 1) >> 4);
					p_lw_r[j + 1] = plus_truncate_uchar(p_lw_r[j], (error_r * 1) >> 4);
#endif 

#if 1
					p_img_b[j + 1] = plus_truncate_uchar(p_img_b[j + 1], (error_b * 7) / 16);
					p_img_g[j + 1] = plus_truncate_uchar(p_img_g[j + 1], (error_g * 7) / 16);
					p_img_r[j + 1] = plus_truncate_uchar(p_img_r[j + 1], (error_r * 7) / 16);

					p_lw_b[j - 1] = plus_truncate_uchar(p_lw_b[j - 1], (error_b * 3) / 16);
					p_lw_g[j - 1] = plus_truncate_uchar(p_lw_g[j - 1], (error_g * 3) / 16);
					p_lw_r[j - 1] = plus_truncate_uchar(p_lw_r[j - 1], (error_r * 3) / 16);

					p_lw_b[j] = plus_truncate_uchar(p_lw_b[j], (error_b * 5) / 16);
					p_lw_g[j] = plus_truncate_uchar(p_lw_g[j], (error_g * 5) / 16);
					p_lw_r[j] = plus_truncate_uchar(p_lw_r[j], (error_r * 5) / 16);

					p_lw_b[j + 1] = plus_truncate_uchar(p_lw_b[j + 1], (error_b * 1) / 16);
					p_lw_g[j + 1] = plus_truncate_uchar(p_lw_g[j + 1], (error_g * 1) / 16);
					p_lw_r[j + 1] = plus_truncate_uchar(p_lw_r[j + 1], (error_r * 1) / 16);
#endif 

			}
		}

	}
		merge(planes_dst, 3, result);
		imshow("输出1", result);
		imwrite("E:\\00test001.png", result);
		// cout << result << endl;
		waitKey();
}


	void  floydSteinbergDither_test(Mat  image, RGBTriple palette[])
	{
		Mat result = Mat::zeros(image.rows, image.cols, CV_8UC3);//先生成空的目标图片
		//result = image;

		Mat matr_dst, matg_dst, matb_dst;

		uchar* p_img_b_dst;
		uchar* p_img_g_dst;
		uchar* p_img_r_dst;

		vector<Mat> planes_dst;
		split(result, planes_dst);
		matb_dst = planes_dst[0];
		matg_dst = planes_dst[1];
		matr_dst = planes_dst[2];

		for (int i = 0; i < image.rows - 2; i++)
		{
			p_img_b_dst = matb_dst.ptr<uchar>(i);
			p_img_g_dst = matg_dst.ptr<uchar>(i);
			p_img_r_dst = matr_dst.ptr<uchar>(i);
			for (int j = 0; j < image.cols - 2; j++)
			{
				p_img_b_dst[j] = 255;
				p_img_g_dst[j] = 255;
				p_img_r_dst[j] = 255;
			}
		}
		merge(planes_dst, result);
		imshow("输出1", result);
		//cout << result << endl;
		waitKey();
	}

	byte plus_truncate_uchar(byte a, int b)
	{
		if ((a & 0x80) != 0)
		{
			//cout << "hhh" << endl;
		}
		if ((a & 0xff) + b < 0)
		{
			return 0;
		}
		else if ((a & 0xff) + b > 255)
		{
			return (byte)255;
		}
		else
		{
			return (byte)(a + b);
		}
	}


	byte findNearestColor(RGBTriple color, RGBTriple palette[])
	{
		int minDistanceSquared = 255 * 255 + 255 * 255 + 255 * 255 + 1;
		byte bestIndex = 0;
		for (byte i = 0; i < palette->m_size; i++)
		{
			int Rdiff = (color.channels[0] & 0xff) - (palette[i].channels[0] & 0xff);
			int Gdiff = (color.channels[1] & 0xff) - (palette[i].channels[1] & 0xff);
			int Bdiff = (color.channels[2] & 0xff) - (palette[i].channels[2] & 0xff);
			int distanceSquared = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
			if (distanceSquared < minDistanceSquared)
			{
				minDistanceSquared = distanceSquared;
				bestIndex = i;
			}
		}
		return bestIndex;
	}
};


int main()
{
	cout << "hello" << endl;
	//Mat img = imread("E:\\photo\\003.png", 1);
	Mat img = imread("E:\\test.png");
	if (img.empty())
	{
		cout << "读取原图错误!" << endl;
		return false;
	}
	if (img.empty())
	{
		cout << "读取logo错误!" << endl;
	}

	namedWindow("原图");
	//cvResizeWindow("原图", 2245/3 , 3142/3 );
	imshow("原图", img);
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	//Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片

	BMPConverter m;
	RGBTriple* palette = m.getPalette(3);
	m.floydSteinbergDither(img, palette);
	//m.floydSteinbergDither_test(img, palette);

	return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值