Reverse Engineering the JD11 - A drama in n chapters


What is the JD11?

The JD11 is a cheap digital camera produced by JenOptik (apparenly a subsidary of Zeiss Jena). It was at the time of writing this available for around 250 DM.

There is software available, a windows program called FotoBee which is a huge heap of MFC dung. It can be made to run with a small hack under the free Windows Emulator WINE.

WINE was crucial in reverse engineering the camera at all stages of the project.

Chapter 1:The lowlevel serial protocol

The JD11 comes with a serial cable with a DB9 connector on the computerside and a small klinkenstecker at the camera end. So just 2 lines (RX, TX) and GND.

There is no documenation on the serial protocol in the inlaying documentation.

The FotoBee.exe program uses the Windows serial communications interface, which is fortunately implemented in WINE. By snooping the setup calls, the serial parameters are:

        115200 Baud, no parity, 1 stopbit, 8 bit data, no flow control
        (neither crtscts, nor XON/XOFF)
It also setting some large timeouts, since the camera sometimes needs like 2 seconds to think about something.

That was rather trivial.

Chapter 2: The serial data transfer protocol

The next step was to find out how the FotoBee software talks to the camera.

 Luckily the program still uses the standard Win32 serial communication functions for that, so I added some code to the ReadFile and WriteFile functions, which dumps the read/written buffer if it is writing to a serial device.

In general, the program sends a command down the serial line (with optional arguments) and receives data back. Every command starts with a 0xFF character.
CommandArgumentsReturnsDescription
0xff 0x08 None 0xff 0xf1 Ping!s the camera.
0xff 0xa7 None A packet with between 10 and 20 bytes is returned. 3 float factors are returned, they are calculated:
f1=r[1]+r[2]*0.1+r[3]*0.01;
f2=r[4]+r[5]*0.1+r[6]*0.01;
f3=r[7]+r[8]*0.1+r[9]*0.01;

Their use is still unknown to me.
Twain calls it 'LoadGRB'
0xff 0xa4 None 0xff 0x01 Select Index Picture to transfer, is followed by packetreading
0xff 0xa1 0xff picturenr 0xff 0x01 Selects Image picturenr to transfer. 3 Bytestreams follow (packetreading has to be called 3 times)
0xff 0xa8 10 bytes of data ... Set floats 0xff 0x01 Sets the 3 floats. Sends 10 bytes to the camera.
The bytestream looks: 0xFF A B C D E F G H I
The floats are: A.BC D.EF G.HI
A...I are binary values between 0 and 9.
Twain calls it 'SaveGRB'.
0xff 0xa6 None No return expected. Deletes all images.
0xff 0xf0 None Returns an ASCII hexnumber (starting with two 0x66 ('f')) Returns the size (in bytes) of the image to transfer next. This command is issued after "select index" or "select image" and is followed by (multiple) packet reads.
0xff 0xf1 None Returns 201 or less bytes The packetreader. It is called in a loop after querying the imagesize. If 201 bytes are read, the 201th byte is the checksum (sum of 0-199)&0xff, otherwise there is no checksum.
Returns the 200 bytes read.
0xff 0xf3 None Returns 201 or less bytes The packet resend command. Works exactly like the 0xff 0xf1 packetread command, but the last packet is retransmitted. This is useful for corrupted transmissions.
0xff 0x72 None 0xff 0x71 The TestDRAM command according to the Twain drivers. Gets a bool reply.
0xff 0x73 None 0xff 0x70/0x71 The TestFLASH command according to the Twain drivers. Gets a bool reply.
0xff 0x75 None 0xff70 or 0xff71 or something else. The TestADC command according to the Twain drivers. Gets a bool reply.
0xff 0x78 None. Some data. Appears to take a snapshot and then it should read N packets of data. Strange. There is some text generated and printed into a dialog. I only get back 0xff.
TWAIN calls it DSC_TestAE.
0xff 0x79 None 0xff 0xXX Function unknown, seems to open/close the shutter in rapid succession.
TWAIN calls it DSC_AjustAGC.
Reads reply.
0xff 0x7a None. Some data. Unclear. GUI enables wait cursor before call and disabled it after.
0xff 0x7b 0xff value. 0xff 0xf1 Unclear.
0xff 0xa9 0xff value. 0xff 0xf1 Set Bulb Mode. Apparently values between 1 and 9.

Following commands can be used on toplevel:

For reading the INDEX picture following sequence appears: For reading the IMAGE picture following sequence appears: Thats all we needed to know about the serial commands.

Chapter 3: The image compression, stage 1

Judging from other cameras it is easy to suspect the camera uses the JPEG format. It does not unfortunately.

Chapter 3.1: The format of the index picture

Casting a closer look at the raw data of the index picture shows a a stream of 8 bit values. Some experimentation later it turns out to a stream of 64x48 grayscale pictures, upside down. Using:
	rawtopgm <index 64 (sizeof_index/64) |pnmflip -tb > index.ppm
we can convert it into a .PPM and convert it further using standard UNIX tools.

The size of the index picture also tells us the number of pictures that are currently stored in the camera. Just divide the size of the indeximage by 64*48.

Chapter 3.2: The low quality compressed image format

The camera has two modes to store pictures, either in low or high quality. This is marked by the letters "L" and "H" on the cameras LCD. Default is the low quality format.

It is still unclear to me how to detect high/low quality formats, except with the size of the returned image.

At first look the data returned from the camera looks like junknoise. So I had to peek into the diassembly, which shows a slightly inefficient huffman decompressor, with following bitpatterns:
BitsValue
00 -2
01 6
10 2
110 6
1110 11
1111 0 -11
1111 10 20
1111 110 -20
1111 1110 45
1111 1111 0 -45
1111 1111 10 90
1111 1111 110 -90
1111 1111 1110 180
1111 1111 1111 0 -180
The result of this decompression run is 320*480 bytes for the first image, and 320*240 for the second and third image. But these are not the pixel values, these are differences!
So I had another look. Pixels are 8bit unsigned values and computed like this:

Tada. We now have 3 uncompressed images. To view each of them we can use:
	rawtopgm 320 480 <image0 | pnmflip -tb > image0.ppm
	rawtopgm 320 240 <image1 | pnmflip -tb > image1.ppm
	rawtopgm 320 240 <image2 | pnmflip -tb > image2.ppm
where image0 is the first image read, and image1,image2 the next two images.

Both give pretty grayscale pictures, but are not yet in color.

Chapter 3.2: The high quality compressed image format

The high quality format uses a very different compression.

It just compacts all 8bit values to 6bit by shortening out the 2 least significant bits. So we just restore them (AAAA AABB BBBB CCCC CCDD DDDD -> AAAAAA00, BBBBBB00, CCCCCC00, DDDDDD00) and get the same grayscale images as with the lowquality compression.

Chapter 3.3: Getting colors

Now we have the green, the red and the blue components, with the green having twice as much rows as the others.
We can apply the bayer algorithm now and get the final 640x480 image.