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;
}


 
posted @ 2008-04-16 23:55  quanben  阅读(190)  评论(0编辑  收藏  举报