code wiki / (root) / nx_image_feature_extract_test.nx

nx_image_feature_extract_test.nx source

↩ module page · 81 lines · 3092 B

1// nx_image_feature_extract_test.nx -- smoke for feature-vector emitter. 2 3import "nx_syscalls.nx" 4import "nx_tier.nx" 5import "nx_image.nx" 6import "nx_image_feature_extract.nx" 7 8func main() -> nx_int { 9 // === Test 1: feature vector length is the documented constant === 10 if NX_IMAGE_FEATURE_LEN != 16 { return 1 } 11 12 // === Test 2: extract features from a uniform gray 16x16 image === 13 // Uniform image: predictable features (high color-harmony since 14 // all-gray gets the achromatic-neutral fallback; sharpness = 0; 15 // edge density very low; symmetry trivially-maximal). 16 let rgb: *Image = nx_image_alloc(16, 16, 3) 17 let gray: *Image = nx_image_alloc(16, 16, 1) 18 var y: nx_int = 0 19 while y < 16 { 20 var x: nx_int = 0 21 while x < 16 { 22 nx_image_set(rgb, x, y, 0, 128) 23 nx_image_set(rgb, x, y, 1, 128) 24 nx_image_set(rgb, x, y, 2, 128) 25 nx_image_set(gray, x, y, 0, 128) 26 x = x + 1 27 } 28 y = y + 1 29 } 30 let vec: *nx_int = (sys_mmap(NX_IMAGE_FEATURE_LEN * NX_SIZEOF_NX_INT)) as *nx_int 31 nx_image_feature_extract(rgb, gray, vec) 32 33 // === Test 3: every feature should be in [0, Q10] (substrate invariant) === 34 var k: nx_int = 0 35 while k < NX_IMAGE_FEATURE_LEN { 36 let v: nx_int = vec[k] 37 if v < 0 { return 10 + k } 38 // Special: SYM_TRANS_PERIOD can be larger than Q10 (it's a 39 // raw pixel count, not a Q10 fraction). Skip the upper bound 40 // for that axis only. 41 if k != NX_IMG_FEAT_SYM_TRANS_PERIOD { 42 if v > 1024 { return 30 + k } 43 } 44 k = k + 1 45 } 46 47 // === Test 4: uniform image's sharpness should be 0 === 48 // Sobel on uniform input = 0 everywhere -> mean magnitude = 0. 49 if vec[NX_IMG_FEAT_SHARPNESS] != 0 { return 50 } 50 51 // === Test 5: uniform image's symmetry axes should be near-max === 52 // Uniform = trivially symmetric across every axis. 53 if vec[NX_IMG_FEAT_SYM_H] < 900 { return 60 } 54 if vec[NX_IMG_FEAT_SYM_V] < 900 { return 61 } 55 if vec[NX_IMG_FEAT_SYM_DIAG] < 900 { return 62 } 56 if vec[NX_IMG_FEAT_SYM_ROT_180] < 900 { return 63 } 57 58 // === Test 6: centroid math === 59 // 2 hand-built vectors [10, 20, 30, 40, ...] and [30, 40, 50, 60, ...] 60 // Centroid should be [20, 30, 40, 50, ...]. 61 let two_vecs: *nx_int = (sys_mmap(2 * NX_IMAGE_FEATURE_LEN * NX_SIZEOF_NX_INT)) as *nx_int 62 var i: nx_int = 0 63 while i < NX_IMAGE_FEATURE_LEN { 64 two_vecs[i] = 10 + i * 10 65 two_vecs[NX_IMAGE_FEATURE_LEN + i] = 30 + i * 10 66 i = i + 1 67 } 68 let centroid: *nx_int = (sys_mmap(NX_IMAGE_FEATURE_LEN * NX_SIZEOF_NX_INT)) as *nx_int 69 nx_image_feature_centroid(two_vecs, 2, centroid) 70 var j: nx_int = 0 71 while j < NX_IMAGE_FEATURE_LEN { 72 let expected: nx_int = 20 + j * 10 73 if centroid[j] != expected { return 70 + j } 74 j = j + 1 75 } 76 77 // === Test 7: centroid with n_vectors = 0 is a no-op (return 0, no crash) === 78 if nx_image_feature_centroid(two_vecs, 0, centroid) != 0 { return 90 } 79 80 return 0 81}