半色调算法

参考:https://blog.csdn.net/majinlei121/article/details/49335693
Y(亮度)=(0.299R)+(0.587G)+(0.114*B)

半色调技术是 指用少量的色彩将一幅连续色调图像(如灰度图像和彩色图像)量化为一幅二值图像或是只有少数几种色彩的彩色图像,并且量化后图像在一定距离的视觉效果和原始图像相似的技术 ;

目前半色调技术最普遍的分类法是按照它的处理方式分为:抖动法,误差扩散法,迭代法三大类。
从整体上看,误差扩散的效果是很好的。目前已有的最好的半色调效果仍是基于误差扩散方法。误差扩散也有不足之处。从速度上看,中值阈值法对每个像素只做一次比较运算,速度很快。而误差扩散要进行大量的乘除法运算,速度相对较慢。

下面是我自己


#include <iostream>
#include <opencv.hpp>
#include <opencv\highgui.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>


using namespace cv;
using namespace std;

uchar findfit(uchar p_param)
{
	//0,16,32,
	uchar m = p_param / 16;
	uchar n = p_param % 16;
	if (n <=8)
	{
		return m*16;
	}
	else
	{
		return (m + 1)*16;
	}
}

void fun1(Mat img)
{
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_dst;
	int len = sizeof(ushort);
	int len1 = sizeof(uchar);
	for (int i = 0; i < height; i++)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_dst = dst.ptr<uchar>(i);
		for (int j = 0; j < width; ++j)
		{
			p_dst[j] = (p_img[j] - minv) / (maxv - minv) * 255;
			p_dst[j] = p_dst[j] / 64;
			p_dst[j] = p_dst[j] * 64;
			//cout << (int)p_dst[j] << "  ";
		/*	if (p_dst[j] > 192)
			{
				p_dst[j] = 255;
			}
			else if(p_dst[j] > 128)
			{
				p_dst[j] = 128;
			}
			else if (p_dst[j] > 64)
			{
				p_dst[j] = 64;
			}
			else
			{
				p_dst[j] = 0;
			}*/
			//p_dst[j] = p_img[j*3/2];
			//下面是失真较大的转换方法
			//int temp = img.at<ushort>(i, j);
			//dst.at<uchar>(i, j) = temp;
		}
	}
	//   dst = img;
	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("2", 0);
	cvResizeWindow("2", 192 * 3, 216 * 3);
	imshow("2", dst);
	imwrite("E:\\4.jpg", dst);
}


void fun2(Mat img)
{
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_dst;
	int len = sizeof(ushort);
	int len1 = sizeof(uchar);
	for (int i = 0; i < height; i++)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_dst = dst.ptr<uchar>(i);
		for (int j = 0; j < width; ++j)
		{
			p_dst[j] = (p_img[j] - minv) / (maxv - minv) * 255;
			if (p_dst[j] > 192)
			{
				p_dst[j] = 255;
			}
			else if(p_dst[j] > 128)
			{
				p_dst[j] = 128;
			}
			else if (p_dst[j] > 64)
			{
				p_dst[j] = 64;
			}
			else
			{
				p_dst[j] = 0;
			}
		}
	}
	//   dst = img;
	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("3", 0);
	cvResizeWindow("3", 192 * 3, 216 * 3);
	imshow("3", dst);
	imwrite("E:\\4.jpg", dst);
}


void fun3(Mat img)
{
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_dst;
	int len = sizeof(ushort);
	int len1 = sizeof(uchar);
	for (int i = 0; i < height; i++)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_dst = dst.ptr<uchar>(i);
		for (int j = 0; j < width; ++j)
		{
			p_dst[j] = (p_img[j] - minv) / (maxv - minv) * 255;
			p_dst[j] = p_dst[j] & 0B11000000;
		}
	}
	//   dst = img;
	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("4", 0);
	cvResizeWindow("4", 192 * 3, 216 * 3);
	imshow("4", dst);
	imwrite("E:\\4.jpg", dst);
}

//误差扩散法1
void floydsetin(Mat img)
{
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	for (int i = 0; i < height-2;++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		
		p_lw = img.ptr<uchar>(i+1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width-2; ++j)
		{
			if (p_img[j] > 128)
			{
				tep = 255;
			}
			else
			{
				tep = 0;
			}
			err = p_img[j] - tep;
			p_dst[j + 1] += err * a;
			p_lw[j - 1] += err * b;
			p_lw[j] += err * c;
			p_lw[j + 1] += err * d;
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("5", 0);
	cvResizeWindow("5", 192 * 3, 216 * 3);
	imshow("5", dst);
	imwrite("E:\\5.jpg", dst);
}

//误差扩散法2灰阶
void floydsetin_2(Mat img)
{
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	//dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	for (int i = 0; i < height - 2; ++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_lw = img.ptr<uchar>(i + 1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width - 2; ++j)
		{
			if (p_img[j] > 128)
			{
				p_dst[j] = 255;
				tep = 255;
			}
			else
			{
				p_dst[j] = 0;
				tep = 0;
			}
			err = p_img[j] - tep;
			p_dst[j + 1] += err * a;
			p_lw[j - 1] += err * b;
			p_lw[j] += err * c;
			p_lw[j + 1] += err * d;
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("2灰阶", 0);
	cvResizeWindow("2灰阶", 192 * 3, 216 * 3);
	imshow("2灰阶", dst);
	imwrite("E:\\2灰阶.jpg", dst);
}

//误差扩散法2灰阶
void my2(Mat img)
{
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	//dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	for (int i = 0; i < height - 2; ++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_lw = img.ptr<uchar>(i + 1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width - 2; ++j)
		{
			if (p_img[j] > 127)
			{
				p_dst[j] = 255;
				tep = 255;
			}
			else
			{
				p_dst[j] = 0;
				tep = 0;
			}
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("my2灰阶", 0);
	cvResizeWindow("my2灰阶", 192 * 3, 216 * 3);
	imshow("my2灰阶", dst);
	imwrite("E:\\my2灰阶.jpg", dst);
}

//误差扩散法4灰阶
void floydsetin_4(Mat img)
{
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	//dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	for (int i = 0; i < height - 2; ++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_lw = img.ptr<uchar>(i + 1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width - 2; ++j)
		{
			if (p_img[j] > 192)
			{
				p_dst[j] = 192;
				tep = 192;
			}
			else if (p_img[j] > 128)
			{
				p_dst[j] = 128;
				tep = 128;
			}
			else if (p_img[j] > 64)
			{
				p_dst[j] = 64;
				tep = 64;
			}
			else
			{
				p_dst[j] = 0;
				tep = 0;
			}
			err = p_img[j] - tep;
			p_dst[j + 1] += err * a;
			p_lw[j - 1] += err * b;
			p_lw[j] += err * c;
			p_lw[j + 1] += err * d;
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("4灰阶", 0);
	cvResizeWindow("4灰阶", 192 * 3, 216 * 3);
	imshow("4灰阶", dst);
	imwrite("E:\\4灰阶.jpg", dst);
}

//误差扩散法8灰阶
void floydsetin_8(Mat img)
{
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	//dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	for (int i = 0; i < height - 2; ++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_lw = img.ptr<uchar>(i + 1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width - 2; ++j)
		{
			if (p_img[j] > 224)
			{
				p_dst[j] = 224;
				tep = 224;
			}
			else if (p_img[j] > 192)
			{
				p_dst[j] = 192;
				tep = 192;
			}
			else if (p_img[j] > 160)
			{
				p_dst[j] = 160;
				tep = 160;
			}
			else if (p_img[j] > 128)
			{
				p_dst[j] = 128;
				tep = 128;
			}
			else if (p_img[j] > 96)
			{
				p_dst[j] = 96;
				tep = 96;
			}
			else if (p_img[j] > 64)
			{
				p_dst[j] = 64;
				tep = 64;
			}
			else if (p_img[j] > 32)
			{
				p_dst[j] = 32;
				tep = 32;
			}
			else
			{
				p_dst[j] = 0;
				tep = 0;
			}
			err = p_img[j] - tep;
			p_dst[j + 1] += err * a;
			p_lw[j - 1] += err * b;
			p_lw[j] += err * c;
			p_lw[j + 1] += err * d;
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("8灰阶", 0);
	cvResizeWindow("8灰阶", 192 * 3, 216 * 3);
	imshow("8灰阶", dst);
	imwrite("E:\\8灰阶.jpg", dst);
}

//误差扩散法16灰阶
void floydsetin_16(Mat img)
{
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	//dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	for (int i = 0; i < height - 2; ++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_lw = img.ptr<uchar>(i + 1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width - 2; ++j)
		{
			for (int m = 15; m >= 0; --m)
			{
				if (p_img[j] > 16*m)
				{
					p_dst[j] = 16 * m;
					tep = 16 * m;
					break;
				}
			}

			err = p_img[j] - tep;
			p_dst[j + 1] += err * a;
			p_lw[j - 1] += err * b;
			p_lw[j] += err * c;
			p_lw[j + 1] += err * d;
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("16灰阶", 0);
	cvResizeWindow("16灰阶", 192 * 3, 216 * 3);
	imshow("16灰阶", dst);
	imwrite("E:\\16灰阶.jpg", dst);
}

//误差扩散法16灰阶
void floydsetin_16_001(Mat img)
{
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	//dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	for (int i = 0; i < height - 2; ++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_lw = img.ptr<uchar>(i + 1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width - 2; ++j)
		{
			/*for (int m = 15; m >= 0; --m)
			{
				if (p_img[j] > 16 * m)
				{
					p_dst[j] = 16 * m;
					tep = 16 * m;
					break;
				}
			}*/
			int te = findfit(p_img[j]);
			p_dst[j] = te;
			tep = te;

			err = p_img[j] - tep;
			p_dst[j + 1] += err * a;
			p_lw[j - 1] += err * b;
			p_lw[j] += err * c;
			p_lw[j + 1] += err * d;
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("16灰阶_001", 0);
	cvResizeWindow("16灰阶_001", 192 * 3, 216 * 3);
	imshow("16灰阶_001", dst);
	imwrite("E:\\16灰阶_001.jpg", dst);
}
//误差扩散法n灰阶
void floydsetin_16(Mat img , int m)
{
	if (m <= 0)
	{
		return;
	}
	double a = 0.4375;
	double b = 0.1875;
	double c = 0.3125;
	double d = 0.0625;
	DWORD Start_time = GetTickCount(); //计时开始
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	//dst = img;
	double minv = 0.0, maxv = 0.0;
	double* minp = &minv;
	double* maxp = &maxv;
	minMaxIdx(img, minp, maxp);  //取得像素值最大值和最小值
	int t = CV_8UC1;
	//用指针访问像素,速度更快
	uchar* p_img;
	uchar* p_lw;
	uchar* p_dst;
	int tep = 0;
	int err = 0;
	int step = 256 / m;
	for (int i = 0; i < height - 2; ++i)
	{
		p_img = img.ptr<uchar>(i);//获取每行首地址
		p_lw = img.ptr<uchar>(i + 1);
		p_dst = dst.ptr<uchar>(i);
		for (int j = 2; j < width - 2; ++j)
		{
			for (; m >= 0; --m)
			{
				if (p_img[j] > step * (m-1))
				{
					p_dst[j] = step * (m)-1;
					tep = step * (m)-1;
					if (m == 0)
					{
						p_dst[j] = 0;
						tep = 0;
					}
					break;
				}
			}

			err = p_img[j] - tep;
			p_dst[j + 1] += err * a;
			p_lw[j - 1] += err * b;
			p_lw[j] += err * c;
			p_lw[j + 1] += err * d;
		}
	}

	DWORD End_time = GetTickCount(); //计时结束
	cout << "Time used:" << End_time - Start_time << " ms" << '\n';
	namedWindow("灰阶", 0);
	cvResizeWindow("灰阶", 192 * 3, 216 * 3);
	imshow("灰阶", dst);
	imwrite("E:\\灰阶.jpg", dst);
}


int main()
{	
	Mat img = imread("E:\\1.jpg", 0);//加载图像;
	if (img.empty())
	{
		cout << "打开文件失败" << endl;
		return -1;
	}
	int width = img.cols;//图片宽度
	int height = img.rows;//图片高度
	Mat dst = Mat::zeros(height, width, CV_8UC1);//先生成空的目标图片
	dst = img;
	imwrite("E:\\灰阶原图.jpg", dst);

	int m_channel = 	(&img)->channels();
	Size m_size = img.size();
	cout << m_size << endl;
	cout << img.total() << endl;
	cout << img.rows << "   " << img.cols << endl;
	//cout << img << endl;
	cout << img.isContinuous() << endl;
	cout << img.type()<< endl;
	cout << CV_8UC1 << endl;
	cout << CV_8UC2 << endl;
	cout << CV_8UC3 << endl;

	for (int r = 0; r < img.rows; ++r)
	{
		const uchar* ptr = img.ptr<uchar>(r);

		for (int c = 0; c < img.cols; ++c)
		{
			//cout << ptr[c] << ",";
		}
		//cout << endl;
	}
	namedWindow("1", WINDOW_NORMAL);
	cvResizeWindow("1", 192 * 3, 216 * 3);
	imshow("1", img);


	//方法1
	//fun1(img);
	//fun2(img);
	//fun3(img);
	//my2(img);
	//floydsetin_2(img);
	//floydsetin_2(img);
	//floydsetin_4(img);
	//floydsetin_8(img);
	//floydsetin_16(img);
	floydsetin_16_001(img);
	findfit(19);
	waitKey(0);
	system("pause");

	return 0;
}

在这里插入图片描述

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值