解压过程中出现停止,输出文件中充满了零(黑像素)?

3

我试图对一个bmp(位图)文件应用DCT(离散余弦变换)压缩。我有一个在Turbo C++中运行的c文件。这并不实际压缩,但我正在尝试实现DCT和IDCT。代码如下:

/*
the image to be compressed is a bmp with 24 bpp and
with name "college4.bmp" of dimensions 200*160 ie 25*20- 8*8 blocks
o/p is college2.dat
format: 8 bit signed integers starting rowwise from 0,0 to 8,8
the coefficients order is blue,green,red
for the block no 1 then 2 and soon
*/

#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#define WIDTH 25
#define HEIGHT 20

typedef struct {
    unsigned int type;
    unsigned long int filesize;
    unsigned int reserved1,reserved2;
    unsigned long int offset;
} BMPHEAD;

typedef struct {
    unsigned long int infosize;
    unsigned long int width,height;
    unsigned int planes,bitsperpixel;
    unsigned long int compression;
    unsigned long int sizeimage;
    long int xpelspermeter,ypelspermeter;
    unsigned long int colorused,colorimportant;
} INFOHEAD;

typedef struct {
    char rgbquad[4];
} colortable;

BMPHEAD bmphead;
INFOHEAD infohead;
FILE *bmp_fp1,*bmp_fp2;
int buf[WIDTH][8][8][3],buf1[WIDTH][8][8][3];
float pi=3.14159265,DCTcoeff[8][8][8][8];

void generatedctcoeff() {
    int y, i, j, x;
    for (i = 0; i < 8; i++) {
        for (j = 0; j < 8; j++) {
            for (x = 0; x < 8; x++) {
                for (y = 0; y < 8; y++) {
                    DCTcoeff[i][j][x][y] = cos(((2 * y + 1) * pi * j) / 16)
                            * cos(((2 * x + 1) * i * pi) / 16);
                }
            }
        }
    }
}

void outputtofile1() {                     // Write into college2.dat
    int i, j, x, y, blockno;              // One block at a time, buf contains pixel 
    int redcoef, greencoef, bluecoef;     // data of one row of blocks
    float gijred, gijgreen, gijblue, c, ci, cj;
    c = 1 / (sqrt(2));
    for (blockno = 0; blockno < WIDTH; blockno++) {
        for (i = 0; i < 8; i++) {
            for (j = 0; j < 8; j++) {
                gijred = 0;
                gijgreen = 0;
                gijblue = 0;
                for (x = 0; x < 8; x++) {
                    for (y = 0; y < 8; y++) {
                        gijblue = gijblue + DCTcoeff[i][j][x][y]
                                * buf[blockno][x][y][0];
                        gijgreen = gijgreen + DCTcoeff[i][j][x][y]
                                * buf[blockno][x][y][1];
                        gijred = gijred + DCTcoeff[i][j][x][y]
                                * buf[blockno][x][y][2];
                    }
                }
                ci = cj = 1.0;
                if (i == 0)
                    ci = c;
                if (j == 0)
                    cj = c;
                gijblue = ci * cj * gijblue / 4;
                gijgreen = ci * cj * gijgreen / 4;
                gijred = ci * cj * gijred / 4;
                bluecoef = (int) gijblue;
                greencoef = (int) gijgreen;
                redcoef = (int) gijred;
                fprintf(bmp_fp2, "%d %d %d ", bluecoef, greencoef, redcoef);
            }
        }
    } /* end of one block processing */
}

void compressimage() {
    int rowcount,x,y;
    bmp_fp1=fopen("college4.bmp","r");
    bmp_fp2=fopen("college2.dat","w");
    printf("generating coefficients...\n");
    generatedctcoeff();
    if(bmp_fp1==NULL) {
        printf("can't open");
        return;
    }
    printf("compressing....\n");
    fread(&bmphead,1,sizeof(bmphead),bmp_fp1);
    fread(&infohead,1,sizeof(infohead),bmp_fp1);
    fseek(bmp_fp1,bmphead.offset,SEEK_SET);
    for(rowcount=0;rowcount<HEIGHT;rowcount++) {
        for(y=0;y<8;y++) {
            for(x=0;x<infohead.width;x++) {
                buf[x/8][x%8][y][0]=(int)fgetc(bmp_fp1);
                buf[x/8][x%8][y][1]=(int)fgetc(bmp_fp1);
                buf[x/8][x%8][y][2]=(int)fgetc(bmp_fp1);
            }
        }
        outputtofile1();         //output contents of buf after dct to file
    }
    fclose(bmp_fp1);
    fclose(bmp_fp2);
}

void outputtofile2() {                                 //output buf to college3.bmp
    int i, j, x, y, blockno;                        // buf now contains coefficients
    float pxyred, pxygreen, pxyblue, c, ci, cj;     // a temp buffer buf1 used to 
    c = 1 / (sqrt(2));                              // store one row of block of
    for (blockno = 0; blockno < WIDTH; blockno++) { // decoded pixel values
        for (x = 0; x < 8; x++)
            for (y = 0; y < 8; y++) {
                pxyred = 0;
                pxygreen = 0;
                pxyblue = 0;
                for (j = 0; j < 8; j++) {
                    cj = 1.0;
                    if (j == 0)
                        cj = c;
                    for (i = 0; i < 8; i++) {
                        ci = 1.0;
                        if (i == 0)
                            ci = c;
                        pxyblue = pxyblue + ci * cj * DCTcoeff[i][j][y][x] * buf[blockno][i][j][0];
                        pxygreen = pxygreen + ci * cj
                        * DCTcoeff[i][j][y][x] * buf[blockno][i][j][1];
                        pxyred = pxyred + ci * cj * DCTcoeff[i][j][y][x] * buf[blockno][i][j][2];
                    }
                }
                pxyblue /= 4;
                pxygreen /= 4;
                pxyred /= 4;
                buf1[blockno][y][x][0] = pxyblue;
                buf1[blockno][y][x][1] = pxygreen;
                buf1[blockno][y][x][2] = pxyred;
            }
    }
    for (y = 0; y < 8; y++) {
        for (blockno = 0; blockno < WIDTH; blockno++)
            for (x = 0; x < 8; x++) {
                fprintf(bmp_fp2, "%c%c%c", (char) buf1[blockno][x][y][0],
                        (char) buf1[blockno][x][y][1],
                        (char) buf1[blockno][x][y][2]);
            }
    }
}

void uncompressimage() {
    int blue,green,red,rowcount,colcount,i,j;
    bmp_fp1=fopen("college2.dat","r");
    bmp_fp2=fopen("college3.bmp","w");
    printf("generating coefficients...\n");
    generatedctcoeff();
    if (bmp_fp1==NULL) {
        printf("open failed");
        return;
    }
    printf("uncompressing....\n");
    bmphead.type=0x4d42;
    bmphead.filesize=30518;
    bmphead.reserved1=0;
    bmphead.reserved2=0;
    bmphead.offset=sizeof(bmphead)+sizeof(infohead);
    infohead.infosize=sizeof(infohead);
    infohead.width=200;
    infohead.height=160;
    infohead.planes=1;
    infohead.bitsperpixel=24;
    infohead.compression=0;
    infohead.sizeimage=0;
    infohead.xpelspermeter=3780;
    infohead.ypelspermeter=3780;
    infohead.colorused=0;
    infohead.colorimportant=0;
    fwrite(&bmphead,sizeof(BMPHEAD),1,bmp_fp2);
    fwrite(&infohead,sizeof(INFOHEAD),1,bmp_fp2);
    for(rowcount=0;rowcount<HEIGHT;rowcount++) {
        for(colcount=0;colcount<WIDTH;colcount++) {
            for(i=0;i<8;i++) {
                for(j=0;j<8;j++) {
                    fscanf(bmp_fp1,"%d",&blue);
                    fscanf(bmp_fp1,"%d",&green);
                    fscanf(bmp_fp1,"%d",&red);
                    buf[colcount][i][j][0]=blue;
                    buf[colcount][i][j][1]=green;
                    buf[colcount][i][j][2]=red;
                }
            }
        }
        outputtofile2();
    }
    fclose(bmp_fp1);
    fclose(bmp_fp2);
}

int main() {
    printf("opening files...\n");
    compressimage();
    printf("opening files...again\n");
    uncompressimage();
    printf("successful decompression\nenter any key\n");
    return 0;
}

这是我用作输入的图片:输入图片

(对不起,网站将bmp格式转换为png格式。您可以将其转回bmp格式以便使用。)这是生成的图片:

输出错误的图片

创建的文件college3.bmp的大小为200x160,大小为93.8 kB,但到图像的四分之一处已经正确解码了系数,但后来该文件被填充了黑色像素。由于上传时显示不是有效的BMP,因此我已经截取了输出屏幕截图。自2004年2月以来,我一直在研究这个问题。如果有人能告诉我哪里有漏洞,我将非常感激。我已经分析了输出文件,并发现EOF正好在像素开始变黑的地方。我阅读了其他关于这个主题的问题,并发现转换因子ci,cj已被不适当地使用。编码时,我也困惑了x,y,i和j的索引。因此,我希望这个问题在几天内得到解决。


1
自2004年2月以来一直在解决这个问题,加1。 - Ulterior
你能否拿这个答案中的DCT/IDCT代码与你的代码进行双重检查吗? 它可以对一个通道(蓝色或红色或绿色或黑白)上的8x8块执行JPEG的DCT / IDCT。 - Alexey Frunze
@Alex 我很有信心这不是缓冲重用问题,因为我正在使用buf来保存像素值并计算系数并将其写入.dat文件。在执行IDCT时,我从文件(.dat)中读取系数,将其存储在buf中,然后执行IDCT并将像素值复制到buf1中,最后写入bmp文件。我怀疑的是为什么在outputtofile2中我引用了DCTcoeff[i][j][y][x]和buf1[blockno][y][x][0]而不是DCTcoeff[i][j][x][y]和buf1[blockno][x][y][0]。我不记得为什么要那样做。我无法在eclipse中重现这个问题。 - Som Pra
1个回答

1

显然,上述代码中的问题在于如何打开文件。

以下是应该在您的代码中的内容(请注意明确指定的打开模式,二进制和文本):

void compressimage() {
...
    bmp_fp1=fopen("college4.bmp","rb");
    bmp_fp2=fopen("college2.dat","wt");
...
}

void uncompressimage() {
...
    bmp_fp1=fopen("college2.dat","rt");
    bmp_fp2=fopen("college3.bmp","wb");
...
}

有了那个,稍微改变一下结构定义:

#pragma pack(push,1)

typedef struct {
    unsigned short int type;
    unsigned long int filesize;
    unsigned short int reserved1,reserved2;
    unsigned long int offset;
} BMPHEAD;

typedef struct {
    unsigned long int infosize;
    unsigned long int width,height;
    unsigned short int planes,bitsperpixel;
    unsigned long int compression;
    unsigned long int sizeimage;
    long int xpelspermeter,ypelspermeter;
    unsigned long int colorused,colorimportant;
} INFOHEAD;

typedef struct {
    char rgbquad[4];
} colortable;

#pragma pack(pop)

我能够使用三种不同的编译器(Turbo C++,Open Watcom,gcc)成功编译您的程序,并获得所需的输出图片。


我需要在Turbo C++中自己验证这个问题,但现在无法访问它。我已经丢失了备份代码。当我复制问题中发布的代码并在eclipse中编译时,它会在compressimage函数中的文件读取(fgetc)处卡住。在gcc中编译并运行它会在compressimage函数内部产生分段错误。只是想知道人们是否可以在这个网站上发布脏代码?非常感谢你的努力。谢谢。 - Som Pra
我使用了问题中的代码,并且只做了我提到的那些修改。没有遗漏任何东西,而且我甚至没有收到任何警告(虽然这并不意味着代码真的很干净)。 - Alexey Frunze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接