Convert image to the map

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
User avatar
darkfrei
Party member
Posts: 1169
Joined: Sat Feb 08, 2020 11:09 pm

Convert image to the map

Post by darkfrei »

Hi all!

For example you have an image as:
2023-03-11T14_47_07-image.png - paint.net v4.2.1.png
2023-03-11T14_47_07-image.png - paint.net v4.2.1.png (22.5 KiB) Viewed 2192 times
and you want to make a file, where every pixel has a value from 1 to N, where the N is amount of colors in this image.
The picture above has 9 colors:

Code: Select all

1	#ffffff00
2	#bf4c26ff
3	#ff8c66ff
4	#66007fff
5	#a53fbfff
6	#4ca53fff
7	#8ce57fff
8	#ffffffff
9	#000000ff
And if you write the color as indices then the table will be:

Code: Select all

{
	{8, 3, 2, 3, 3, 2, 2, 2, 4, 5, 5, 4, 3, 3, 2, 7, 7, 6, 7, 7, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5},
	{9, 5, 5, 6, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, 7, 6, 3, 3, 3, 3, 3, 2, 3, 3, 2, 3, 3, 3, 3, 7, 7},
	{7, 7, 7, 7, 6, 6, 5, 4, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 7, 6, 7, 7, 6, 6, 7},
	{2, 3, 7, 7, 7, 6, 7, 6, 7, 7, 7, 6, 6, 5, 3, 3, 2, 5, 3, 2, 3, 3, 2, 5, 5, 4, 3, 7, 7, 7, 7},
	{7, 6, 6, 6, 7, 6, 7, 7, 2, 3, 3, 5, 7, 7, 2, 2, 3, 3, 3, 3, 7, 7, 7, 7, 6, 7, 6, 6, 7, 7, 7},
	{5, 5, 5, 5, 5, 7, 3, 5, 5, 5, 4, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 7, 7, 6, 2, 3, 3},
	{2, 2, 4, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 7, 7, 7, 4, 5, 4, 5, 2, 2, 7, 7, 3},
	{3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 2, 3, 2, 3, 3, 7, 6, 6, 6, 3, 3, 3, 2, 2, 3},
	{3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, 2, 2, 3, 2, 3, 3, 2, 2, 3, 2, 2, 3, 3, 3, 3, 3, 3},
	{2, 3, 3, 3, 2, 3, 3, 2, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 4, 3, 3, 3, 2, 3, 3, 2, 2, 3, 2},
	{3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
	{2, 5, 5, 5, 4, 3, 2, 2, 3, 3, 3, 3, 5, 5, 5, 5, 5, 4, 5, 4, 5, 5, 7, 2, 3, 4, 5, 5, 4, 5, 5},
	{5, 5, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3, 6, 3, 3, 3, 3, 3, 3, 2, 2, 3, 4, 5, 5},
	{5, 5, 4, 4, 5, 4, 5, 7, 7, 6, 7, 7, 6, 7, 6, 7, 5, 5, 5, 5, 5, 4, 5, 5, 4, 5, 5, 4, 5, 5, 7},
	{7, 6, 6, 6, 7, 7, 7, 7, 7, 5, 5, 4, 5, 3, 3, 2, 3, 2, 7, 7, 6, 4, 5, 5, 5, 4, 4, 5, 4, 5, 4},
	{7, 3, 3, 3, 3, 3, 3, 3, 7, 7, 6, 7, 7, 7, 6, 7, 7, 6, 7, 7, 7, 6, 7, 7, 7, 6, 7, 7, 7, 7, 6},
	{2, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 2, 2, 3, 3, 2, 3, 3, 3},
	{2, 3, 3, 3, 3, 4, 5, 5, 5, 4, 4, 5, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5},
	{5, 5, 5, 5, 5, 6, 5, 5, 4, 5, 5, 7, 7, 6, 7, 7, 7, 7, 7, 7, 6, 7, 7, 6, 7, 7, 7, 7, 7, 7, 6},
	{7, 7, 7, 6, 4, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 3, 3, 3, 2, 5, 5, 7, 6, 6, 6, 7, 7},
	{6, 7, 7, 6, 7, 3, 2, 2, 7, 7, 7, 7, 6, 7, 6, 6, 7, 7, 7, 7, 7, 7, 6, 5, 4, 5, 5, 6, 7, 6, 7},
	{7, 7, 6, 6, 7, 7, 7, 6, 7, 7, 6, 6, 7, 7, 6, 7, 5, 5, 5, 4, 5, 5, 5, 4, 5, 4, 3, 3, 3, 2, 2},
	{3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 7, 6, 4},
	{5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 3, 2, 3, 3, 3, 3, 3, 6, 7, 6, 6, 6, 6, 7, 5, 5, 4, 5, 4, 5},
	{5, 4, 7, 7, 7, 6, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 3, 3, 2, 3, 3, 3, 2, 3, 3},
	{3, 3, 3, 2, 3, 3, 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 2, 3, 5, 3, 2, 3, 5, 5, 5, 7, 7, 6, 7, 7, 7},
	{7, 7, 7, 3, 7, 6, 6, 7, 7, 7, 7, 7, 7, 6, 7, 6, 7, 7, 7, 3, 3, 3, 2, 3, 6, 3, 3, 2, 3, 2, 3},
	{3, 3, 3, 3, 2, 3, 5, 5, 5, 5, 4, 5, 5, 7, 3, 3, 2, 5, 5, 5, 5, 5, 4, 7, 6, 7, 7, 7, 7, 7, 7},
	{7, 6, 6, 7, 7, 3, 2, 3, 3, 2, 2, 2, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 6, 7, 7, 7, 6, 7, 7, 7},
	{7, 4, 5, 5, 7, 7, 7, 5, 5, 2, 3, 3, 3, 2, 3, 2, 7, 7, 6, 6, 3, 3, 3, 3, 4, 5, 5, 5, 4, 7, 7},
	{7, 6, 6, 7, 4, 5, 4, 5, 5, 5, 5, 5, 2, 3, 3, 7, 6, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7},
}
Here you can see that from the image 33x33 pixels we are get the table 32x32 items, it's while we have the first line (internally the row number 0) as palette, where we define colors before reading the other lines.

Code: Select all

 -- image-to-map.lua
local function hex (number)
	return string.format("%02x", number)
end

local function hashColor (r, g, b, a)
	return hex (r) .. hex (g) .. hex (b) .. hex (a or 255)
end

local function addToPalette (paletteHash, hashIndex, palette, r, g, b, a)
	-- the color is not known before:
	local color = {r, g, b, a}
	table.insert(palette, color)
	local colorIndex = #palette
	paletteHash[hashIndex] = colorIndex
	return colorIndex
end

local function createPalette (imageData, paletteHash, palette)
	local width = imageData:getWidth()-1
	-- read the whole line for all colors:
	for x = 0, width do
		-- [r g b a] as color [0..1]
		local r, g, b, a = imageData:getPixel(x, 0)
		-- [rb gb bb ab] as byte color [0..255]
		local rb, gb, bb, ab = love.math.colorToBytes( r, g, b, a )
		-- just unique value for this color:
		local hashIndex = hashColor (rb, gb, bb, ab)
		
		if not paletteHash[hashIndex] then
			addToPalette (paletteHash, hashIndex, palette, r, g, b, a)
		end
	end
	print ('amount of colors in defined palette:', #palette)
end

local function imageToMap (filename, withPalette)
	local imageData = love.image.newImageData(filename)
	
	-- first is not value, but palette:
	local width = imageData:getWidth()
	local height = imageData:getHeight()
	
	-- a list of colors:
	local palette = {}
	
	-- hash table of colors
	local paletteHash = {}
	
	-- the result table as map[y][x]
	local map = {}
	
	-- value to start the imageData reading:
	local firstPosition = 0
	
	if withPalette then
		-- skip the 0th line
		firstPosition = 1
		
		-- the first line is a palette
		width, height = width-1, height-1

		-- fill the palette with first line
		-- except the pixel (0, 0)
		createPalette (imageData, paletteHash, palette)
	end
	print ('width:', width, 'height:', height)
	
	print ('y:', 1, height-firstPosition)
	print ('x:', 1, width-firstPosition)
	
	for y = 1, height do
		map[y] = {}
		local y0 = y-1+firstPosition -- 0-based position
		
		for x = 1, width do
			local x0 = x-1+firstPosition -- 0-based position
			-- [r g b a] as color [0..1]:
			local r, g, b, a = imageData:getPixel(x0, y0)
			-- [rb gb bb ab] as byte color [0..255]:
			local rb, gb, bb, ab = love.math.colorToBytes( r, g, b, a )
			-- just unique value for this color:
			local hashIndex = hashColor (rb, gb, bb, ab)
			
			if paletteHash[hashIndex] then
				-- taking color index from hash table:
				local colorIndex = paletteHash[hashIndex]
				-- write the number to the map:
				map[y][x] = colorIndex
			else
				if withPalette then
					print ('color '..hashIndex..' was not defined in the palette')
				end
				local colorIndex = addToPalette (paletteHash, hashIndex, palette, r, g, b, a)
				-- write the number to the map:
				map[y][x] = colorIndex
			end
		end
	end
	
	if withPalette then
		print ('Extended palette:' .. #palette .. ' colors')
	else
		print ('Palette: ' .. #palette .. ' colors')
	end
	for i, color in ipairs (palette) do
		print (i, '#'.. hex(color[1]*255) .. hex(color[2]*255) .. hex(color[3]*255) .. hex(color[4]*255))
	end
	
	print ('{ -- rows:'.. #map,'cols: ' .. #map[1])
	for y = 1, #map do
		print ('	{'..table.concat (map[y], ', ') .. '},')
	end
	print ('}')
	return map, palette
end

return imageToMap
Such tool can be useful to have level maps as a pictures like this:
two-colors-map.png
two-colors-map.png (7.22 KiB) Viewed 2192 times
Attachments
image-to-map-01.love
(2.8 KiB) Downloaded 114 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
darkfrei
Party member
Posts: 1169
Joined: Sat Feb 08, 2020 11:09 pm

Re: Convert image to the map

Post by darkfrei »

Updated version:

It takes picture (without palette) and creates the text file filled with 1 or 2 in .lua format.

Also it uses tiles16 system with tile variations to create the map with spites (it uses quads):

Code: Select all

local variations = { -- 16 variations for tiles16
	{0,0,0,0}, -- nothing around it
	{1,0,0,0}, -- one tile above
	{0,1,0,0}, -- one tile right
	{1,1,0,0}, -- tiles: right and above
	{0,0,1,0}, -- one tile below
	{1,0,1,0}, -- tiles: below, above
	{0,1,1,0}, -- tiles: below, right
	{1,1,1,0}, -- tiles: below, right, above
	{0,0,0,1}, -- one tile left
	{1,0,0,1}, -- tiles: left, above
	{0,1,0,1}, -- tiles: left, right
	{1,1,0,1}, -- tiles: left, rigt, above
	{0,0,1,1}, -- tiles: left, below
	{1,0,1,1}, -- tiles: left, below, above
	{0,1,1,1}, -- tiles: left, below, right
	{1,1,1,1}, -- tiles: left, below, right, above
}

-- where 
local neighbors = { -- bits for neighbors
	{x=0, y=-1}, -- 1
	{x=1, y=0}, -- 2
	{x=0, y=1}, -- 4
	{x=-1, y=0}, -- 8
}
tiles16-16x16.png
tiles16-16x16.png (659 Bytes) Viewed 2098 times
The result picture:
image-to-map-02.png
image-to-map-02.png (34.29 KiB) Viewed 2098 times
https://github.com/darkfrei/love2d-lua- ... age-to-map
Attachments
image-to-map-02.love
(5.28 KiB) Downloaded 105 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
pauljessup
Party member
Posts: 355
Joined: Wed Jul 03, 2013 4:06 am

Re: Convert image to the map

Post by pauljessup »

Hey, that's really neat!
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 52 guests