CImage读取8位灰度图像数据

来源:互联网 发布:录制视频软件下载 编辑:程序博客网 时间:2024/05/21 17:19

CImage读取8位灰度图像数据

今天收到了一个任务,就是将4张256X256的小图拼成一个大图,然后再缩小成256X256的小图,图像均是8位的灰度图。网上查了一下资料,决定采用CImage类来实现。

首先利用CImage的Load函数读取磁盘上的4个图像文件。然后创建一个512X512的图像,利用GetBits函数获取图像数据指针将前四个图像的数据复制到创建图像的指定位置,最后调用Save函数保存图像,看看效果。

大致代码如下:

    CImage imgs[4];    imgs[0].Load(file0);    imgs[1].Load(file1);    imgs[2].Load(file2);    imgs[3].Load(file3);    CImage image;    image.Create(512, 512);    BYTE* bits = image.GetBits();    SetRectBits(image.GetBits(), 0, 0, 256, 256, imgs[0].GetBits());    SetRectBits(image.GetBits(), 256, 0, 256, 256, imgs[1].GetBits());    SetRectBits(image.GetBits(), 0, 256, 256, 256, imgs[2].GetBits());    SetRectBits(image.GetBits(), 256, 256, 256, 256, imgs[3].GetBits());    image.Save(file);    // 将数据复制到目标的矩形区域内, x, y 是矩形的左上角坐标, width是矩形的宽, height是矩形的高    void SetRectBits(BYTE* src, int x, int y, int width, int height, BYTE* dst)    {        for (int i = y; i < y + height; ++i)        {            memcpy(dst + srcWidth * i + x, src + (i - y) * width, width);        }    }

程序运行中断,中断在memcpy上,目标不可写。猜测问题出现在获取图像数据指针上。查找资料后发现,GetBits获取的指针并不一定是图像的开始位置,必须根据GetPitch来判断,如果返回值为正,则GetBits返回的指针指向图像数据的开头,如果为负,则指向图像数据最后一行的开头。

    BYTE* bits = (BYTE*)image.GetBits() + (image.GetPitch()*(image.GetHeight() - 1));

再次运行程序,运行正常,但打开生成的图像发现图像全黑。调试程序,查看image的内存内容,图像数据确实写进去了,所以猜测问题出现在CImage图像数据的表示上。再次查资料,发现8位图像时,CImage图像数据是调色板的索引值。问题就出现在调色板上,调色程序查看image的调色板数据,发现均为0,调色板未设置,所以需要设置调色板的值。

    RGBQUAD colors[256];    image.GetColorTable(0, image.GetMaxColorTableEntries(), colors);    for (int i = 0; i < 256; i++)    {        colors[i].rgbBlue = (BYTE)i;        colors[i].rgbGreen = (BYTE)i;        colors[i].rgbRed = (BYTE)i;        colors[i].rgbReserved = 0;    }    image.SetColorTable(0, image.GetMaxColorTableEntries(), colors);

再次编译运行程序,然后打开生成的图像,显示正常,问题解决。

接下来就是图像的缩小了。
采用的是双线性插值算法。就是根据目标图像像素的x0,y0坐标计算从其相对于源图像中的x1,y1坐标,得到的坐标值会是个浮点数,在获取这个x,y坐标周围的4个像素值,由这4个像素到x1,y1的距离作权值来计算加权平均值,最后复制给x0,y0坐标的像素,循环上述操作,直至目标图像所有像素都填充完。大致代码如下:

    CImage dstImage;    dstImage.Create(256, 256);    /*设置调色板略*/    BYTE* dst = dstImage.GetBits();    int srcWidth = image.GetWidth();    int srcHeight = image.GetHeight();    BYTE* src = (BYTE*)image.m_GetBits();    float x0 = 0;    float y0 = 0;    int x1 = 0;    int y1 = 0;    float f1 = 0.0;    int x2 = 0;    int y2 = 0;    float f2 = 0.0;    int x3 = 0;    int y3 = 0;    float f3 = 0.0;    int x4 = 0;    int y4 = 0;    float f4 = 0.0;    for (int i = 0; i < height; i++)    {        for (int j = 0; j < width; ++j)        {            x0 = (float)j * (float)srcWidth / (float)width;            y0 = (float)i * (float)srcHeight / (float)height;            x1 = (int)x0;            y1 = (int)y0;            f1 = (x0 - (float)x1) * (y0 - (float)y1);            x2 = x1;            if (x1 + 1 < srcWidth)            {                ++x2;            }            y2 = y1;            f2 = (1 - (x0 - (float)x1)) * (y0 - (float)y1);            x3 = x1;            y3 = y1;            if (y1 + 1 < srcHeight)            {                ++y3;            }            f3 = (x0 - (float)x1) * (1 - (y0 - (float)y1));            x4 = x1;            if (x1 + 1 < srcWidth)            {                ++x4;            }            y4 = y1;            if (y1 + 1 < srcHeight)            {                ++y4;            }            f4 = (1 - (x0 - (float)x1)) * (1 - (y0 - (float)y1));            *((dst + i * width + j) = ((float)*(src + y1 * srcWidth + x1)) * f1 +                ((float)*(src + y2 * srcWidth + x2)) * f2 +                ((float)*(src + y3 * srcWidth + x3)) * f3 +                ((float)*(src + y4 * srcWidth + x4)) * f4;        }    }dstImage.Save(dstFile);

任务解决。

后来发现图像时按比例缩小的,这种情况下双线性插值就变成临插法了,结果图像严重失真,所以直接取周围像素的平均值就可以了:

*((dst + i * width + j) = ((src + y1 * srcWidth + x1) +                (src + y2 * srcWidth + x2) +                (src + y3 * srcWidth + x3) +                (src + y4 * srcWidth + x4)) / 4;
0 0
原创粉丝点击