Image Flattener
The code is to smooth out the JPEG artifacts on some digitized maps in order to facilitate the PNG compression. I finally decided to use some flood-style algorithm the result of which is somewhat satifactory (As applied on the 640x480 sample image, the sizeof the output PNG file is reduced from 613KB to 373KB without apparent distortion. Currently I care not too much about the speed.) Further improvment is expected to be made in refining the lines and the characters, which is intrinsically much tougher.
Original Image: http://p.blog.csdn.net/images/p_blog_csdn_net/quanben/379511/o_ditu_ori.PNG
Processed Image:
http://p.blog.csdn.net/images/p_blog_csdn_net/quanben/379511/o_ditu_enh.PNG
Following is the code area.
#include <iostream>
#include <cstdio>
#include <list>
#include <queue>
#include <vector>
#include <stack>
#include <malloc.h>
#include <cmath>
using namespace std;
typedef unsigned char Byte;
struct Pixel
{
Byte b,g,r;
Pixel () : r(0),g(0),b(0) {}
void Reset () { r=g=b=0; }
};
struct Coord
{
int x,y;
Coord () : x(0),y(0) {}
Coord (int _x, int _y) : x(_x),y(_y) {}
};
class Flattener
{
protected:
void Init ()
{
m_nAccepted = 0;
m_InitThr = (128*5) * 3;
m_BotThr = (128*5) * 3;
m_NumThr = 100;
m_SADThr = m_BotThr * 3/2;
}
int Diff (Pixel &pel)
{
int dr = (int)pel.r - m_MeanPel.r;
int dg = (int)pel.g - m_MeanPel.g;
int db = (int)pel.b - m_MeanPel.b;
dr = abs(dr);
dg = abs(dg);
db = abs(db);
return (65*dr+128*dg+25*db);
}
bool CloseEnough (Pixel &pel)
{
return (Diff(pel) < m_Thr);
}
void Accept (int p)
{
int nLastAccepted = m_nAccepted;
m_nAccepted++;
int halfn = m_nAccepted >> 1;
int v = m_MeanPel.r;
v *= nLastAccepted;
v += m_pSrc[p].r + halfn;
v /= m_nAccepted;
m_MeanPel.r = v;
v = m_MeanPel.g;
v *= nLastAccepted;
v += m_pSrc[p].g + halfn;
v /= m_nAccepted;
m_MeanPel.g = v;
v = m_MeanPel.b;
v *= nLastAccepted;
v += m_pSrc[p].b + halfn;
v /= m_nAccepted;
m_MeanPel.b = v;
// update
m_Thr = (int)((double)(m_InitThr - m_BotThr)
* pow(1.2, -nLastAccepted) + m_BotThr);
}
void Reject (int p)
{
// Do nothing
}
void Filter (int x, int y)
{
// N-point average
int D = 2;
int dymin = y>D-1?-D:-y;
int dxmin = x>0?-D:-x;
int dymax = y<m_H-D?D:m_H-y-1;
int dxmax = x<m_W-D?D:m_W-x-1;
int p = y * m_W + x;
int r=0, g=0, b=0;
int c = 0;
for (int dy=dymin; dy<=dymax; ++dy)
{
for (int dx=dxmin; dx<=dxmax; ++dx)
{
if (dx==0&&dy==0)
{
continue;
}
int nx = x+dx; int ny = y+dy;
int np = ny*m_W+nx;
r += m_pSrc[np].r;
g += m_pSrc[np].g;
b += m_pSrc[np].b;
c++;
}
}
r /= c; g /= c; b /= c;
m_pDst[p].r = r;
m_pDst[p].g = g;
m_pDst[p].b = b;
}
protected:
typedef vector< int > IntVec;
Pixel *m_pSrc;
Pixel *m_pDst;
IntVec m_Stat;
int m_nAccepted;
Pixel m_MeanPel;
int m_Thr;
int m_InitThr;
int m_BotThr;
int m_H, m_W;
int m_NumThr;
int m_Rx, m_Ry;
int m_SADThr;
public:
Flattener ()
{
}
~Flattener ()
{
}
void SetPic (Pixel *src, Pixel *dst, int w, int h)
{
m_Stat.resize(w*h, 0);
m_pSrc = src;
m_pDst = dst;
m_H = h, m_W = w;
memcpy(dst, src, w*h*3);
}
void FlattenAll ()
{
for (int y = 0; y < m_H; y++)
{
for (int x = 0; x < m_W; x++)
{
Flatten(x,y,true);
if (m_Rx != -1)
{
Flatten(m_Rx,m_Ry);
}
}
}
}
void Flatten (int sx, int sy, bool pre = false)
{
queue<Coord> q;
IntVec v1, v2;
Coord cc;
int x,y,nx,ny;
int p;
Init();
p = sy*m_W+sx;
if (m_Stat[p])
{
return;
}
cc.x = sx; cc.y = sy;
Accept(sy*m_W+sx);
q.push(cc);
while (!q.empty())
{
Coord &coord = q.front();
x = coord.x;
y = coord.y;
p = y*m_W+x;
Pixel &ctr = m_pSrc[p];
q.pop();
int dymin = y>0?-1:0;
int dxmin = x>0?-1:0;
int dymax = y<m_H-1?1:0;
int dxmax = x<m_W-1?1:0;
for (int dy=dymin; dy<=dymax; ++dy)
{
for (int dx=dxmin; dx<=dxmax; ++dx)
{
if (dx==0&&dy==0)
{
continue;
}
ny=y+dy;
nx=x+dx;
int np = ny*m_W+nx;
if (m_Stat[np]!=0)
{
continue;
}
cc.x = nx; cc.y = ny;
if (CloseEnough(m_pSrc[np]))
{
Accept(np);
q.push(cc);
m_Stat[np] = 1;
v1.push_back(np);
}
else
{
Filter(nx, ny);
if (CloseEnough(m_pDst[np]))
{
Accept(np);
q.push(cc);
m_Stat[np] = 1;
v1.push_back(np);
}
else
{
Reject(np);
m_Stat[np] = 2;
v2.push_back(np);
}
}
}
}
}
if (m_nAccepted > m_NumThr)
{
int md = 0x7fffffff; // max int
int t;
double sd = 0;
for (int i = 0; i < v1.size(); ++i )
{
if (pre)
{ // for actual processing in the next turn
int d = Diff(m_pSrc[v1[i]]);
if (d < md)
{
md = d;
t = v1[i];
}
m_Stat[v1[i]] = 0;
sd += d*d;
}
else
{
m_pDst[v1[i]] = m_MeanPel;
}
}
for (int i = 0; i < v2.size(); ++i )
{
m_Stat[v2[i]] = 0;
}
sd /= v1.size();
sd = sqrt(sd);
if (sd < m_SADThr)
{
m_Rx = t % m_W;
m_Ry = t / m_W;
}
else
{
m_Rx = m_Ry = -1;
}
}
else
{
for (int i = 0; i < v1.size(); ++i )
{
m_Stat[v1[i]] = 3; // skipped
}
for (int i = 0; i < v2.size(); ++i )
{
m_Stat[v2[i]] = 0;
}
m_Rx = m_Ry = -1;
}
}
void Finalize ()
{
for (int i = 0; i < m_W*m_H; ++i )
{
if (m_Stat[i] != 1)
{
m_pDst[i]=m_pSrc[i];
}
}
}
};
#define MAXH 480
#define MAXW 640
#define EndianU32_L2N(v) (v)
int main (void)
{
Flattener flattener;
FILE *fIn = fopen("in.bmp", "rb");
FILE *fOut = fopen("out.bmp", "wb");
Byte buf[MAXW*MAXH*3];
Byte dst[MAXW*MAXH*3];
fread(buf, 1, 54, fIn);
fwrite(buf, 1, 54, fOut);
unsigned long w, h;
w = EndianU32_L2N(*(unsigned long *)(buf + 18));
h = EndianU32_L2N(*(unsigned long *)(buf + 22));
printf("w = %d, h = %d/n", w, h);
int x = 255;
int y = 46;
fread(buf, 1, w*h*3, fIn);
flattener.SetPic((Pixel*)buf, (Pixel*)dst, w, h);
//flattener.Flatten(x, h-y);
flattener.FlattenAll();
flattener.Finalize();
fwrite(dst, 1, w*h*3, fOut);
fclose(fIn);
fclose(fOut);
return 0;
}
Original Image: http://p.blog.csdn.net/images/p_blog_csdn_net/quanben/379511/o_ditu_ori.PNG
Processed Image:
http://p.blog.csdn.net/images/p_blog_csdn_net/quanben/379511/o_ditu_enh.PNG
Following is the code area.
#include <iostream>
#include <cstdio>
#include <list>
#include <queue>
#include <vector>
#include <stack>
#include <malloc.h>
#include <cmath>
using namespace std;
typedef unsigned char Byte;
struct Pixel
{
Byte b,g,r;
Pixel () : r(0),g(0),b(0) {}
void Reset () { r=g=b=0; }
};
struct Coord
{
int x,y;
Coord () : x(0),y(0) {}
Coord (int _x, int _y) : x(_x),y(_y) {}
};
class Flattener
{
protected:
void Init ()
{
m_nAccepted = 0;
m_InitThr = (128*5) * 3;
m_BotThr = (128*5) * 3;
m_NumThr = 100;
m_SADThr = m_BotThr * 3/2;
}
int Diff (Pixel &pel)
{
int dr = (int)pel.r - m_MeanPel.r;
int dg = (int)pel.g - m_MeanPel.g;
int db = (int)pel.b - m_MeanPel.b;
dr = abs(dr);
dg = abs(dg);
db = abs(db);
return (65*dr+128*dg+25*db);
}
bool CloseEnough (Pixel &pel)
{
return (Diff(pel) < m_Thr);
}
void Accept (int p)
{
int nLastAccepted = m_nAccepted;
m_nAccepted++;
int halfn = m_nAccepted >> 1;
int v = m_MeanPel.r;
v *= nLastAccepted;
v += m_pSrc[p].r + halfn;
v /= m_nAccepted;
m_MeanPel.r = v;
v = m_MeanPel.g;
v *= nLastAccepted;
v += m_pSrc[p].g + halfn;
v /= m_nAccepted;
m_MeanPel.g = v;
v = m_MeanPel.b;
v *= nLastAccepted;
v += m_pSrc[p].b + halfn;
v /= m_nAccepted;
m_MeanPel.b = v;
// update
m_Thr = (int)((double)(m_InitThr - m_BotThr)
* pow(1.2, -nLastAccepted) + m_BotThr);
}
void Reject (int p)
{
// Do nothing
}
void Filter (int x, int y)
{
// N-point average
int D = 2;
int dymin = y>D-1?-D:-y;
int dxmin = x>0?-D:-x;
int dymax = y<m_H-D?D:m_H-y-1;
int dxmax = x<m_W-D?D:m_W-x-1;
int p = y * m_W + x;
int r=0, g=0, b=0;
int c = 0;
for (int dy=dymin; dy<=dymax; ++dy)
{
for (int dx=dxmin; dx<=dxmax; ++dx)
{
if (dx==0&&dy==0)
{
continue;
}
int nx = x+dx; int ny = y+dy;
int np = ny*m_W+nx;
r += m_pSrc[np].r;
g += m_pSrc[np].g;
b += m_pSrc[np].b;
c++;
}
}
r /= c; g /= c; b /= c;
m_pDst[p].r = r;
m_pDst[p].g = g;
m_pDst[p].b = b;
}
protected:
typedef vector< int > IntVec;
Pixel *m_pSrc;
Pixel *m_pDst;
IntVec m_Stat;
int m_nAccepted;
Pixel m_MeanPel;
int m_Thr;
int m_InitThr;
int m_BotThr;
int m_H, m_W;
int m_NumThr;
int m_Rx, m_Ry;
int m_SADThr;
public:
Flattener ()
{
}
~Flattener ()
{
}
void SetPic (Pixel *src, Pixel *dst, int w, int h)
{
m_Stat.resize(w*h, 0);
m_pSrc = src;
m_pDst = dst;
m_H = h, m_W = w;
memcpy(dst, src, w*h*3);
}
void FlattenAll ()
{
for (int y = 0; y < m_H; y++)
{
for (int x = 0; x < m_W; x++)
{
Flatten(x,y,true);
if (m_Rx != -1)
{
Flatten(m_Rx,m_Ry);
}
}
}
}
void Flatten (int sx, int sy, bool pre = false)
{
queue<Coord> q;
IntVec v1, v2;
Coord cc;
int x,y,nx,ny;
int p;
Init();
p = sy*m_W+sx;
if (m_Stat[p])
{
return;
}
cc.x = sx; cc.y = sy;
Accept(sy*m_W+sx);
q.push(cc);
while (!q.empty())
{
Coord &coord = q.front();
x = coord.x;
y = coord.y;
p = y*m_W+x;
Pixel &ctr = m_pSrc[p];
q.pop();
int dymin = y>0?-1:0;
int dxmin = x>0?-1:0;
int dymax = y<m_H-1?1:0;
int dxmax = x<m_W-1?1:0;
for (int dy=dymin; dy<=dymax; ++dy)
{
for (int dx=dxmin; dx<=dxmax; ++dx)
{
if (dx==0&&dy==0)
{
continue;
}
ny=y+dy;
nx=x+dx;
int np = ny*m_W+nx;
if (m_Stat[np]!=0)
{
continue;
}
cc.x = nx; cc.y = ny;
if (CloseEnough(m_pSrc[np]))
{
Accept(np);
q.push(cc);
m_Stat[np] = 1;
v1.push_back(np);
}
else
{
Filter(nx, ny);
if (CloseEnough(m_pDst[np]))
{
Accept(np);
q.push(cc);
m_Stat[np] = 1;
v1.push_back(np);
}
else
{
Reject(np);
m_Stat[np] = 2;
v2.push_back(np);
}
}
}
}
}
if (m_nAccepted > m_NumThr)
{
int md = 0x7fffffff; // max int
int t;
double sd = 0;
for (int i = 0; i < v1.size(); ++i )
{
if (pre)
{ // for actual processing in the next turn
int d = Diff(m_pSrc[v1[i]]);
if (d < md)
{
md = d;
t = v1[i];
}
m_Stat[v1[i]] = 0;
sd += d*d;
}
else
{
m_pDst[v1[i]] = m_MeanPel;
}
}
for (int i = 0; i < v2.size(); ++i )
{
m_Stat[v2[i]] = 0;
}
sd /= v1.size();
sd = sqrt(sd);
if (sd < m_SADThr)
{
m_Rx = t % m_W;
m_Ry = t / m_W;
}
else
{
m_Rx = m_Ry = -1;
}
}
else
{
for (int i = 0; i < v1.size(); ++i )
{
m_Stat[v1[i]] = 3; // skipped
}
for (int i = 0; i < v2.size(); ++i )
{
m_Stat[v2[i]] = 0;
}
m_Rx = m_Ry = -1;
}
}
void Finalize ()
{
for (int i = 0; i < m_W*m_H; ++i )
{
if (m_Stat[i] != 1)
{
m_pDst[i]=m_pSrc[i];
}
}
}
};
#define MAXH 480
#define MAXW 640
#define EndianU32_L2N(v) (v)
int main (void)
{
Flattener flattener;
FILE *fIn = fopen("in.bmp", "rb");
FILE *fOut = fopen("out.bmp", "wb");
Byte buf[MAXW*MAXH*3];
Byte dst[MAXW*MAXH*3];
fread(buf, 1, 54, fIn);
fwrite(buf, 1, 54, fOut);
unsigned long w, h;
w = EndianU32_L2N(*(unsigned long *)(buf + 18));
h = EndianU32_L2N(*(unsigned long *)(buf + 22));
printf("w = %d, h = %d/n", w, h);
int x = 255;
int y = 46;
fread(buf, 1, w*h*3, fIn);
flattener.SetPic((Pixel*)buf, (Pixel*)dst, w, h);
//flattener.Flatten(x, h-y);
flattener.FlattenAll();
flattener.Finalize();
fwrite(dst, 1, w*h*3, fOut);
fclose(fIn);
fclose(fOut);
return 0;
}
enjoy every minute of an appless, googless and oracless life