nx_image_load_raw.nx source
↩ module page · 118 lines · 4264 B
1// nx_image_load_raw.nx -- load real images via raw-binary header + pixels.
2//
3// The substrate bridges to real images via a SIMPLE raw format that any
4// preprocessor (PIL, ImageMagick, ffmpeg, custom script) can produce:
5//
6// bytes 0..7 width (little-endian i64)
7// bytes 8..15 height (little-endian i64)
8// bytes 16..23 channels (little-endian i64)
9// bytes 24.. raw pixel data, row-major, width * height * channels bytes
10//
11// This is the LAST-MILE primitive — once it lands, nx_color_skin_mask /
12// nx_region_segment / nx_eye_detect / etc all consume real PNG renders
13// via this conversion bridge:
14//
15// # Python preprocessing (svc-quality does this once per image):
16// import struct, PIL.Image, numpy as np
17// arr = np.array(PIL.Image.open(path).convert("RGB"))
18// h, w, c = arr.shape
19// with open("/tmp/img.raw", "wb") as f:
20// f.write(struct.pack("<QQQ", w, h, c))
21// f.write(arr.tobytes())
22//
23// # nxc2-compiled detector reads /tmp/img.raw, runs the full pipeline,
24// # writes verdict.json. svc-quality reads verdict.json, emits to UI.
25
26// nx_safety_envelope:
27// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
28// sil_target: SIL1
29// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
30// verdict: NOT_YET_EVALUATED
31
32import "nx_syscalls.nx"
33import "nx_image.nx"
34
35// Read 8 bytes (little-endian i64) starting at buf+offset.
36func nx_image_load_read_i64_le(buf: *u8, offset: i64) -> i64 {
37 let b0: i64 = buf[offset + 0]
38 let b1: i64 = buf[offset + 1]
39 let b2: i64 = buf[offset + 2]
40 let b3: i64 = buf[offset + 3]
41 let b4: i64 = buf[offset + 4]
42 let b5: i64 = buf[offset + 5]
43 let b6: i64 = buf[offset + 6]
44 let b7: i64 = buf[offset + 7]
45 return b0 + (b1 << 8) + (b2 << 16) + (b3 << 24) + (b4 << 32) + (b5 << 40) + (b6 << 48) + (b7 << 56)
46}
47
48// Load an image from a raw-binary file at the given path.
49// Returns *Image or 0-pointer on failure.
50func nx_image_load_raw(path: *u8) -> *Image {
51 let len_out: *i64 = (sys_mmap(16)) as *i64
52 let buf: *u8 = sys_read_file(path, len_out)
53 let total_len: i64 = len_out[0]
54 if total_len < 24 { return 0 as *Image }
55
56 let w: i64 = nx_image_load_read_i64_le(buf, 0)
57 let h: i64 = nx_image_load_read_i64_le(buf, 8)
58 let c: i64 = nx_image_load_read_i64_le(buf, 16)
59 if w <= 0 { return 0 as *Image }
60 if h <= 0 { return 0 as *Image }
61 if c <= 0 { return 0 as *Image }
62 let expected_pixels: i64 = w * h * c
63 if total_len < 24 + expected_pixels { return 0 as *Image }
64
65 // Allocate the Image struct, point pixels at buf+24.
66 let raw: *u8 = sys_mmap(NX_IMG_BYTES)
67 let img: *Image = raw as *Image
68 img.width = w
69 img.height = h
70 img.channels = c
71 img.stride = w * c
72 img.pixels = (buf as i64 + 24) as *u8
73 return img
74}
75
76// Pack header + pixel buffer into a single contiguous buffer. Useful
77// for round-trip tests + the inverse "save" path. Caller pre-allocates
78// out_buf of size at least 24 + w*h*c.
79func nx_image_save_raw_pack(img: *Image, out_buf: *u8) -> i64 {
80 let w: i64 = img.width
81 let h: i64 = img.height
82 let c: i64 = img.channels
83 // Write width LE.
84 out_buf[0] = (w >> 0) & 255
85 out_buf[1] = (w >> 8) & 255
86 out_buf[2] = (w >> 16) & 255
87 out_buf[3] = (w >> 24) & 255
88 out_buf[4] = (w >> 32) & 255
89 out_buf[5] = (w >> 40) & 255
90 out_buf[6] = (w >> 48) & 255
91 out_buf[7] = (w >> 56) & 255
92 // Write height LE.
93 out_buf[8] = (h >> 0) & 255
94 out_buf[9] = (h >> 8) & 255
95 out_buf[10] = (h >> 16) & 255
96 out_buf[11] = (h >> 24) & 255
97 out_buf[12] = (h >> 32) & 255
98 out_buf[13] = (h >> 40) & 255
99 out_buf[14] = (h >> 48) & 255
100 out_buf[15] = (h >> 56) & 255
101 // Write channels LE.
102 out_buf[16] = (c >> 0) & 255
103 out_buf[17] = (c >> 8) & 255
104 out_buf[18] = (c >> 16) & 255
105 out_buf[19] = (c >> 24) & 255
106 out_buf[20] = (c >> 32) & 255
107 out_buf[21] = (c >> 40) & 255
108 out_buf[22] = (c >> 48) & 255
109 out_buf[23] = (c >> 56) & 255
110 // Copy pixels.
111 let total: i64 = w * h * c
112 var i: i64 = 0
113 while i < total {
114 out_buf[24 + i] = img.pixels[i]
115 i = i + 1
116 }
117 return 24 + total
118}