Image to C array converter

As some of you know I have a 32x32 led matrix, and making graphics for it could be a tedious process. For my original 2 bitmaps I drew them using the gimp saved them as ASCII formatted pbm files and formatted them by hand into arrays, yay fun!

so moving forward I decided to make a hacky little lua script that converts *.pbm files into C arrays

goal: convert this

P1
# CREATOR: GIMP PNM Filter Version 1.1
32 32
0000000000000000000000000000000001111100100000000000010011011000000100
0010000000000000001101100000010000111000011000010111111100000100001001
0010010000010101010000010000100100111100010111111100000100001001001000
0000010101010000010000100100011100010111111100000000000000000000000101
0101010000000000000000000000011111111110000000000000000000000111010101
1000000000000000000000011111111110001110010000000000000000000000000100
0101000000000000000000000000010000011100011000110001110011000100000100
1010010100101000010010010000010010111101111001100111100100010100101000
0100000001010000001110010010011100111011100011100000000000000000000000
0000000000000000000000000000000000000000000111111111000000000000000000
0000011111111100000000000000000000000110000000000000000000000000000001
1000000000000001000000000000000111110111001100111001100110101001111100
0101001001001001010010100110000111010000010010010100101001100010010100
1001001001010001000110001111001100011001100100010001100000000000000000
00000000010000000000000000000000000000000000

into this

byte splash[32][4] = {
{B00000000, B00000000, B00000000, B00000000},
{B01111100, B10000000, B00000100, B11011000},
{B00010000, B10000000, B00000000, B11011000},
{B00010000, B11100001, B10000101, B11111100},
{B00010000, B10010010, B01000001, B01010100},
{B00010000, B10010011, B11000101, B11111100},
{B00010000, B10010010, B00000001, B01010100},
{B00010000, B10010001, B11000101, B11111100},
{B00000000, B00000000, B00000101, B01010100},
{B00000000, B00000000, B00000111, B11111110},
{B00000000, B00000000, B00000111, B01010110},
{B00000000, B00000000, B00000111, B11111110},
{B00111001, B00000000, B00000000, B00000000},
{B01000101, B00000000, B00000000, B00000000},
{B01000001, B11000110, B00110001, B11001100},
{B01000001, B00101001, B01001010, B00010010},
{B01000001, B00101111, B01111001, B10011110},
{B01000101, B00101000, B01000000, B01010000},
{B00111001, B00100111, B00111011, B10001110},
{B00000000, B00000000, B00000000, B00000000},
{B00000000, B00000000, B00000000, B00000000},
{B01111111, B11000000, B00000000, B00000000},
{B01111111, B11000000, B00000000, B00000000},
{B01100000, B00000000, B00000000, B00000000},
{B01100000, B00000000, B01000000, B00000000},
{B01111101, B11001100, B11100110, B01101010},
{B01111100, B01010010, B01001001, B01001010},
{B01100001, B11010000, B01001001, B01001010},
{B01100010, B01010010, B01001001, B01000100},
{B01100011, B11001100, B01100110, B01000100},
{B01100000, B00000000, B00000000, B00000100},
{B00000000, B00000000, B00000000, B00000000}};

without having to touch any of it

Script:
this is a lua script, so you need a lua interpreter installed, doing a google search for lua.exe (windows) the first result turns up version 5.0.2, this is or at least was the default version included with different linux distros, so this script is compatible with lua 5.0.2 or newer

Images MUST be a power of 2 in their X dimension (width must be 8, 16, 32, 64 etc), Y (height) really does not matter much, so if you made a custom 16x9 matrix this should work, the following resolutions have been tested to work

8x8
16x8
32x32
1024x1024 (and it took like 6 min on a dualcore 2.8ghz pc)

Usage:
Make an monochrome image in the gimp, save it as a pbm file and save it in ASCII format

copy the image to the same folder as script (on windows I have my script and images saved in the same folder as lua so I don't have to fiddle around with environment stuff)

edit the script, changing the file name to which ever image you want to convert, save and close

open a command line, goto the folder where everything is and run the script (lua pbm2C.lua)

and there you go

-- pbm2C.lua
-- ascii saved pbm image to arduino array converter
-- 2010 Osgeld (zlib) 
-- very basic, change filename in script, run
-- requires lua 5.0.2 or better
-- tested with pbm files generated by the gimp 
-- X (width) must be a power of 2 (8, 16, 32, etc) 
-------------------------------------------------------
----------------- FILE NAME ---------------------------
-------------------------------------------------------
filename = "splash.pbm"
-------------------------------------------------------
----------------- RUN THE SCRIPT ----------------------
----------- C:\lua\lua pbm2arduino.lua ----------------
-------------------------------------------------------

file      = io.open(filename)
shortname = string.sub(filename, 1, string.len(filename) - 4)
output    = io.open(shortname .. ".OUT", "w+")

data  = {}
image = ""
pixel = ""
byte  = ""
xSize = 0;
ySize = 0;



-- read the file into a table
for line in file:lines() do
      table.insert(data, line);
end

-- scan the 3rd line of the header to get image rez 
space = string.find(data[3], " ")
xSize = tonumber(string.sub(data[3], 0, space - 1))
ySize = tonumber(string.sub(data[3], space + 1, -1))

-- chop off the header 
for i = 1, 3 do
      table.remove(data, 1)
end

-- dump the image data into 1 large string
for i = 1, table.getn(data) do
      image = image .. data[i]
end

data = {}

-- chop up the image data into groups of 8

for i = 1, string.len(image) do      
      pixel = string.sub(image,i,i)
      
      byte = byte .. pixel
      
      if string.len(byte) == 8 then
            table.insert(data, byte)
            byte = ""
      end      
end


-- format for output
image = "byte " .. shortname .. "[" .. ySize .. "][".. (xSize /8).. "]".. " = {\n"
count = 1

for i = 1, table.getn(data), (xSize / 8) do
      image = image .. "{"
      for x = 1, (xSize / 8) do
            if x < (xSize / 8) then
                  image = image .. "B" .. tostring(data[i + (x - 1)]) .. ", "
            else
                  if (i + (xSize / 8) - 1) >= table.getn(data) then
                        image = image .. "B" .. tostring(data[i + (x - 1)]) .. "}};"
                  else 
                        image = image .. "B" .. tostring(data[i + (x - 1)]) .. "},\n"
                  end
            end
      end
end

--write file
output:write(image)

-- shutdown
io.close(file)
io.close(output)
print("done, view: " .. shortname .. ".OUT")

have fun ;D

The Gimp http://www.gimp.org/ has the capability to create C arrays out of the box. You just have to "save as" and use file extension c or h. And of course it reads almost any format :slight_smile:

Udo

The GIMP export is pretty cool, but only really works for RGB or indexed images (as far as I can tell). For Osgeld's project, it's much less wasteful of space to pack 8 pixels per byte instead of one or more bytes per pixel.

Yes, I did some simple test with an 8*8 pixel picture which should give 8 bytes. Here are the two results:

/* GIMP RGB C-Source image dump (test.c) */

#define GIMP_IMAGE_WIDTH (8)
#define GIMP_IMAGE_HEIGHT (8)
#define GIMP_IMAGE_BYTES_PER_PIXEL (3) /* 3:RGB, 4:RGBA /
#define GIMP_IMAGE_PIXEL_DATA ((guint8
) GIMP_IMAGE_pixel_data)
static const guint8 GIMP_IMAGE_pixel_data[8 * 8 * 3 + 1] =
("\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377"
"\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377"
"\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377"
"\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377"
"\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377"
"\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377"
"\377\377\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\0\0\0\0\0\0\0\0\0");

/*  GIMP header image file format (INDEXED): /home/udo/Desktop/test.h  */

static unsigned int width = 8;
static unsigned int height = 8;

/*  Call this macro repeatedly.  After each use, the pixel data can be extracted  */

#define HEADER_PIXEL(data,pixel) {\
pixel[0] = header_data_cmap[(unsigned char)data[0]][0]; \
pixel[1] = header_data_cmap[(unsigned char)data[0]][1]; \
pixel[2] = header_data_cmap[(unsigned char)data[0]][2]; \
data ++; }

static char header_data_cmap[256][3] = {
      {  0,  0,  0},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255},
      {255,255,255}
      };
static char header_data[] = {
      1,1,1,1,0,0,0,0,
      1,1,1,0,0,0,0,0,
      1,1,1,0,0,0,0,0,
      1,1,1,0,0,0,0,0,
      1,1,1,0,0,0,0,0,
      1,1,1,1,0,0,0,0,
      1,1,1,1,0,0,0,0,
      1,1,1,1,1,0,0,0
      };

Looking at the output of the LUA script both outputs of the Gimp could be coaxed into the desired format by means of a regular expression. Probably the lua approach is somewhat easier.

Udo

it was just a quickie sunday afternoon project to fill a need I had, tho I did not know the gimp will output C out of the box, least I know if i ever do a RGB matrix

heh looking through it again found a couple oddities

when I first read the file i state

table.insert(data, line);

notice the semicolon, this is perfectly valid in lua so no problem, but its also totally unnecessary, and it does appear elsewhere 3 more times in the script, so much for consistency

also half way through the script i define count = 1 and never used it, I started to, changed my mind and apparently lost it in the mess

but IMO since I have not written anything in lua for nearly a year its not that... never mind yes it is (omg i cant believe I didnt read it better :-[)

least it works, and is a little flexible for single color matrices