如何缩小图像大小?

时间:2014-11-29 01:08:44

标签: c image-resizing bitmapimage

您好我正在开发一个程序,将640x480位地图图像缩小为320x240图像。我一直在研究这个问题,但我发现的所有好例子都是为了增加图像的大小。

(见这里:http://cboard.cprogramming.com/c-programming/154737-help-program-resize-image.html

我很难将该计划中的内容翻译成我的需要做的事情。 这是我的代码:

include stdio.h
include stdlib.h
include string.h
include math.h


pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER
{
unsigned short bfType; //specifies the file type
unsigned int bfSize; //specifies the size in bytes of  the bitmap file
unsigned short bfReserved1; //reserved; must be 0
unsigned short bfReserved2; //reserved; must be 0
unsigned int bfOffBits; //species the offset in bytes from the bitmapfileheader to the bitmap bits
} BITMAPFILEHEADER;
pragma pack(pop)

pragma pack(push, 1)
typedef struct tagBITMAPDIBHEADER
{
unsigned int biSize; //specifies the number of bytes required by the struct
int biWidth; //specifies width in pixels
int biHeight; //species height in pixels
unsigned short biPlanes; //specifies the number of color planes, must be 1
unsigned short biBitCount; //specifies the number of bit per pixel
unsigned int biCompression;//spcifies the type of compression
unsigned int biSizeImage; //size of image in bytes
int biXPelsPerMeter; //number of pixels per meter in x axis
int biYPelsPerMeter; //number of pixels per meter in y axis
unsigned int biClrUsed; //number of colors used by th ebitmap
unsigned int biClrImportant; //number of colors that are important
} BITMAPDIBHEADER;
pragma pack(pop)

pragma pack(push, 1)
typedef struct
{
int  rgbtBlue;
int  rgbtGreen;
int  rgbtRed;
}
RGBTRIPLE;
pragma pack(pop)

int main()
{
FILE *input, *output;
BITMAPDIBHEADER inputdibHeader;
BITMAPFILEHEADER inputfileHeader;
BITMAPDIBHEADER outputdibHeader;
BITMAPFILEHEADER outputfileHeader;

int greenValue = 0;
int blueValue = 0;
int redValue = 0;
fopen_s(&output, "test.bmp", "wb");
if (output == NULL){
    return NULL;
}
fopen_s(&input, "lolcat.bmp", "rb");
if (input == NULL)
    return NULL;

rewind(input);  // rewind the file before reading it again
fread(&(inputfileHeader), sizeof(BITMAPFILEHEADER), 1, input);
fread(&(inputdibHeader), sizeof(BITMAPDIBHEADER), 1, input);
rewind(input);  // rewind the file before reading it again
fread(&(outputfileHeader), sizeof(BITMAPFILEHEADER), 1, input);
fread(&(outputdibHeader), sizeof(BITMAPDIBHEADER), 1, input);





outputdibHeader.biWidth = inputdibHeader.biWidth *.5;
outputdibHeader.biHeight = inputdibHeader.biHeight *.5;
outputfileHeader.bfSize = outputdibHeader.biWidth * outputdibHeader.biHeight;
outputdibHeader.biSizeImage = inputdibHeader.biSizeImage *.5;
fwrite(&(outputfileHeader), sizeof(BITMAPFILEHEADER), 1, output);
fwrite(&(outputdibHeader), sizeof(BITMAPDIBHEADER), 1, output);

rewind(input);
fseek(input, inputfileHeader.bfOffBits, SEEK_SET);
fseek(output, outputfileHeader.bfOffBits, SEEK_SET);
int oldheight = inputdibHeader.biHeight;
int oldwidth = inputdibHeader.biWidth;
int i;
int timeswriten = 0;
int oldPad = (4 - ((inputdibHeader.biWidth * sizeof(RGBTRIPLE)) % 4)) % 4;
int newPad = (4 - ((outputdibHeader.biWidth * sizeof(RGBTRIPLE)) % 4)) % 4;

// iterate over infile's scanlines
for (int i = 0; i < abs(oldheight); i++)
{
    if (i % 2){


        // iterate over pixels in scanline
        for (int j = 0; j < oldwidth; j++)
        {
            // temporary storage
            RGBTRIPLE triple;
            fread(&triple, sizeof(RGBTRIPLE), 1, input);
            if (j % 2){
                fwrite(&triple, sizeof(RGBTRIPLE), 1, output);
            }
            // skip over any input padding
            fseek(input, oldPad, SEEK_CUR);


        }
    }
    }

    fclose(input);
    fclose(output);

} 

目前,此代码生成有效的位图图像,但创建的图像是原始图像的非常扭曲的版本。我相当肯定这是由于我从新图像中省略像素的方式,但我不确定应该采用什么样的方法。 在我的所有问题上,任何人都可以帮助向我解释我应该省略像素的位置和方式吗?

更新

我现在知道我的目标是将2x2像素平均分成一个像素,但我找不到如何做到这一点的好例子。可以解释一下这个过程吗?

更新2 感谢PeterT,我知道以下代码看起来是正确的,我的输出不是。

 RGBTRIPLE *line_a = (RGBTRIPLE*)malloc(inputdibHeader.biWidth * sizeof(RGBTRIPLE)); /* check malloc() */
RGBTRIPLE *line_b = (RGBTRIPLE*)malloc(inputdibHeader.biWidth *sizeof(RGBTRIPLE)); /* check malloc() */
RGBTRIPLE *dest_line = (RGBTRIPLE*)malloc(outputdibHeader.biWidth * sizeof(RGBTRIPLE));

    /* move through the target array line by line, consuming two lines from the source
    image at a time */
    /* also assuming you verified the source image is exactly 2x the size of the dest
    malloc() */
for (i = 0; i < outputdibHeader.biHeight; ++i)
{
    fread(&(line_a), sizeof(RGBTRIPLE), inputdibHeader.biWidth, input);  /* read scanline & advance file pointer, err check in func */
    fread(&(line_b), sizeof(RGBTRIPLE), inputdibHeader.biWidth, input);/* read scanline & advance file pointer, err check in func */
    for (j = 0; j < outputdibHeader.biWidth; ++j)
    {
        bilinear_filter(&(dest_line[j]), &(line_a[j * 2]), &(line_a[(j * 2) + 1]), &(line_b[j * 2]), &(line_b[(j * 2) + 1]));
    }
    fwrite(&(dest_line), sizeof(RGBTRIPLE), outputdibHeader.biWidth, output);
    /* or something... point is we're creeping through the files scaline by scanline,
    and letting another function handle it to keep this code more intelligble */
}

fclose(input);
fclose(output);

}
void bilinear_filter(RGBTRIPLE *dest, RGBTRIPLE *A, RGBTRIPLE *B, RGBTRIPLE *C, RGBTRIPLE *D)
{

/* assuming 0888 ARGB */
dest->Red = (A->Red + B->Red + C->Red + D->Red) / 4;
dest->Green = (A->Green + B->Green + C->Green + D->Green) / 4;
dest->Blue = (A->Blue + B->Blue + C->Blue + D->Blue) / 4;

}

我认为这个问题可能在于我的标题创建,所以这就是

fread(&(inputHeader), sizeof(TwoHeader), 1, input);
inputfileHeader = inputHeader.fileHeader;
inputdibHeader = inputHeader.dibHeader;
rewind(input);  // rewind the file before reading it again
fread(&(outputHeader), sizeof(TwoHeader), 1, input);
outputfileHeader = outputHeader.fileHeader;
outputdibHeader = outputHeader.dibHeader;


outputdibHeader.biWidth = inputdibHeader.biWidth *.5;
outputdibHeader.biHeight = inputdibHeader.biHeight *.5;
//outputfileHeader.bfSize = inputfileHeader.bfSize - (inputdibHeader.biWidth*inputdibHeader.biHeight) + outputdibHeader.biWidth*outputdibHeader.biHeight;
outputfileHeader.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPDIBHEADER)+outputdibHeader.biSizeImage;
//outputdibHeader.biSizeImage = inputdibHeader.biSizeImage * .25;
//outputdibHeader.biXPelsPerMeter = inputdibHeader.biXPelsPerMeter * .5;
//outputdibHeader.biYPelsPerMeter = inputdibHeader.biYPelsPerMeter * .5;
//fwrite(&(outputfileHeader), sizeof(BITMAPFILEHEADER), 1, output);
//fwrite(&(outputdibHeader), sizeof(BITMAPDIBHEADER), 1, output);
fwrite(&(outputHeader), sizeof(TwoHeader), 1, output);
rewind(input);
fseek(input, inputfileHeader.bfOffBits, SEEK_SET);
fseek(output, outputfileHeader.bfOffBits, SEEK_SET);

请原谅所有评论,其中大部分是我不确定的旧代码或代码。

1 个答案:

答案 0 :(得分:4)

“我现在一直在研究这个问题”......真的吗? /引起眉毛/;)

听起来你正在寻找一个双线性滤镜:新像素实际上是四个像素的平均值,位于前四个像素的中心。

你真的需要重新发明轮子吗?我只是使用一个可靠的库,并将精力集中在解决其他问题上:

https://github.com/nothings/stb

关于插值的基本方法有一篇很棒的维基百科文章:

http://en.wikipedia.org/wiki/Image_scaling

对2D图像进行下采样是计算机科学中一个已有40多年历史的问题。 Foley在他的开创性着作“计算机图形学”中介绍了它,这是一本很棒的书。

关于您的代码:

对于简单的双线性下采样,您必须存储至少两条扫描线。我建议将代码分解为更模块化的代码,例如:这是一个非常简单的实现:

triple *line_a = (triple*)malloc(...) /* check malloc() */
triple *line_b = (triple*)malloc(...) /* check malloc() */
triple *dest_line = etc...

/* move through the target array line by line, consuming two lines from the source 
   image at a time */
/* also assuming you verified the source image is exactly 2x the size of the dest 
   malloc() */
for (i = 0; i < dest_height; ++i) 
{
    read_line(line_a); /* read scanline & advance file pointer, err check in func */
    read_line(line_b); /* read scanline & advance file pointer, err check in func */
    for (j = 0; j < dest_width; ++j)
    {
        bilinear_filter(&(dest_line[j]), &(line_a[j*2]), &(line_a[(j*2)+1]), &(line_b[j*2]), &(line_b[(j*2)+1]));
    }
    write_line_to_file(dest_line, fp); /* or something... point is we're creeping through the files scaline by scanline, and letting another function handle it to keep this code more intelligble */
}
:
:
void bilinear_filter(triple *dest, triple *A, triple *B, triple *C, triple *D) 
{

    /* assuming 0888 ARGB */
    dest->r = (A->r + B->r + C->r + D->r) / 4;
    dest->g = (A->g + B->g + C->g + D->g) / 4;
    dest->b = (A->b + B->b + C->b + D->b) / 4;

}

现在,有很多方法可以插入颜色通道。有些理论考虑了眼睛可见光谱的能量发射,或者印刷/胶片等的色域曲线。大多数都没有像我那样独立地缩放r / g / b,因为它不能保持r / g / b的关系。三个值对眼睛的敏感度。我在上面展示的方法只是指出双线性滤波器需要读取四个像素的数据才能生成一个新像素。

我希望这会有所帮助。