我一直在尝试制作这个程序,将彩色图像转换为黑白图像。但我不知道如何去做。我是C的新手,还没有掌握语法,甚至正确使用ubuntu。
我认为我的问题是与tga文件标题无法读取的问题。因为在tga文件上尝试这个程序时得到的结果是一张没有高度的无法打开的图片。 “height = 0”。
是否有一些好的链接可供人阅读C?
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct pixel {
uint8_t r, g, b, a;
};
static uint8_t *load_image(char *filename, int *sizex, int *sizey)
{
uint8_t *image;
char buf[512];
char *bufptr;
int ret;
FILE *fp = fopen(filename, "r");
bufptr = fgets(buf, 512, fp);
ret = fscanf(fp, "%d %d\n", sizex, sizey);
bufptr = fgets(buf, 512, fp);
image = malloc(*sizex * *sizey * 4);
int i;
uint8_t *ptr = image;
for (i=0; i<*sizex * *sizey; ++i) {
ret = fread(ptr, 1, 3, fp);
ptr += 4;
}
fclose(fp);
return image;
}
static int save_image(const char *filename, uint8_t *image, int sizex, int sizey)
{
FILE *fp = fopen(filename, "w");
fprintf(fp, "P6\n%d %d\n255\n", sizex, sizey);
int i;
uint8_t *ptr = image;
for (i=0; i<sizex * sizey; ++i) {
fwrite(ptr, 1, 3, fp);
ptr += 4;
}
fclose(fp);
return 1;
}
void convert_grayscale(uint8_t *input, uint8_t *output, int sizex, int sizey)
{
// Y = 0.299 * R + 0.587 * G + 0.114 * B
int i;
for (i = 0; i < sizex * sizey; ++i)
{
struct pixel *pin = (struct pixel*) &input[i*4];
struct pixel *pout = (struct pixel*) &output[i*4];
float luma = 0.299 * pin->r + 0.587 * pin->g + 0.114 * pin->b;
if (luma > 255)
luma = 255;
uint8_t intluma = (int) luma;
pout->r = intluma;
pout->g = intluma;
pout->b = intluma;
pout->a = 255;
}
}
int main()
{
uint8_t *inputimg, *outputimg;
int sizex, sizey;
inputimg = load_image("image.tga", &sizex, &sizey);
outputimg = malloc(sizex * sizey * 4);
convert_grayscale(inputimg, outputimg, sizex, sizey);
save_image("output.tga", outputimg, sizex, sizey);
}
答案 0 :(得分:1)
(个人提示:阅读Why Stackoverflow sucks后答案较长。对于获得版主权限的所有人来说,这应该是必读的。)
问题是你的load_image
代码似乎是为了读取PPM(基于ASCII的)图像而设计的:
每个PPM图像包含以下内容: 1.一个神奇的数字&#34;用于标识文件类型。 ppm图像的幻数是两个字符&#34; P6&#34;。 2.空白(空白,TAB,CR,LF)。 3.宽度,格式为十进制的ASCII字符。 4.空白。 5.高度,再次以ASCII十进制表示。 6.空白。 7.最大颜色值(Maxval),再次以ASCII十进制表示。必须小于65536且大于零。 8.单个空白字符(通常是换行符)。 9.高度行的栅格[...]
- 您的第一个fgets
读取,然后丢弃,&#34;幻数&#34;线,然后读取宽度和高度,然后丢弃&#34; maxval&#34;线。
它应该适用于PPM图像(你可以重命名这个例程load_ppm_image
)如果它不是一个重要的问题:在所有ASCII内容之后,你切换到fread
,所以这里是警告#1 。
在打开文件之前,请确定您是要阅读专有 ASCII文本,还是可能需要读取二进制数据。
问题在于&#39;文字模式&#39; &#34; W&#34; 在读取和写入其他字符时转换某些字符。这是所有常见C库中的内置行为;它试图修复上一代程序员给我们留下的行尾字符混乱。现在,以文本模式读取文本文件有点简单,但读取二进制数据不可能。你不能确定你得到了文件中的内容。
让我们继续警告#2:并非所有文件格式都相同。
上述例程(主要)适用于PPM图像,但它会在TGA上失败,因为它的标题组织方式不同。 TGA标题的描述相当不错here(谷歌结果的随机选择)。
规范描述 bytes ,因此首先要做的是将fopen
行更改为
FILE *fp = fopen(filename, "rb");
顺便说一下,如果成功的话, test 就是一个好的做法:
if (fp == NULL)
{
printf ("Opening the file '%s' failed\n", filename);
return NULL;
}
然后您可以使用fgetc
或fread
来读取一个或多个字节。来自警告#3:小心使用fread
。
fread
按照它们存储到文件中的顺序读取多个字节,因此您可能认为它可能会读取width
和height
等项目 - 每个字节2字节整数值 - 在一个&#39;读取&#39;操作。但fread
不知道系统中字节的顺序(也不知道文件本身),因此它可以读取&#34; lo-hi&#34;,正如我所指出的那样,在你的计算机中,整数中的字节顺序是&#34; hi-lo&#34;。澄清:如果文件包含此
80 00
然后用fread (&width,1,2, fp)
读取,然后存储,这两个字节以相同的顺序存储到计算机内存中。字节采用Big-Endian顺序; &#34;大&#34;字节结束了。但是,如果您的计算机恰好是Little-Endian订单系统,则不会获得0x0080 = 128
而是0x8000 = 32768
的价值!
避免这种情况的方法是一次读取一个字节:
width = fgetc(fp) + (fgetc(fp)<<8);
将始终以正确的顺序读取数据:先低,然后高。只存储总和(按照系统的顺序存储,但现在不相关!)。
有了上述内容,我想我已经没有警告了。使用TGA规范作为指导,您现在可以打开文件,一次读取一个字节的标题,直到您拥有所需的所有信息,并继续将{1}}原始图像数据存入内存。你可以安全地使用fread
一次读取三个图像字节,因为它们在读取时会以相同的顺序显示在内存中(它们不是整数或更大,所以& #34;内存顺序&#34;不是问题。)
确保您正在阅读正确信息的好方法是:
为了帮助您入门,在fread
行之后(以及必要的检查是否有效):
fopen
..等等。读完整个标题并且您没有遇到意外时,您就可以设置读取实际图像数据的内容了。不需要进行任何更改,您现有的代码应该可以正常工作。
编辑后:我看到我用了
int idLength = fgetc(fp); /* length of id string after header */
printf ("id length: %u bytes\n", idLength);
int colorMapType = fgetc(fp); /* 0 = RGB */
printf ("color map type: %u\n", colorMapType);
if (colorMapType != 0)
{
printf ("unexpected color map type!\n");
return NULL;
}
int imageType = fgetc(fp); /* 0 = None, 1 = Indexed, 2 = RGB, 3 = Greyscale */
其中&#39;彩色地图类型&#39;实际上是字节,而不是整数。这是为了让腰带和吊带接近。如果您在阅读标题时遇到文件末尾,则int colorMapType = fgetc(fp);
返回的代码为fgetc
。 EOF
无法存储到EOF
,因为它是一个整数值:char
(更准确地说:0xFFFFFFFF
)。如果将其存储到(int)-1
中,则无法将其与完全正常的值char
(值255)区分开来。
腰带和吊带方法是检查每个字节:
0x000000FF
如果您正在使用已知的文件,并且您知道它是一个有效的TGA(您可以使用位图编辑器查看和编辑它),但是如果您打算继续工作,那就太多了你不知道它们是否有效的文件,你可能需要这个。