abitofcode

Create data with Photoshop

A technique I used in Lingo *cough* has found its way into my mobile iOS development. It involves using Photoshop to create an array of data that the app can step through to determine a value for a given pixel. Further posts will give some examples of use but first we need to create some data.

Fig 1. Photoshop 2 pixel wide by 2 pixel high square

Fig 1. Photoshop 2 pixel wide by 2 pixel high square (zoomed)

It’ll help to know whats happening under the hood so first step is to create a 2 pixel wide by 2 pixel high image in Photoshop and use color it as follows. Colors: Black (#000000) White (#FFFFFF) Red (#FF0000) Grey (#CCCCCC). Save this as 2×2.psd (All these files are available in the download at the bottom of the post).

The PSD format that photoshop uses is not much use to us as it contains lots of data that we just don’t need, we’re going to use the Photoshop Raw format to create something we can work with. In Photoshop select ‘Save As…’ from the File menu and then Photoshop Raw from the Format dropdown, save as 2×2.raw and set the following options (I’m using CS3).

Fig 2. Raw output options

edit: The Raw format is not unique to Photoshop, Gimp for example also allows saving in a raw format.

Fig 3. Using Gimp to output Raw format files

There are lots of tools we can access from the terminal that’ll make looking into the files a lot easier. I have a hotkey setup to my terminal but you can access it easily from spotlight (Command+Space) by typing ‘terminal’. Open the folder you’ve saved the files in with finder, select the folder and ‘Copy’ using Command+c. We can now get to the folder quickly in terminal by typing;

>cd *paste*

Where *paste* is Command + V, alternatively you can drag the folder onto the terminal window. ‘cd’ is the change directory command.

Use hexdump in the terminal to have a look at what’s going on in each file, the 2×2.psd is bulky as it contains the extra information required by Photoshop. The first number on each line indicates the number of the byte that the line starts with.

>hexdump 2×2.psd | head
0000000 38 42 50 53 00 01 00 00 00 00 00 00 00 03 00 00
0000010 00 02 00 00 00 02 00 08 00 03 00 00 00 00 00 00
0000020 6c fa 38 42 49 4d 04 25 00 00 00 00 00 10 00 00
0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 38 42
0000040 49 4d 04 24 00 00 00 00 3a df 3c 3f 78 70 61 63
0000050 6b 65 74 20 62 65 67 69 6e 3d 22 ef bb bf 22 20
0000060 69 64 3d 22 57 35 4d 30 4d 70 43 65 68 69 48 7a
0000070 72 65 53 7a 4e 54 63 7a 6b 63 39 64 22 3f 3e 0a
0000080 3c 78 3a 78 6d 70 6d 65 74 61 20 78 6d 6c 6e 73
0000090 3a 78 3d 22 61 64 6f 62 65 3a 6e 73 3a 6d 65 74

I’ve had to pipe the output of hexdump through head to limit the amount of data shown.

>hexdump 2×2.raw
0000000 00 00 00 ff ff ff ff 00 00 cc cc cc
000000c

The raw file is a lot more promising and we can see the black (00 00 00), white (ff ff ff), red (ff 00 00) and grey (cc cc cc) values just follow on after each other. At the end of a hexdump we get the number of bytes in the file in hexadecimal, here it is 000000c. You can convert this to decimal using a calculator or in the terminal again.

>printf “%d\n” 0x000000c
12

This prints to the terminal (printf), in decimal notation (%d) followed by a newline (\n) the hexadecimal value (0x prefix) 0x000000c. The result is 12 bytes, a red byte, green byte and blue byte for each of our 4 pixels. If we open finder from our terminal;

>open .

we get the following;

Fig

Fig 4. Finder reports the size on disk not the file size

53k! This is the size of the file on disk, not the size of the file in bytes. Finder is reporting no difference to the size of the files, back to the terminal and the list directory contents command with the -l (long format) option.

>ls -l 2×2.raw
-rw-r–r–@ 1 abitofcode staff 12 2 Jul 02:21 2×2.raw

>ls -l 2×2.psd
-rw-r–r–@ 1 abitofcode staff 28310 2 Jul 02:20 2×2.psd

We can see here that the 2×2.raw file is returning the correct size of 12 bytes, while the psd file returns 28,310 bytes! We can check this with by converting 28310 to Hex and comparing it with the value of bytes output by hexdump.

>printf “%x\n”, 28310
6e96

>hexdump 2×2.psd | tail
0006e10 00 00 00 04 00 00 00 00 38 42 49 4d 6c 63 6c 72
0006e20 00 00 00 08 00 00 00 00 00 00 00 00 38 42 49 4d
0006e30 66 78 72 70 00 00 00 10 00 00 00 00 00 00 00 00
0006e40 00 00 00 00 00 00 00 00 00 00 ff ff ff ff 00 00
0006e50 00 ff ff cc 00 00 00 ff 00 cc 00 00 00 ff 00 cc
0006e60 00 00 00 00 38 42 49 4d 50 61 74 74 00 00 00 00
0006e70 38 42 49 4d 46 4d 73 6b 00 00 00 0c 00 00 ff ff
0006e80 00 00 00 00 00 00 00 32 00 00 00 ff ff cc 00 ff
0006e90 00 cc 00 ff 00 cc
0006e96

Currently we have 3 bytes for each pixel, each capable of holding up to 256 values, combined we can store very large numbers. This is overkill for our needs so the first step is to use only one out of every 3 bytes, the plan is to drop the green and blue bytes and only use the red.

Originally I used to save the image to greyscale before outputting to Raw hoping that values such as 0xCCCCCC and 0xABABAB would be averaged out to 0xCC and 0xAB however the grayscale conversion doesn’t do a straight average between the red, green and blue values. Grayscale conversion is discussed in more detail over here http://photo.net/digital-darkroom-forum/00R5Z7

The new approach means I can determine the value of a pixel in Photoshop by adjusting the Red value between 0×00 and 0xFF, the green and blue values can be set to anything (as they will be discarded). This has the added bonus of allowing me to use distinctive colors when laying out hit areas (this’ll make more sense when we use the data).

A quick python script and the conversion is done. Python is a language I bumble along in, please excuse any errors :)

# convert.py
# Loop through a file of raw rgb data and create a new file 
# containing only the r values.
# Author: C.Wilson - abitofcode
 
import sys, struct
 
if len(sys.argv) != 2:  
  # the program name and the filename
  # stop the program and print an error message
  sys.exit("Must provide a filename")
 
# use the value we passed in as the source filename
infile = sys.argv[1]
# prepend '_' to create an output file name
outfile = '_' + infile
# open the output file for write binary (wb)
o = open(outfile, "wb")
#openf the input file for read binary (rb)
with open(infile, "rb") as f:
    # grab the first 3 bytes from the file	
    pixel = f.read(3)
    # loop through 3 bytes at a time until complete
    while pixel:
        # split the bytes into the individual color components
        r,g,b = struct.unpack("BBB", pixel)
        #print str(r) + ':' + str(g) + ':' + str(b)
        # pack the first byte (red) so it can be written
        p = struct.pack("B",r)
        # write the byte to the output file
        o.write(p)
        # read the next 3 bytes from the file
        pixel = f.read(3)
# close the source file
f.close()
# close the output file
o.close()

Using the script is simple;

>hexdump 2×2.raw
0000000 00 00 00 ff ff ff ff 00 00 cc cc cc
000000c

>python convert.py 2×2.raw

>hexdump _2x2.raw
0000000 00 ff ff cc
0000004

This takes the input filename and prepends an underscore to it to create the output filename. We now have a file that contains only the red byte for each pixel, it can store a value between 0×00 and 0xFF (Zero and 255). In the next post I’ll show a few different ways of using this but in the meantime here’s how you can access the data.

// Remember to add the _2x2.raw to your project
 
// get the filepath to the Raw data
NSString *filePath = 
  [[NSBundle mainBundle] pathForResource:@"_2x2" ofType:@"raw"];  
// load it as NSData
NSData *lookupData = [NSData dataWithContentsOfFile:filePath];  
// If successful
if (lookupData) 
{  
    // Log it
    NSLog(@"FILE LOADED: %@", lookupData);
 
    // get the 3rd pixel from the data .
    // We start at zero, so the 3rd Byte is at position 2.
    NSRange range = {2,sizeof(Byte)};
    NSData *pixelValue = [lookupData subdataWithRange:range];   
 
    // [NSData bytes] returns a pointer (a const void *) to the 
    // first byte in a block. Cast this to an int pointer and 
    // dereference it to get the value.
    int iPixelValue = *(int*)[pixelValue bytes];
 
    // log the value
    NSLog(@"RANGE: %@ Value:%d ", pixelValue, iPixelValue);   
}

Download the files here: Create data with Photoshop

5 Comments

  1. Very nice! I can see this technique being very useful for an upcoming game of mine.

  2. Very useful info! Definitely could be useful… not sure when I’ll use it yet, but I probably will :)

    Gray

  3. So cool! Can’t wait for the next part.

  4. What’s the purpose of this? Having data in raw format. Wouldn’t .plist files be enought for this?

  5. @Ray, Gray, Dave Thanks for the positive feedback :)

    @Jose The second part should make it a bit clearer, but plists add some overhead in file size and processing time. A 2×2.plist consisting of a single array with 4 Boolean values (one for each pixel) weighed in at 181 bytes compared to the 12 bytes using the Raw approach. Not only was it bigger but it managed to store less!

    It’s also nice being able to visually manipulate your data in a graphical tool.

%d bloggers like this: