GithubHelp home page GithubHelp logo

Comments (4)

OneLoneCoder avatar OneLoneCoder commented on July 19, 2024

Lol, sorry for any confusion - quite literally I've had three people today contact me via youtube, discord and github regarding improved algorithms for this, so I'm getting mixed up - If I've mistakenly interacted with you in anyway, I apologise - The imagery looks great - I'll do a follow up video to this to show off your findings.

from javidx9.

kingatgi avatar kingatgi commented on July 19, 2024

This is the Simplify version (higher performance with lower quality)
result image https://imgur.com/6BwxkJU https://imgur.com/fKzXyNs https://imgur.com/Y8yENnp
color distribution in coordinate https://imgur.com/PjBUlAT
(because my algorithm generate RGB lookuptable on the fly ,the 16 default color palletes could be change for better color distribution in the RGB coordinate. In the machine and terminal section of this link(https://en.wikipedia.org/wiki/List_of_color_palettes ) show lots of color palletes of difference console if we choose their color palletes we could mimic those console color style(well need to modify the color value accordingly in RGB ColorTable[16] then the algorithm will generate newRGB table)
the original implementation need some optimization though,in the 3,3,3 surrounding cube look up I silly
create vector to store all color in those cube to lookup this could be change by only remember the small's distance's r,g,b,coordinate.It will perform much better.(already updated)

#include<stdio.h>
#include"olcConsoleGameEngine.h"
#include<algorithm>
#include<string>
#include"lodepng.h"

std::vector<unsigned char> vecPng;
unsigned  imageWidth, imageHeight;

class testConsole :public olcConsoleGameEngine
{
	virtual bool OnUserCreate()
	{
//png
				pngWidth = imageWidth;
				pngHeight = imageHeight;

//dither buff
				dither = new  RGB[sizeof(RGB)*pngWidth*pngHeight * 3];

				memset(dither, 0, sizeof(RGB)*pngWidth*pngHeight * 3);


		ccbToRGB.clear();
		for (int f = 0; f < 16; ++f)
			for (int b = 0; b < 16; ++b)
				for (int p = 0; p < 4; ++p)
				{
					RGB newColor = ColorTable[f] * percent[p] + ColorTable[b] * (1 - percent[p]);
					newColor.clip();
					ccbToRGB.push_back({ {f,b,p} ,newColor });
				}

		//delete  duplicate color
		for (auto i = ccbToRGB.begin(); i != ccbToRGB.end();)
		{
			CTR::iterator next = i + 1;
			while (next != ccbToRGB.end())
			{
				if (i->second == next->second)
					next = ccbToRGB.erase(next);
				else
					++next;
			}
			if (i != ccbToRGB.end())++i;
		}

		int n = 0;
		for (auto a : ccbToRGB)
		{
			int r = a.second.r, b = a.second.b, g = a.second.g;
			_3DTABLE[r / 64][g / 64][b / 64].push_back(a);
			++n;
		}
		//
		for (int _x = 0; _x < pngWidth; ++_x)
			for (int _y = 0; _y < pngHeight; ++_y)
			{
				int pngIndex = (_y*pngWidth + _x);
				//how to find a color
				RGB color = { vecPng[pngIndex * 4 + 0],vecPng[pngIndex * 4 + 1],vecPng[pngIndex * 4 + 2] };
				color = color + dither[pngIndex];
				color.clip();
				CTR* colorToCompare=&_3DTABLE[color.r/64][color.g/64][color.b/64];
				int l = 12288+1;
				size_t index = 0;
				for (size_t i = 0; i < colorToCompare->size(); ++i)
				{
					int newl = (colorToCompare->at(i).second - color).sqr();
					if (newl < l)
					{
						l = newl;
						index = i;
					}
				}
				CCB ccb = colorToCompare->at(index).first;
				RGB error = color - colorToCompare->at(index).second;// use for error diffusion, totally up to you
		
				if (_x + 1 < pngWidth)
					dither[_y*pngWidth + _x + 1] = error * (7.0f / 16.0f);
				if (_y + 1 < pngHeight)
				{
					if (_x - 1 >= 0)
						dither[(_y + 1)*pngWidth + _x - 1] = error * (3.0f / 16.0f);
					dither[(_y + 1)*pngWidth + _x] = error * (5.0f / 16.0f);
					if (_x + 1 < pngWidth)
						dither[(_y + 1)*pngWidth + _x + 1] = error * (1.0f / 16.0f);
				}
				Draw(_x, _y, pixelP[ccb.p], ccb.f | (ccb.b << 4));

			}


		return true;

	}
	virtual bool OnUserUpdate(float _fDeltaTime)
	{
		return true;

	}
	struct RGB
	{
		int r;
		int g;
		int b;
		RGB operator+(RGB _t)
		{
			return { r + _t.r,g + _t.g,b + _t.b };
		}
		RGB operator-(RGB _t)
		{
			return { r - _t.r,g - _t.g,b - _t.b };
		}
		RGB operator*(float _f)
		{
			return { (int)((float)r*_f),(int)((float)g*_f),(int)((float)b*_f) };
		}
		bool operator==(RGB _t)
		{
			return  r == _t.r&&g == _t.g&&b == _t.b;
		}
		int sqr()
		{
			return r * r + g * g + b * b;
		}
		void clip()
		{
			if (r < 0) r = 0;
			if (r >= 256) r = 255;
			if (g < 0) g = 0;
			if (g >= 256) g = 255;
			if (b < 0) b = 0;
			if (b >= 256) b = 255;
		}
	};
	//console color block
	struct CCB
	{
		//foreground color
		int f;
		//background color
		int b;
		//block type
		int p;
	};
	RGB ColorTable[16] =
	{
		{0,0,0},
		{0,0,128},
		{0,128,0},
		{0,128,128},
		{128,0,0},
		{128,0,128},
		{128,128,0},
		{192,192,192},
		{128,128,128},
		{0,0,256},
		{0,256,0},
		{0,256,256},
		{256,0,0},
		{256,0,256},
		{256,256,0},
		{256,256,256},
	};
	typedef std::vector<std::pair<CCB, RGB>> CTR;
	std::vector<std::pair<CCB, RGB>> ccbToRGB;
	CTR _3DTABLE[4][4][4];
	//ForeGroundBlockPercent
	float percent[4] = { 1,0.75,0.5,0.25 };
	short pixelP[4] = { PIXEL_SOLID,PIXEL_THREEQUARTERS,PIXEL_HALF,PIXEL_QUARTER };
	RGB *dither;
	unsigned pngWidth, pngHeight;
};
int main()
{
	const char* filename = "C:/test_PNG/van_gogh.png";
//	const char* filename = "C:/test_PNG/olc.png";
		unsigned error = lodepng::decode(vecPng, imageWidth, imageHeight, filename);
		if (error) {
			std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
			return false;
		}
  testConsole test;
	test.ConstructConsole(imageWidth, imageHeight, 4, 4);
//	test.ConstructConsole(100, 100, 8, 8);
	test.Start();

	return 0;
}

from javidx9.

kingatgi avatar kingatgi commented on July 19, 2024

here's the 9x9x9 version without dither effect (I just thinking well the 9x9x9 is making the best approximation maybe I could get out of dither overhead for performance gain this is about 32 error range according to the dither method(I didn't do the 4x4x4 without dither because the 64 error range will have significant visual difference when I pick color to compare in photoshop)
some result of this version https://imgur.com/bd4ZHZv https://imgur.com/yVSZhLA https://imgur.com/erAl3D4

#include<stdio.h>
#include"olcConsoleGameEngine.h"
#include<algorithm>
#include<string>
#include"lodepng.h"

std::vector<unsigned char> vecPng;
unsigned  imageWidth, imageHeight;

class testConsole :public olcConsoleGameEngine
{
	virtual bool OnUserCreate()
	{
		pngWidth = imageWidth;
		pngHeight = imageHeight;
//		dither = new  RGB[sizeof(RGB)*pngWidth*pngHeight * 3];

//		memset(dither, 0, sizeof(RGB)*pngWidth*pngHeight * 3);
		ccbToRGB.clear();
		for (int f = 0; f < 16; ++f)
			for (int b = 0; b < 16; ++b)
				for (int p = 0; p < 4; ++p)
				{
					ccbToRGB.push_back({ {f,b,p} ,ColorTable[f] * percent[p] + ColorTable[b] * (1 - percent[p]) });
				}

		//delete  duplicate color
		for (auto i = ccbToRGB.begin(); i != ccbToRGB.end();)
		{
			CTR::iterator next = i + 1;
			while (next != ccbToRGB.end())
			{
				if (i->second == next->second)
					next = ccbToRGB.erase(next);
				else
					++next;
			}
			if (i != ccbToRGB.end())++i;
		}


		int n = 0;
		for (auto a : ccbToRGB)
		{
			int r = a.second.r, b = a.second.b, g = a.second.g;
			_3DTABLE[r / 32][g / 32][b / 32].push_back(a);
			++n;
		}
		//
		for (int _x = 0; _x < pngWidth; ++_x)
			for (int _y = 0; _y < pngHeight; ++_y)
			{
				int pngIndex = (_y*pngWidth + _x);
				//how to find a color
				RGB color = { vecPng[pngIndex * 4 + 0],vecPng[pngIndex * 4 + 1],vecPng[pngIndex * 4 + 2] };
//				color = color + dither[pngIndex];
				color.clip();
				int foundr, foundg, foundb;
				// first find the cube surround it
				int l = 8192;
				size_t index = 0;
				for (int x = -1; x <= 1; ++x)
					for (int y = -1; y <= 1; ++y)
						for (int z = -1; z <= 1; ++z)
						{
							int xr = color.r / 32 + x, yg = y + color.g / 32, zb = z + color.b / 32;
							if (xr >= 0 && xr <= 8 && yg >= 0 && yg <= 8 && zb >= 0 && zb <= 8)
							{
								int  the_index = 0;
								for (auto i : _3DTABLE[xr][yg][zb])
								{
									int newl = (i.second - color).sqr();
									if (newl < l)
									{
										l = newl;
										index = the_index;
										foundr = xr;
										foundg = yg;
										foundb = zb;
									}
									++the_index;

								}
							}
						}
				//maximum distance will not bigger than the cubic diagonal 
				CCB ccb = _3DTABLE[foundr][foundg][foundb][index].first;
//				RGB error = color -_3DTABLE[foundr][foundg][foundb][index].second;// use for error diffusion, totally up to you
		
//				if (_x + 1 < pngWidth)
//					dither[_y*pngWidth + _x + 1] = error * (7.0f / 16.0f);
//				if (_y + 1 < pngHeight)
//				{
//					if (_x - 1 >= 0)
//						dither[(_y + 1)*pngWidth + _x - 1] = error * (3.0f / 16.0f);
//					dither[(_y + 1)*pngWidth + _x] = error * (5.0f / 16.0f);
//					if (_x + 1 < pngWidth)
//						dither[(_y + 1)*pngWidth + _x + 1] = error * (1.0f / 16.0f);
//				}
				Draw(_x, _y, pixelP[ccb.p], ccb.f | (ccb.b << 4));

			}


		return true;

	}
	virtual bool OnUserUpdate(float _fDeltaTime)
	{
		return true;

	}
	struct RGB
	{
		int r;
		int g;
		int b;
		RGB operator+(RGB _t)
		{
			return { r + _t.r,g + _t.g,b + _t.b };
		}
		RGB operator-(RGB _t)
		{
			return { r - _t.r,g - _t.g,b - _t.b };
		}
		RGB operator*(float _f)
		{
			return { (int)((float)r*_f),(int)((float)g*_f),(int)((float)b*_f) };
		}
		bool operator==(RGB _t)
		{
			return  r == _t.r&&g == _t.g&&b == _t.b;
		}
		int sqr()
		{
			return r * r + g * g + b * b;
		}
		void clip()
		{
			if (r < 0) r = 0;
			if (r > 256) r = 256;
			if (g < 0) g = 0;
			if (g > 256) g = 256;
			if (b < 0) b = 0;
			if (b > 256) b = 256;
		}
	};
	//console color block
	struct CCB
	{
		//foreground color
		int f;
		//background color
		int b;
		//block type
		int p;
	};
	RGB ColorTable[16] =
	{
		{0,0,0},
		{0,0,128},
		{0,128,0},
		{0,128,128},
		{128,0,0},
		{128,0,128},
		{128,128,0},
		{192,192,192},
		{128,128,128},
		{0,0,256},
		{0,256,0},
		{0,256,256},
		{256,0,0},
		{256,0,256},
		{256,256,0},
		{256,256,256},
	};
	typedef std::vector<std::pair<CCB, RGB>> CTR;
	std::vector<std::pair<CCB, RGB>> ccbToRGB;
	CTR _3DTABLE[9][9][9];
	//ForeGroundBlockPercent
	float percent[4] = { 1,0.75,0.5,0.25 };
	short pixelP[4] = { PIXEL_SOLID,PIXEL_THREEQUARTERS,PIXEL_HALF,PIXEL_QUARTER };
	RGB *dither;
	unsigned pngWidth, pngHeight;
};
int main()
{
	const char* filename = "C:/test_PNG/van_gogh.png";
	//const char* filename = "C:/test_PNG/mario2.png";
	//const char* filename = "C:/test_PNG/olc.png";
	unsigned error = lodepng::decode(vecPng, imageWidth, imageHeight, filename);
	if (error) {
		std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
		return false;
	}
	testConsole test;
	test.ConstructConsole(imageWidth, imageHeight, 4, 4);
	//	test.ConstructConsole(100, 100, 8, 8);
	test.Start();

	return 0;
}

from javidx9.

kingatgi avatar kingatgi commented on July 19, 2024

I already update the first version to increase the performance of searching the color.Hope you could do some performance comparision of these 3 versions even with the version you optimized.(because my computer can't...)
If its efficient enough I think it's an opportunity to upgrade the engine to support RGB value function(with RGB support we could do all crazy color composition stuff and output the rgb buffer layer in our code and the engine will handle the rgb buffer for display itself.)

from javidx9.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.