[c+ai] one night with image codec
From
fir@profesor.fir@gmail.com to
comp.lang.c on Thu Sep 24 15:36:39 2026
From Newsgroup: comp.lang.c
i once was writing on this -
the idea is take bitmap (like 960x540) take some pixels of it
(in practice i take liek 2% maybe 5% random pixels store it as a file
(new image codec) and interpolate it to recreate the image
i just wonder how it would work..
it was a bit to tedious to write it alone but in present times you just
say it to chat gpt and it practically write it to yourself
the technical quest is here mainly
1) what interpolation function tu use (ai write me some im not sure how
good it is)
2) optimiser function - it just generates various pixels interpolate and compare it to oryginal bitmap searching for least error among
interpolated and oryginal
3) additionally if i store this 2% pixels in disk file there is
also option to store it in most 'packed' way..right now i just
used 3 butes for storing xy of pixels (12 bits for x and 12 bits y)
and 3 bytes for rgb color - so on pixel its 6 bytes
generally t showed to work like i take about 2 MB source bitmap
and result is about 180kb -250kb result that in fact is not such bad
(i could post pices of images how it work but later)
its notably worse than jpg of its size but still its quite ok
almost whole code here was simply generated by ai..and i post it below
it is in some kind interesting topic...so if someone want to comment on
that may commnet on tah if no may just ignore (the group has not so
many topics to comment so i dont hink is harm such post on this)
(note i compile in c++ mode as i dont ike use #defines i prefer use
const int from c++, also i use my own green fire library to setup
windowm with frame bitmap i cant set pixels to it and draw things on
client area of window)
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#include <windows.h>
#include <Mmsystem.h>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include <stdint.h>
#include "green-fire.h"
const int GRID_SIZE = 5;
const int INTERP_K = 3;
const int MIN_POINTS_PER_GRID = 1;
const int MAX_POINTS_PER_GRID = 10;
const int TRIALS = 100; //50
const int OPT_PASSES = 5; //10
typedef struct {
short x, y;
unsigned color;
} PointColor;
typedef struct {
unsigned *pixels;
int w, h, stride;
} Bitmap;
typedef struct {
int *p;
int n;
} GridCell;
static GridCell *grid;
static int grid_w, grid_h;
int LoadBitmap(const char *filename, Bitmap *b)
{
HBITMAP bmp = (HBITMAP)LoadImageA(
0, filename, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (!bmp)
{
ERROR_("LoadImageA failed");
return 0;
}
DIBSECTION ds;
if (!GetObject(bmp, sizeof(ds), &ds))
{
ERROR_("GetObject failed");
DeleteObject(bmp);
return 0;
}
int bpp = ds.dsBm.bmBitsPixel;
if (bpp != 24 && bpp != 32)
{
ERROR_("bitmap must be 24 or 32 bit");
DeleteObject(bmp);
return 0;
}
b->w = ds.dsBm.bmWidth;
b->h = ds.dsBm.bmHeight;
b->stride = b->w;
b->pixels = (unsigned*)malloc(
b->w * b->h * sizeof(unsigned));
if (!b->pixels)
{
DeleteObject(bmp);
return 0;
}
unsigned char *src =
(unsigned char*)ds.dsBm.bmBits;
int src_stride = ds.dsBm.bmWidthBytes;
int top_down = ds.dsBmih.biHeight < 0;
for (int y = 0; y < b->h; y++)
{
int sy = top_down ? y : b->h - 1 - y;
unsigned char *row =
src + sy * src_stride;
for (int x = 0; x < b->w; x++)
{
unsigned char *p =
row + x * (bpp / 8);
// BMP stores B,G,R,(A)
b->pixels[y * b->w + x] =
p[0] | (p[1] << 8) | (p[2] << 16);
}
}
DeleteObject(bmp);
return 1;
}
static int ColorError(unsigned a, unsigned b)
{
Color ca, cb;
ca.u = a;
cb.u = b;
int dr = ca.r - cb.r;
int dg = ca.g - cb.g;
int db = ca.b - cb.b;
return dr * dr +
dg * dg +
db * db;
}
/*
* Lokalna "z+eo++ono+c-c" grida.
*
* Liczymy r||++nice pomi-Odzy s-asiednimi
* pikselami. Jednolity obszar daje ma+ey
* wynik, kraw-Odzie i tekst du++y.
*/
static long long GridComplexity(
unsigned *src,
int stride,
int gx,
int gy)
{
const int x0 = gx * GRID_SIZE;
const int y0 = gy * GRID_SIZE;
int x1 = x0 + GRID_SIZE;
int y1 = y0 + GRID_SIZE;
if (x1 > frame_size_x)
x1 = frame_size_x;
if (y1 > frame_size_y)
y1 = frame_size_y;
long long e = 0;
for (int y = y0; y < y1; y++) {
for (int x = x0; x < x1; x++) {
if (x + 1 < x1)
e += ColorError(
src[y * stride + x],
src[y * stride + x + 1]);
if (y + 1 < y1)
e += ColorError(
src[y * stride + x],
src[(y + 1) * stride + x]);
}
}
return e;
}
/*
* Na podstawie ca+eego obrazu ustalamy
* progi dla 1..MAX_POINTS_PER_GRID punkt||w.
*
* U++ywamy min/max z+eo++ono+cci grid||w.
*/
static int *BuildPointCounts(
unsigned *src,
int stride)
{
const int grid_n =
grid_w * grid_h;
long long *complexity =
(long long*)malloc(
grid_n * sizeof(long long));
long long min_c = 0;
long long max_c = 0;
for (int gy = 0; gy < grid_h; gy++)
for (int gx = 0; gx < grid_w; gx++) {
const int i =
gy * grid_w + gx;
complexity[i] =
GridComplexity(
src,
stride,
gx,
gy);
if (!i ||
complexity[i] < min_c)
min_c = complexity[i];
if (!i ||
complexity[i] > max_c)
max_c = complexity[i];
}
int *counts =
(int*)malloc(
grid_n * sizeof(int));
/*
* Je++eli ca+ey obraz jest prawie jednolity,
* nie ma sensu robi-c wielu punkt||w.
*/
if (max_c == min_c) {
for (int i = 0; i < grid_n; i++)
counts[i] =
MIN_POINTS_PER_GRID;
free(complexity);
return counts;
}
/*
* Logarytmiczna skala daje wi-Ocej punkt||w
* obszarom z naprawd-O du++-a zmienno+cci-a,
* zamiast rozdziela-c punkty liniowo.
*
* Tu u++ywamy prostego przybli++enia:
*
* normalized = (c-min)/(max-min)
*
* a potem pot-Ogowanie przez 0.5,
* czyli sqrt(normalized).
*/
const double range =
(double)(max_c - min_c);
for (int i = 0; i < grid_n; i++) {
double t =
(complexity[i] - min_c) /
range;
t = sqrt(t);
int n =
MIN_POINTS_PER_GRID +
(int)(
t *
(MAX_POINTS_PER_GRID -
MIN_POINTS_PER_GRID));
if (n < MIN_POINTS_PER_GRID)
n = MIN_POINTS_PER_GRID;
if (n > MAX_POINTS_PER_GRID)
n = MAX_POINTS_PER_GRID;
counts[i] = n;
}
free(complexity);
return counts;
}
/*
* Losujemy pocz-atkowe punkty,
* ale liczba punkt||w jest adaptacyjna.
*
* points_count[i] m||wi ile punkt||w
* ma grid i.
*/
static int RandomSamplesAdaptive(
PointColor *p,
int *points_count,
Bitmap *b)
{
grid_w =
(b->w + GRID_SIZE - 1) /
GRID_SIZE;
grid_h =
(b->h + GRID_SIZE - 1) /
GRID_SIZE;
int k = 0;
for (int gy = 0; gy < grid_h; gy++)
for (int gx = 0; gx < grid_w; gx++) {
const int gi =
gy * grid_w + gx;
const int x0 =
gx * GRID_SIZE;
const int y0 =
gy * GRID_SIZE;
int x1 =
x0 + GRID_SIZE;
int y1 =
y0 + GRID_SIZE;
if (x1 > b->w)
x1 = b->w;
if (y1 > b->h)
y1 = b->h;
for (int j = 0;
j < points_count[gi];
j++) {
int x =
x0 + rand() % (x1 - x0);
int y =
y0 + rand() % (y1 - y0);
p[k].x = (short)x;
p[k].y = (short)y;
p[k].color =
b->pixels[
y * b->stride + x];
k++;
}
}
return k;
}
static unsigned InterpolatePixel(PointColor *points, int x, int y)
{
const int gx = x / GRID_SIZE;
const int gy = y / GRID_SIZE;
int kd[INTERP_K];
int kdist[INTERP_K];
int kn = 0;
for (int cy = gy - 1; cy <= gy + 1; cy++) {
if (cy < 0 || cy >= grid_h) continue;
for (int cx = gx - 1; cx <= gx + 1; cx++) {
if (cx < 0 || cx >= grid_w) continue;
GridCell *c = &grid[cy * grid_w + cx];
for (int j = 0; j < c->n; j++) {
const int pi = c->p[j];
const int dx = points[pi].x - x;
const int dy = points[pi].y - y;
const int d2 = dx * dx + dy * dy;
if (!d2) return points[pi].color;
if (kn < INTERP_K) {
int k = kn++;
while (k && d2 < kdist[k - 1]) {
kdist[k] = kdist[k - 1];
kd[k] = kd[k - 1];
k--;
}
kdist[k] = d2;
kd[k] = pi;
}
else if (d2 < kdist[INTERP_K - 1]) {
int k = INTERP_K - 1;
while (k && d2 < kdist[k - 1]) {
kdist[k] = kdist[k - 1];
kd[k] = kd[k - 1];
k--;
}
kdist[k] = d2;
kd[k] = pi;
}
}
}
}
if (!kn) return 0;
double sr = 0, sg = 0, sb = 0, sw = 0;
for (int i = 0; i < kn; i++) {
Color c;
c.u = points[kd[i]].color;
// const double w = 1.0 / kdist[i];
const double w = 1.0 / (double)(kdist[i] * kdist[i]);
// const double w = exp(-kdist[i] / 50.0);
sr += c.r * w;
sg += c.g * w;
sb += c.b * w;
sw += w;
}
Color c;
c.r = (unsigned char)(sr / sw + 0.5);
c.g = (unsigned char)(sg / sw + 0.5);
c.b = (unsigned char)(sb / sw + 0.5);
c.a = 0;
return c.u;
}
static long long GridError(
PointColor *points,
unsigned *src,
int src_stride,
int gx,
int gy)
{
const int x0 =
gx * GRID_SIZE;
const int y0 =
gy * GRID_SIZE;
int x1 =
x0 + GRID_SIZE;
int y1 =
y0 + GRID_SIZE;
if (x1 > frame_size_x)
x1 = frame_size_x;
if (y1 > frame_size_y)
y1 = frame_size_y;
long long error = 0;
for (int y = y0; y < y1; y++)
for (int x = x0; x < x1; x++)
error += ColorError(
InterpolatePixel(
points,
x,
y),
src[y * src_stride + x]);
return error;
}
void OptimizeGridSamples(
PointColor *points,
int n,
int *points_count,
unsigned *src,
int src_stride)
{
grid_w =
(frame_size_x + GRID_SIZE - 1) /
GRID_SIZE;
grid_h =
(frame_size_y + GRID_SIZE - 1) /
GRID_SIZE;
const int grid_n =
grid_w * grid_h;
grid =
(GridCell*)calloc(
grid_n,
sizeof(GridCell));
/*
* Poniewa++ liczba punkt||w w gridzie
* jest ju++ ustalona, mo++emy od razu
* zaalokowa-c dok+eadne tablice.
*/
int k = 0;
for (int i = 0; i < grid_n; i++) {
const int count =
points_count[i];
grid[i].n = count;
grid[i].p =
(int*)malloc(
count * sizeof(int));
for (int j = 0; j < count; j++)
grid[i].p[j] = k++;
}
for (int pass = 0;
pass < OPT_PASSES;
pass++) {
for (int gy = 0;
gy < grid_h;
gy++) {
for (int gx = 0;
gx < grid_w;
gx++) {
GridCell *cell =
&grid[
gy * grid_w + gx];
const int count =
cell->n;
PointColor *best =
(PointColor*)malloc(
count *
sizeof(PointColor));
for (int i = 0;
i < count;
i++)
best[i] =
points[cell->p[i]];
long long best_error =
GridError(
points,
src,
src_stride,
gx,
gy);
const int x0 =
gx * GRID_SIZE;
const int y0 =
gy * GRID_SIZE;
int x1 =
x0 + GRID_SIZE;
int y1 =
y0 + GRID_SIZE;
if (x1 > frame_size_x)
x1 = frame_size_x;
if (y1 > frame_size_y)
y1 = frame_size_y;
for (int trial = 0;
trial < TRIALS;
trial++) {
for (int i = 0;
i < count;
i++) {
const int x =
x0 +
rand() %
(x1 - x0);
const int y =
y0 +
rand() %
(y1 - y0);
PointColor *p =
&points[cell->p[i]];
p->x = (short)x;
p->y = (short)y;
p->color =
src[
y * src_stride +
x];
}
const long long error =
GridError(
points,
src,
src_stride,
gx,
gy);
if (error < best_error) {
best_error = error;
for (int i = 0;
i < count;
i++)
best[i] =
points[cell->p[i]];
}
}
for (int i = 0;
i < count;
i++)
points[cell->p[i]] =
best[i];
free(best);
}
}
}
for (int i = 0;
i < grid_n;
i++)
free(grid[i].p);
free(grid);
grid = 0;
}
static int DumpSamples(
const char *filename,
PointColor *points,
int n)
{
FILE *f = fopen(filename, "wb");
if (!f)
return 0;
/* magic */
const unsigned char magic[4] = {'F', 'I', 'M', 'A'};
if (fwrite(magic, 1, 4, f) != 4) {
fclose(f);
return 0;
}
/* image dimensions */
uint16_t w = (uint16_t)frame_size_x;
uint16_t h = (uint16_t)frame_size_y;
if (fwrite(&w, 2, 1, f) != 1 ||
fwrite(&h, 2, 1, f) != 1) {
fclose(f);
return 0;
}
/*
* x and y are both <= 10 bits for 960x540.
* Pack them into 20 bits:
*
* bits 0.. 9 = x
* bits 10..19 = y
*/
for (int i = 0; i < n; i++) {
const unsigned x = (unsigned)points[i].x;
const unsigned y = (unsigned)points[i].y;
const unsigned xy = x | (y << 10);
unsigned char p[6];
p[0] = (unsigned char)(xy);
p[1] = (unsigned char)(xy >> 8);
p[2] = (unsigned char)(xy >> 16);
Color c;
c.u = points[i].color;
p[3] = c.r;
p[4] = c.g;
p[5] = c.b;
if (fwrite(p, 1, 6, f) != 6) {
fclose(f);
return 0;
}
}
fclose(f);
return 1;
}
static PointColor *LoadSamples(
const char *filename,
int *w,
int *h,
int *n)
{
FILE *f = fopen(filename, "rb");
if (!f)
return 0;
unsigned char magic[4];
if (fread(magic, 1, 4, f) != 4 ||
magic[0] != 'F' ||
magic[1] != 'I' ||
magic[2] != 'M' ||
magic[3] != 'A') {
fclose(f);
return 0;
}
uint16_t uw, uh;
if (fread(&uw, 2, 1, f) != 1 ||
fread(&uh, 2, 1, f) != 1) {
fclose(f);
return 0;
}
*w = uw;
*h = uh;
fseek(f, 0, SEEK_END);
const long file_size = ftell(f);
fseek(f, 8, SEEK_SET);
if (file_size < 8 || (file_size - 8) % 6 != 0) {
fclose(f);
return 0;
}
*n = (int)((file_size - 8) / 6);
PointColor *points =
(PointColor*)malloc(*n * sizeof(PointColor));
if (!points) {
fclose(f);
return 0;
}
for (int i = 0; i < *n; i++) {
unsigned char p[6];
if (fread(p, 1, 6, f) != 6) {
free(points);
fclose(f);
return 0;
}
const unsigned xy =
(unsigned)p[0] |
((unsigned)p[1] << 8) |
((unsigned)p[2] << 16);
points[i].x = (short)(xy & 1023);
points[i].y = (short)((xy >> 10) & 1023);
Color c;
c.r = p[3];
c.g = p[4];
c.b = p[5];
c.a = 0;
points[i].color = c.u;
}
fclose(f);
return points;
}
static void BuildGrid(
PointColor *points,
int n)
{
grid_w =
(frame_size_x + GRID_SIZE - 1) /
GRID_SIZE;
grid_h =
(frame_size_y + GRID_SIZE - 1) /
GRID_SIZE;
grid =
(GridCell*)calloc(
grid_w * grid_h,
sizeof(GridCell));
for (int i = 0; i < n; i++) {
int gx =
points[i].x /
GRID_SIZE;
int gy =
points[i].y /
GRID_SIZE;
if (gx < 0 ||
gx >= grid_w ||
gy < 0 ||
gy >= grid_h)
continue;
grid[
gy * grid_w + gx
].n++;
}
for (int i = 0;
i < grid_w * grid_h;
i++) {
if (grid[i].n) {
int n =
grid[i].n;
grid[i].p =
(int*)malloc(
n * sizeof(int));
grid[i].n = 0;
}
}
for (int i = 0; i < n; i++) {
int gx =
points[i].x /
GRID_SIZE;
int gy =
points[i].y /
GRID_SIZE;
if (gx < 0 ||
gx >= grid_w ||
gy < 0 ||
gy >= grid_h)
continue;
GridCell *c =
&grid[
gy * grid_w + gx];
c->p[c->n++] = i;
}
}
void InterpolateBitmap(
PointColor *points,
int n)
{
BuildGrid(points, n);
for (int y = 0;
y < frame_size_y;
y++)
for (int x = 0;
x < frame_size_x;
x++)
SetPixelUnsafe(
x,
y,
InterpolatePixel(
points,
x,
y));
for (int i = 0;
i < grid_w * grid_h;
i++)
free(grid[i].p);
free(grid);
grid = 0;
}
DrawKeyPoints(PointColor *points, int n)
{
for(int i=0;i<n; i++)
SetPixelUnsafe(points[i].x, points[i].y, 0x0000);
}
char* args_filename = NULL;
static int LoadSamplesAndDisplay(const char *filename)
{
int w, h, n;
PointColor *points =
LoadSamples(filename, &w, &h, &n);
if (!points)
return 0;
frame_size_x = w;
frame_size_y = h;
/*
* Standard reconstruction.
* InterpolateBitmap() eventually uses SetPixelUnsafe()
* for every output pixel.
*/
InterpolateBitmap(points, n);
free(points);
return 1;
}
char* input = "photo1.bmp";
test_opt()
{
Bitmap bitmap;
if (!LoadBitmap(
input,
&bitmap))
ERROR_("cant load bitmap");
grid_w =
(bitmap.w + GRID_SIZE - 1) /
GRID_SIZE;
grid_h =
(bitmap.h + GRID_SIZE - 1) /
GRID_SIZE;
const int grid_n =
grid_w * grid_h;
/*
* Najpierw okre+cl, ile punkt||w
* dostaje ka++dy grid.
*/
int *points_count =
BuildPointCounts(
bitmap.pixels,
bitmap.stride);
/*
* Zsumuj liczb-O punkt||w.
*/
int N = 0;
for (int i = 0;
i < grid_n;
i++)
N += points_count[i];
PointColor *points =
(PointColor*)malloc(
N * sizeof(PointColor));
/*
* Adaptacyjne rozmieszczenie
* pocz-atkowych pr||bek.
*/
RandomSamplesAdaptive(
points,
points_count,
&bitmap);
OptimizeGridSamples(
points,
N,
points_count,
bitmap.pixels,
bitmap.stride);
DumpSamples(
"sample.fima",
points,
N);
InterpolateBitmap(
points,
N);
if(i_toggler) DrawKeyPoints(points, N);
free(points_count);
}
test()
{
Bitmap bitmap;
if (!LoadBitmap(
input,
&bitmap))
ERROR_("cant load bitmap");
grid_w =
(bitmap.w + GRID_SIZE - 1) /
GRID_SIZE;
grid_h =
(bitmap.h + GRID_SIZE - 1) /
GRID_SIZE;
const int grid_n =
grid_w * grid_h;
int *points_count =
BuildPointCounts(
bitmap.pixels,
bitmap.stride);
int N = 0;
for (int i = 0;
i < grid_n;
i++)
N += points_count[i];
PointColor *points =
(PointColor*)malloc(
N * sizeof(PointColor));
RandomSamplesAdaptive(
points,
points_count,
&bitmap);
/*
* OptimizeGridSamples(
* points,
* N,
* points_count,
* bitmap.pixels,
* bitmap.stride);
*/
InterpolateBitmap(
points,
N);
if(i_toggler) DrawKeyPoints(points, N);
free(points_count);
}
void ProcessMouseMove(int x_, int y_){}
void ProcessKeyDown(int key)
{
if(key==VK_SPACE)
{
test_opt();
}
if(key=='A')
test();
}
void OnResize(){}
void OnRMBDown(int x, int y)
{
}
void OnLMBDown(int x, int y)
{
}
void DrawFrame()
{
}
void RunFrame(int advance)
{
static int initialised = 0;
if(!initialised)
{
initialised =1;
if(args_filename)
{
if (!LoadSamplesAndDisplay(args_filename))
{
ERROR_("Failed to load FIMA file: %s\n", args_filename);
}
return;
}
test();
}
}
int main(int argc, char **argv)
{
if (argc == 2)
{
args_filename= argv[1];
}
RegisterMouseMove( ProcessMouseMove );
RegisterKeyDown( ProcessKeyDown );
RegisterOnResize( OnResize );
RegisterRunFrame( RunFrame );
SetSleepValue(10);
SetScaleOnResize(1);
RegisterLeftMouseButtonDown( OnLMBDown );
RegisterRightMouseButtonDown(OnRMBDown );
float screen_size_y = GetSystemMetrics(SM_CYSCREEN);
SetupWindow4(" FIR'S INTERPOLATED IMAGE CODECK ", 10, 10, .9,
.9, 600 );
return 0;
}
--- Synchronet 3.22a-Linux NewsLink 1.2