PPM files map U/V coordinates from flattened surfaces back to the original 3D volume coordinates x/y/z and also provide a normal for every point.
The file has a small header and is otherwise a huge array of double
values (in the common case,
but see header).
One key/value pair per line. Lines are separated by \n
, key and value by ": ".
E.g.
width: 8882
height: 3476
dim: 6
ordered: true
type: double
version: 1
<>
After reading "<>\n"
the array begins immediately. In the common case,
for each point in a flattened surface u/v, 6 little endian double
values
can be read sequentially, x
/y
/z
, and n_x
/n_y
/n_z
.
C-struct similar to this:
struct Mapping {
double x, y, z
double n_x, n_y, n_z;
};
It is possible to memory map the file after the header and access the data directly. E.g.
void *ptr = // mmap ...
Mapping *mapping = (Mapping*) ptr;
// get mapping for a certain point u/v
// int u, v;
Mappping *uvMapping = mapping[v * width + u];
int x = uvMapping->x;
// ...
E.g. to get the x-value for a certain u/v, you can find the offset like this:
int offset = 73; // in the example below, depends on actual header
// int u, v;
long offset_x_uv = offset + 48 * (v * width + u);
long offset_y_uv = offset + 48 * (v * width + u) + 8;
long offset_z_uv = offset + 48 * (v * width + u) + 16;
// ...
00000000 77 69 64 74 68 3a 20 38 38 38 32 0a 68 65 69 67 |width: 8882.heig|
00000010 68 74 3a 20 33 34 37 36 0a 64 69 6d 3a 20 36 0a |ht: 3476.dim: 6.|
00000020 6f 72 64 65 72 65 64 3a 20 74 72 75 65 0a 74 79 |ordered: true.ty|
00000030 70 65 3a 20 64 6f 75 62 6c 65 0a 76 65 72 73 69 |pe: double.versi|
00000040 6f 6e 3a 20 31 0a 3c 3e 0a 00 00 00 00 00 00 00 |on: 1.<>........|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000842e0 00 00 00 00 00 00 00 00 00 96 4c 4a 68 85 6c aa |..........LJh.l.|
000842f0 40 76 6b ea d7 38 4f a7 40 5c 3e d1 b0 bc 2d c2 |@vk..8O.@\>...-.|
00084300 40 0f 49 73 a2 33 ca c0 3f 88 bc fe 2e 57 42 ef |@.Is.3..?....WB.|
00084310 bf d5 e2 3f f8 de a1 c5 3f c0 0c 48 78 79 6e aa |...?....?..Hxyn.|
00084320 40 12 90 60 f3 80 4f a7 40 84 0c 2f 86 c3 2d c2 |@..`..O.@../..-.|
00084330 40 73 71 fa 07 94 b0 c0 3f fe d9 37 7c a0 40 ef |@sq.....?..7|.@.|
00084340 bf 00 29 dd f7 ff dc c5 3f e9 cc 45 88 6d 70 aa |..).....?..E.mp.|
00084350 40 ad b4 d6 0e c9 4f a7 40 ac da 8c 5b ca 2d c2 |@.....O.@...[.-.|
00084360 40 c0 72 67 3e 19 8c c0 3f bb 60 81 9b 22 3e ef |@.rg>...?.`..">.|
00084370 bf b9 22 73 91 13 31 c6 3f 12 8d 43 98 61 72 aa |.."s..1.?..C.ar.|
00084380 40 48 d9 4c 2a 11 50 a7 40 d3 a8 ea 30 d1 2d c2 |@H.L*[email protected].|
00084390 40 76 bd 99 cd 02 54 c0 3f 43 f8 d5 73 2f 3a ef |@v....T.?C..s/:.|
000843a0 bf 37 ea e6 4b 1b b2 c6 3f 3a 4d 41 a8 55 74 aa |.7..K...?:MA.Ut.|
000843b0 40 e4 fd c2 45 59 50 a7 40 fb 76 48 06 d8 2d c2 |@[email protected].|
000843c0 40 f8 f0 e7 cc 68 e5 bf 3f 94 11 73 2d fe 32 ef |@....h..?..s-.2.|
000843d0 bf 5d fd 80 8f 47 91 c7 3f 64 0d 3f b8 49 76 aa |.]...G..?d.?.Iv.|
000843e0 40 80 22 39 61 a1 50 a7 40 22 45 a6 db de 2d c2 |@."9a.P.@"E...-.|
000843f0 40 3b 71 38 b3 d7 40 be 3f 9b fd d4 45 fd 21 ef |@;q8..@.?...E.!.|
Data for neighbors is highly correlated, so the data could be serialized much more efficiently. If you assume quantizing to integer x/y/z is acceptable, neighboring mappings differ in most cases (e.g. > 97% in one example I tested) by 0, 1, or -1. Since there's usually a prevalent direction, one of 1 and -1 will be much more likely.
E.g. in this example this is the distribution of diffs of neighboring points in a ppm:
Total points 2068x4102: 8482936 uvs
dxs
0: 7040446 83.00%
-1: 1187540 14.00%
1: 244761 2.89%
-2: 1230 0.01%
2: 558 0.01%
3430: 57 0.00%
3431: 49 0.00%
-3173: 47 0.00%
-3171: 47 0.00%
-3172: 45 0.00%
...
If you calculate the entropy, you get a value of ~ 1.2 bits per pixel. That's only achievable with an optimal entropy coder, but even a simple(Huffman) scheme such as
dx | Code | Num bits |
---|---|---|
0 | 0 | 1 |
-1 | 10 | 2 |
1 | 110 | 3 |
rest | 111 + 16 bit full value | 19 |
will be able to reap most of the benefits (in the above example this leads to 1.22 bits per pixel, down from 64 bit double, a compression factor of ~50x). This compression will be lossy due to the quantization to integer values.