-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimage_processing.zig
More file actions
106 lines (90 loc) · 4.21 KB
/
image_processing.zig
File metadata and controls
106 lines (90 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! Zigen Example: Image Compression via SVD
//! Low-rank approximation of an 8x8 grayscale image block.
const std = @import("std");
const Zigen = @import("zigen");
const Mat8 = Zigen.Matrix(f32, 8, 8);
pub fn main() !void {
std.debug.print(
\\
\\ ╔═══════════════════════════════════════════════╗
\\ ║ Zigen — Image Compression via SVD ║
\\ ╚═══════════════════════════════════════════════╝
\\
\\
, .{});
// ── 8x8 grayscale block (gradient + circle pattern) ───────────────
const img = Mat8.fromArray(.{
.{ 140, 144, 147, 150, 154, 158, 162, 163 },
.{ 148, 155, 169, 175, 178, 180, 174, 165 },
.{ 150, 163, 189, 210, 215, 212, 197, 172 },
.{ 152, 170, 205, 235, 242, 234, 210, 178 },
.{ 152, 170, 205, 235, 242, 234, 210, 178 },
.{ 150, 163, 189, 210, 215, 212, 197, 172 },
.{ 148, 155, 169, 175, 178, 180, 174, 165 },
.{ 140, 144, 147, 150, 154, 158, 162, 163 },
});
std.debug.print("━━ Original 8x8 Image Block ━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n", .{});
printImage(img);
// ── SVD ───────────────────────────────────────────────────────────
std.debug.print("━━ SVD Decomposition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n", .{});
const svd = Zigen.SVD(f32, 8, 8).compute(img);
std.debug.print(" Singular values:\n ", .{});
for (0..8) |i| {
std.debug.print("s{d}={d:>8.2} ", .{ i + 1, svd.S.at(i) });
if (i == 3) std.debug.print("\n ", .{});
}
std.debug.print("\n\n", .{});
// ── Low-rank approximations ───────────────────────────────────────
const ranks = [_]usize{ 1, 2, 4 };
for (ranks) |k| {
const storage = k * (8 + 8 + 1);
std.debug.print("━━ Rank-{d} Approximation ({d} values, {d:.0}% of 64) ━━\n\n", .{
k, storage, @as(f32, @floatFromInt(storage)) / 64.0 * 100.0,
});
var approx = Mat8.zero();
for (0..k) |i| {
const sigma = svd.S.at(i);
const u_col = svd.U.col(i);
const v_col = svd.V.col(i);
for (0..8) |r| {
for (0..8) |c| {
approx.data[r][c] += sigma * u_col.at(r) * v_col.at(c);
}
}
}
printImage(approx);
const err = img.sub(approx);
const rmse = err.norm() / 8.0;
std.debug.print(" RMSE = {d:.2}\n\n", .{rmse});
}
// ── Summary ───────────────────────────────────────────────────────
std.debug.print("━━ Compression Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n", .{});
std.debug.print(" Rank | Storage | Ratio\n", .{});
std.debug.print(" Full | 64 | 1.00x\n", .{});
for (ranks) |k| {
const storage = k * (8 + 8 + 1);
std.debug.print(" {d} | {d:>2} | {d:.2}x\n", .{
k, storage, 64.0 / @as(f32, @floatFromInt(storage)),
});
}
std.debug.print("\nDone!\n", .{});
}
fn printImage(m: Mat8) void {
for (0..8) |r| {
std.debug.print(" ", .{});
for (0..8) |c| {
const val = @max(0.0, @min(255.0, m.at(r, c)));
if (val < 64) {
std.debug.print(" .", .{});
} else if (val < 128) {
std.debug.print(" o", .{});
} else if (val < 192) {
std.debug.print(" #", .{});
} else {
std.debug.print(" @", .{});
}
}
std.debug.print("\n", .{});
}
std.debug.print("\n", .{});
}