当前位置: 首页 > 工具软件 > Anime4K > 使用案例 >

OpenCV——Anime4k代码

阙沛
2023-12-01

 新的实时漫画放大算法。立志于改善AV画质。至于更清晰的说明请搜索baidu。

核心还是用到了8方向sobel核的思想,在改善边缘的同时,不造成振铃和过冲。

代码运行设置参数请详细见官网说明。

我的设置:"Art_0.png" "test_upscaled.png" 2 (Art_0.png直接下载测试有问题,估计不是真正的png图像,需要额外转一次格式)

值得一提的是,代码所需处理的图像必须是png格式(4通道,aRGB格式)

#include <iostream>
#include <opencv2\opencv.hpp>
#include <windows.h>
using namespace std;
using namespace cv;
/**
*
* @author bloc97
*/
class ImageKernel{

private:
	int width = 0;
	int height = 0;

	int * colorData;
	BYTE * derivData;
	int * tempColorData;
	BYTE * tempDerivData;
	Mat img;
	bool isForwardPass = true;

	int *runtype; 

public:
	ImageKernel()
	{
		runtype = new int[3];//Kernel run type, unblur strength (0-255), refine strength (0-255)
		runtype[0] = 0;
		runtype[1] = 80;
		runtype[2] = 225;
	}
	void setBufferedImage(Mat &img) {

		if (width != img.cols || height != img.rows) {
			colorData = (int *)img.data;
			derivData = new BYTE[img.rows * img.cols];
			tempColorData = new int[img.rows * img.cols];
			tempDerivData = new BYTE[img.rows * img.cols];
			
			width = img.cols;
			height = img.rows;
		}
		isForwardPass = true;
		this->img = img.clone();

	}

	void updateBufferedImage() {
		if (!isForwardPass) {
			runtype[0] = -1;
			this->execute(img.rows * img.cols);
			isForwardPass = !isForwardPass;
		}
	}
	void execute(int range)
	{
		for (int i = 0; i < range; i++)
		{
			this->run(i);
		}
	}
	void setPushStrength(float strength) {
		runtype[1] = clamp((int)(strength * 255), 0, 0xFFFF);
	}

	void setPushGradStrength(float strength) {
		runtype[2] = clamp((int)(strength * 255), 0, 0xFFFF);
	}

	void process() {
		//Compute Lum
		runtype[0] = 0;
		this->execute(img.rows * img.cols);

		int maxUnblur = runtype[1];
		int remainingUnblur = runtype[1];

		while (remainingUnblur > 0) { //While we still have to remove blur spread
			int currentThin = min(remainingUnblur, 0xFF);

			if (isForwardPass) {
				runtype[0] = 1;
			}
			else {
				runtype[0] = 2;
			}
			this->execute(img.rows * img.cols);
			isForwardPass = !isForwardPass;

			remainingUnblur -= currentThin;
		}

		runtype[1] = maxUnblur; //Restore original value

								//Compute Gradient
		if (isForwardPass) {
			runtype[0] = 3;
		}
		else {
			runtype[0] = 4;
		}
		this->execute(img.rows * img.cols);
		isForwardPass = !isForwardPass;

		int maxRefine = runtype[2];
		int remainingRefine = runtype[2];

		while (remainingRefine > 0) { //While we still have to refine
			int currentRefine = min(remainingRefine, 0xFF);

			if (isForwardPass) {
				runtype[0] = 5;
			}
			else {
				runtype[0] = 6;
			}
			this->execute(img.rows * img.cols);
			isForwardPass = !isForwardPass;

			remainingRefine -= currentRefine;
		}

		runtype[2] = maxRefine; //Restore original value



	}
private:
	int clamp(int i, int min, int max) {
		if (i < min) {
			i = min;
		}
		else if (i > max) {
			i = max;
		}

		return i;
	}
	int getAlpha(int argb) {
		return argb >> 24 & 0xFF;
	}
	int getRed(int rgb) {
		return rgb >> 16 & 0xFF;
	}
	int getGreen(int rgb) {
		return rgb >> 8 & 0xFF;
	}
	int getBlue(int rgb) {
		return rgb & 0xFF;
	}
	int toARGB(int a, int r, int g, int b) {
		return (clamp(a, 0, 0xFF) << 24) | (clamp(r, 0, 0xFF) << 16) | (clamp(g, 0, 0xFF) << 8) | (clamp(b, 0, 0xFF));
	}
	int getLuminance(int r, int g, int b) {
		return (r + r + g + g + g + b) / 6;
	}
	int unsignedByteToInt(byte b) {
		return ((int)b) & 0xFF;
	}

	bool compareLuminance3(int dark0, int dark1, int dark2, int light0, int light1, int light2) {
		return (dark0 < light0 && dark0 < light1 && dark0 < light2 && dark1 < light0 && dark1 < light1 && dark1 < light2 && dark2 < light0 && dark2 < light1 && dark2 < light2);
	}

	bool compareLuminance4(int dark0, int dark1, int dark2, int light0, int light1, int light2, int light3) {
		return (dark0 < light0 && dark0 < light1 && dark0 < light2 && dark0 < light3 && dark1 < light0 && dark1 < light1 && dark1 < light2&& dark1 < light3 && dark2 < light0 && dark2 < light1 && dark2 < light2 && dark2 < light3);
	}

	int averageGray(int d0, int d1, int d2) {
		return clamp((d0 + d1 + d2) / 3, 0, 0xFF);
	}

	int averageRGB(int c0, int c1, int c2) {
		int ra = (getRed(c0) + getRed(c1) + getRed(c2)) / 3;
		int ga = (getGreen(c0) + getGreen(c1) + getGreen(c2)) / 3;
		int ba = (getBlue(c0) + getBlue(c1) + getBlue(c2)) / 3;
		int aa = (getAlpha(c0) + getAlpha(c1) + getAlpha(c2)) / 3;

		return toARGB(aa, ra, ga, ba);
	}

	int weightedAverageGray(int d0, int d1, int alpha) {
		return clamp((d0 * (0xFF - alpha) + d1 * alpha) / 0xFF, 0, 0xFF);
	}

	int weightedAverageRGB(int c0, int c1, int alpha) {
		int ra = (getRed(c0) * (0xFF - alpha) + getRed(c1) * alpha) / 0xFF;
		int ga = (getGreen(c0) * (0xFF - alpha) + getGreen(c1) * alpha) / 0xFF;
		int ba = (getBlue(c0) * (0xFF - alpha) + getBlue(c1) * alpha) / 0xFF;
		int aa = (getAlpha(c0) * (0xFF - alpha) + getAlpha(c1) * alpha) / 0xFF;

		return toARGB(aa, ra, ga, ba);
	}

	void gradientRefine(int id, int strength, bool inversePass) {

		int * thisColorData = colorData;
		BYTE * thisDerivData = derivData;
		int * thisTempColorData = tempColorData;
		BYTE * thisTempDerivData = tempDerivData;

		if (inversePass) {
			thisColorData = tempColorData;
			thisDerivData = tempDerivData;
			thisTempColorData = colorData;
			thisTempDerivData = derivData;
		}

		strength = clamp(strength, 0, 0xFF);

		int x = id % width;
		int y = id / width;

		//Default translation constants
		int xn = -1;
		int xp = 1;
		int yn = -width;
		int yp = width;

		//If x or y is on the border, don't move out of bounds
		if (x == 0) {
			xn = 0;
		}
		else if (x == width - 1) {
			xp = 0;
		}
		if (y == 0) {
			yn = 0;
		}
		else if (y == height - 1) {
			yp = 0;
		}

		//Get indices for adjacent cells
		int ti = id + yn;
		int tli = ti + xn;
		int tri = ti + xp;

		int li = id + xn;
		int ri = id + xp;

		int bi = id + yp;
		int bli = bi + xn;
		int bri = bi + xp;

		int di0 = id;
		int di1 = id;
		int di2 = id;

		int li0 = id;
		int li1 = id;
		int li2 = id;
		int li3 = id;

		bool l4 = false;

		for (int kernelType = 0; kernelType<8; kernelType++) {
			if (kernelType == 0) { //Todo, binary search instead of searching entire list
				di0 = tli;
				di1 = ti;
				di2 = tri;

				li0 = id;
				li1 = bli;
				li2 = bi;
				li3 = bri;

				l4 = true;

			}
			else if (kernelType == 1) {
				di0 = ti;
				di1 = tri;
				di2 = ri;

				li0 = id;
				li1 = li;
				li2 = bi;

				l4 = false;

			}
			else if (kernelType == 2) {
				di0 = tri;
				di1 = ri;
				di2 = bri;

				li0 = id;
				li1 = tli;
				li2 = li;
				li3 = bli;

				l4 = true;

			}
			else if (kernelType == 3) {
				di0 = ri;
				di1 = bri;
				di2 = bi;

				li0 = id;
				li1 = ti;
				li2 = li;

				l4 = false;

			}
			else if (kernelType == 4) {
				di0 = bli;
				di1 = bi;
				di2 = bri;

				li0 = id;
				li1 = tli;
				li2 = ti;
				li3 = tri;

				l4 = true;

			}
			else if (kernelType == 5) {
				di0 = li;
				di1 = bli;
				di2 = bi;

				li0 = id;
				li1 = ti;
				li2 = ri;

				l4 = false;

			}
			else if (kernelType == 6) {
				di0 = tli;
				di1 = li;
				di2 = bli;

				li0 = id;
				li1 = tri;
				li2 = ri;
				li3 = bri;

				l4 = true;

			}
			else if (kernelType == 7) {
				di0 = tli;
				di1 = ti;
				di2 = li;

				li0 = id;
				li1 = bi;
				li2 = ri;

				l4 = false;

			}
			else if (kernelType < 0 || kernelType >= 8) {
				return;
			}

			if (l4) {

				int d0 = unsignedByteToInt(thisDerivData[di0]);
				int d1 = unsignedByteToInt(thisDerivData[di1]);
				int d2 = unsignedByteToInt(thisDerivData[di2]);

				int l0 = unsignedByteToInt(thisDerivData[li0]);
				int l1 = unsignedByteToInt(thisDerivData[li1]);
				int l2 = unsignedByteToInt(thisDerivData[li2]);
				int l3 = unsignedByteToInt(thisDerivData[li3]);

				if (compareLuminance4(d0, d1, d2, l0, l1, l2, l3)) {
					int c0 = thisColorData[di0];
					int c1 = thisColorData[di1];
					int c2 = thisColorData[di2];
					thisTempDerivData[id] = (byte)weightedAverageGray(thisDerivData[id], averageGray(d0, d1, d2), strength);
					thisTempColorData[id] = weightedAverageRGB(thisColorData[id], averageRGB(c0, c1, c2), strength);
					return;
				}
			}
			else {

				int d0 = unsignedByteToInt(thisDerivData[di0]);
				int d1 = unsignedByteToInt(thisDerivData[di1]);
				int d2 = unsignedByteToInt(thisDerivData[di2]);

				int l0 = unsignedByteToInt(thisDerivData[li0]);
				int l1 = unsignedByteToInt(thisDerivData[li1]);
				int l2 = unsignedByteToInt(thisDerivData[li2]);

				if (compareLuminance3(d0, d1, d2, l0, l1, l2)) {
					int c0 = thisColorData[di0];
					int c1 = thisColorData[di1];
					int c2 = thisColorData[di2];
					thisTempDerivData[id] = (byte)weightedAverageGray(thisDerivData[id], averageGray(d0, d1, d2), strength);
					thisTempColorData[id] = weightedAverageRGB(thisColorData[id], averageRGB(c0, c1, c2), strength);
					return;
				}
			}
		}
		thisTempDerivData[id] = thisDerivData[id];
		thisTempColorData[id] = thisColorData[id];


	}

	void unblur(int id, int strength, bool inversePass) {


		int * thisColorData = colorData;
		BYTE * thisDerivData = derivData;
		int * thisTempColorData = tempColorData;
		BYTE * thisTempDerivData = tempDerivData;

		if (inversePass) {
			thisColorData = tempColorData;
			thisDerivData = tempDerivData;
			thisTempColorData = colorData;
			thisTempDerivData = derivData;
		}

		strength = clamp(strength, 0, 0xFF);

		int x = id % width;
		int y = id / width;

		//Default translation constants
		int xn = -1;
		int xp = 1;
		int yn = -width;
		int yp = width;

		//If x or y is on the border, don't move out of bounds
		if (x == 0) {
			xn = 0;
		}
		else if (x == width - 1) {
			xp = 0;
		}
		if (y == 0) {
			yn = 0;
		}
		else if (y == height - 1) {
			yp = 0;
		}

		//Get indices for adjacent cells
		int ti = id + yn;
		int tli = ti + xn;
		int tri = ti + xp;

		int li = id + xn;
		int ri = id + xp;

		int bi = id + yp;
		int bli = bi + xn;
		int bri = bi + xp;

		int di0 = id;
		int di1 = id;
		int di2 = id;

		int li0 = id;
		int li1 = id;
		int li2 = id;
		int li3 = id;

		bool l4 = false;

		int lightestColor = thisColorData[id];
		int lightestLum = thisDerivData[id];

		for (int kernelType = 0; kernelType<8; kernelType++) {
			if (kernelType == 0) { //Todo, binary search instead of searching entire list
				di0 = tli;
				di1 = ti;
				di2 = tri;

				li0 = id;
				li1 = bli;
				li2 = bi;
				li3 = bri;

				l4 = true;

			}
			else if (kernelType == 1) {
				di0 = ti;
				di1 = tri;
				di2 = ri;

				li0 = id;
				li1 = li;
				li2 = bi;

				l4 = false;

			}
			else if (kernelType == 2) {
				di0 = tri;
				di1 = ri;
				di2 = bri;

				li0 = id;
				li1 = tli;
				li2 = li;
				li3 = bli;

				l4 = true;

			}
			else if (kernelType == 3) {
				di0 = ri;
				di1 = bri;
				di2 = bi;

				li0 = id;
				li1 = ti;
				li2 = li;

				l4 = false;

			}
			else if (kernelType == 4) {
				di0 = bli;
				di1 = bi;
				di2 = bri;

				li0 = id;
				li1 = tli;
				li2 = ti;
				li3 = tri;

				l4 = true;

			}
			else if (kernelType == 5) {
				di0 = li;
				di1 = bli;
				di2 = bi;

				li0 = id;
				li1 = ti;
				li2 = ri;

				l4 = false;

			}
			else if (kernelType == 6) {
				di0 = tli;
				di1 = li;
				di2 = bli;

				li0 = id;
				li1 = tri;
				li2 = ri;
				li3 = bri;

				l4 = true;

			}
			else if (kernelType == 7) {
				di0 = tli;
				di1 = ti;
				di2 = li;

				li0 = id;
				li1 = bi;
				li2 = ri;

				l4 = false;

			}
			else if (kernelType < 0 || kernelType >= 8) {
				return;
			}



			if (l4) {

				int d0 = unsignedByteToInt(thisDerivData[di0]);
				int d1 = unsignedByteToInt(thisDerivData[di1]);
				int d2 = unsignedByteToInt(thisDerivData[di2]);

				int l0 = unsignedByteToInt(thisDerivData[li0]);
				int l1 = unsignedByteToInt(thisDerivData[li1]);
				int l2 = unsignedByteToInt(thisDerivData[li2]);
				int l3 = unsignedByteToInt(thisDerivData[li3]);

				if (!compareLuminance4(d0, d1, d2, l0, l1, l2, l3)) {
					int c0 = thisColorData[di0];
					int c1 = thisColorData[di1];
					int c2 = thisColorData[di2];

					int newColor = weightedAverageRGB(thisColorData[id], averageRGB(c0, c1, c2), strength);
					int newLum = getLuminance(getRed(newColor), getGreen(newColor), getBlue(newColor));

					if (newLum > lightestLum) {
						lightestLum = newLum;
						lightestColor = newColor;
					}

				}
			}
			else {

				int d0 = unsignedByteToInt(thisDerivData[di0]);
				int d1 = unsignedByteToInt(thisDerivData[di1]);
				int d2 = unsignedByteToInt(thisDerivData[di2]);

				int l0 = unsignedByteToInt(thisDerivData[li0]);
				int l1 = unsignedByteToInt(thisDerivData[li1]);
				int l2 = unsignedByteToInt(thisDerivData[li2]);

				if (!compareLuminance3(d0, d1, d2, l0, l1, l2)) {
					int c0 = thisColorData[di0];
					int c1 = thisColorData[di1];
					int c2 = thisColorData[di2];

					int newColor = weightedAverageRGB(thisColorData[id], averageRGB(c0, c1, c2), strength);
					int newLum = getLuminance(getRed(newColor), getGreen(newColor), getBlue(newColor));

					if (newLum > lightestLum) {
						lightestLum = newLum;
						lightestColor = newColor;
					}
				}
			}
		}
		thisTempColorData[id] = lightestColor;
		thisTempDerivData[id] = (byte)clamp(lightestLum, 0, 0xFF);

	}

public:
		void computeGradient(int id, bool inversePass) {

		int * thisColorData = colorData;
		BYTE * thisDerivData = derivData;
		int * thisTempColorData = tempColorData;
		BYTE * thisTempDerivData = tempDerivData;

		if (inversePass) {
			thisColorData = tempColorData;
			thisDerivData = tempDerivData;
			thisTempColorData = colorData;
			thisTempDerivData = derivData;
		}

		int x = id % width;
		int y = id / width;

		//Default translation constants
		int xn = -1;
		int xp = 1;
		int yn = -width;
		int yp = width;

		//If x or y is on the border, don't move out of bounds
		if (x == 0) {
			xn = 0;
		}
		else if (x == width - 1) {
			xp = 0;
		}
		if (y == 0) {
			yn = 0;
		}
		else if (y == height - 1) {
			yp = 0;
		}

		//Get indices for adjacent cells
		int topi = id + yn;
		int topLefti = topi + xn;
		int topRighti = topi + xp;

		int lefti = id + xn;
		int righti = id + xp;

		int bottomi = id + yp;
		int bottomLefti = bottomi + xn;
		int bottomRighti = bottomi + xp;

		//Get values for adjacent cells
		int top = ((int)thisDerivData[topi]) & 0xFF;
		int topLeft = ((int)thisDerivData[topLefti]) & 0xFF;
		int topRight = ((int)thisDerivData[topRighti]) & 0xFF;

		int left = ((int)thisDerivData[lefti]) & 0xFF;
		int right = ((int)thisDerivData[righti]) & 0xFF;

		int bottom = ((int)thisDerivData[bottomi]) & 0xFF;
		int bottomLeft = ((int)thisDerivData[bottomLefti]) & 0xFF;
		int bottomRight = ((int)thisDerivData[bottomRighti]) & 0xFF;

		//Perform Sobel Operation
		int xSobel = abs(-topLeft + topRight
			- left - left + right + right
			- bottomLeft + bottomRight);
		int ySobel = abs(-topLeft - top - top - topRight
			+ bottomLeft + bottom + bottom + bottomRight);


		int deriv = clamp((xSobel + ySobel) / 2, 0, 0xFF);
		thisTempDerivData[id] = (byte)deriv;
		thisTempColorData[id] = thisColorData[id];
	}



		void run(int ID) {
		int id = ID;

		if (runtype[0] == 0) {

			int rgb = colorData[id];
			int r = getRed(rgb);
			int g = getGreen(rgb);
			int b = getBlue(rgb);
			int lum = clamp(getLuminance(r, g, b), 0, 0xFF);
			derivData[id] = (byte)lum;

		}
		else if (runtype[0] == 1) {
			unblur(id, runtype[1], false);
		}
		else if (runtype[0] == 2) {
			unblur(id, runtype[1], true);
		}
		else if (runtype[0] == 3) {
			computeGradient(id, false);
		}
		else if (runtype[0] == 4) {
			computeGradient(id, true);
		}
		else if (runtype[0] == 5) {
			gradientRefine(id, runtype[2], false);
		}
		else if (runtype[0] == 6) {
			gradientRefine(id, runtype[2], true);


		}
		else if (runtype[0] < 0) { //Prevent JVM from optimizing this chain of ifs into a switch statement, since switch/case causes Aparapi to crash
			derivData[id] = tempDerivData[id];
			colorData[id] = tempColorData[id];
		}

	}
};


/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/


/**
*
* @author bloc97
*/
void main(int argc, char *argv[])
{

	/**
	* @param argv the command line arguments
	*/

	ImageKernel kernel;

		
	if (argc < 3) {
		cout << "Error: Please specify input and output png files.";
		return;
	}

	String inputFile = argv[1];
	String outputFile = argv[2];
	
	Mat img = imread(inputFile, -1);
	imshow("img", img);
	waitKey();

	float scale = 2.0f;

	if (argc >= 4) {
		scale = atof(argv[3]);
	}
	scale = 2.0f;
	float pushStrength = scale / 6.0f;
	float pushGradStrength = scale / 2.0f;

	if (argc >= 5) {
		pushGradStrength = atof(argv[4]);
	}
	if (argc >= 6) {
		pushStrength = atof(argv[5]);
	}

	resize(img, img, Size(img.cols * scale, img.rows * scale), 0, 0, INTER_CUBIC);
	imshow("ddst", img);
	kernel.setPushStrength(pushStrength);
	kernel.setPushGradStrength(pushGradStrength);
	kernel.setBufferedImage(img);
	kernel.process();
	kernel.updateBufferedImage();
	Mat ddst;
	
	imshow("dst", img);
	waitKey();
	imwrite(outputFile, img);



}

原文是java的转成 c++难度不大,索性就赚了,将来也好用。

 类似资料: