调整 BMP 图像的大小(使其变小)
Resizing a BMP image (making it smaller)
当缩放系数小于 1 时,我似乎需要一些帮助来调整 BMP 图像的大小。您可以在下面看到我的代码中最关键的部分。代码中的变量 f 是缩放因子。这对我来说似乎合乎逻辑,但它工作不正常。
- 这是我需要调整大小的图像(使其更小)。
- 这是看起来不正确的结果图片。我想我上传失败了,但它看起来像一个没有任何白色中心的绿色小方块。
此外,我尝试调整另一张图片的大小 - 这个美丽的笑脸:
结果出乎意料:
这让我觉得 for 循环有问题,尽管对我来说这似乎完全合乎逻辑。
这就是 BMP 的组织方式。
// BMP-related data types based on Microsoft's own
#include <stdint.h>
// aliases for C/C++ primitive data types
// https://msdn.microsoft.com/en-us/library/cc230309.aspx
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
// information about the type, size, and layout of a file
// https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx
typedef struct
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;
// information about the dimensions and color format
// https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx
typedef struct
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;
// relative intensities of red, green, and blue
// https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
有人知道如何修复下面的代码吗?
// Copies a BMP file
#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 4)
{
fprintf(stderr, "Usage: resize n infile outfile\n");
return 1;
}
// remember filenames
float f=atof(argv[1]);
int n=f;
if(f<=0||f>=1){
fprintf(stderr, "f, the resize factor, must be between 0 and 1.\n");
return 1;
}
char *infile = argv[2];
char *outfile = argv[3];
// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", infile);
return 2;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
fprintf(stderr, "Could not create %s.\n", outfile);
return 3;
}
// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
// ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
bi.biBitCount != 24 || bi.biCompression != 0)
{
fclose(outptr);
fclose(inptr);
fprintf(stderr, "Unsupported file format.\n");
return 4;
}
BITMAPFILEHEADER bf_resize = bf;
BITMAPINFOHEADER bi_resize = bi;
bi_resize.biWidth = bi.biWidth * f;
bi_resize.biHeight = bi.biHeight * f;
int padding_resize = (4 - (bi_resize.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bi_resize.biSizeImage = (bi_resize.biWidth * sizeof(RGBTRIPLE) + padding_resize) * abs(bi_resize.biHeight);
bf_resize.bfSize = bi_resize.biSizeImage + sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
int fn = f * 10;
int x = 10 / fn;
int diff = x;
diff--;
int w = 0;
int h = 0;
// write outfile's BITMAPFILEHEADER
fwrite(&bf_resize, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi_resize, sizeof(BITMAPINFOHEADER), 1, outptr);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
if(h==0){
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
if(w==0){
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
w++;
}
else{
w++;
}
if(w==diff){
w=0;
}
}
}
h++;
if(h==diff){
h=0;
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// then add it back (to demonstrate how)
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}
我假设您想通过使用变量 w
、h
和 diff
跳过行和列来缩小图像。例如,如果我们设置
比例因子f
赋值为0.5,diff
赋值为1,每隔一个
rows/columns 将被跳过以将图像缩放 0.5 倍。
然后在循环中有两个关键问题 i 和 j:
- 您正在
if(w==diff){ w=0; }
w++;
之后重置 w
。然后
w
一直为 0,没有跳过任何列。
- 您正在将
if(h==0){
条件放在外部块中。然后是像素
h==0 时不读取。为了缩小图像,你需要保持
无论条件如何读取每个像素,并写入像素
如果条件满足。
那么循环将被改进为:
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) {
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++) {
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
if (w == 0 && h == 0) {
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
w++;
if (w > diff){
w = 0;
}
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// padding to the output file, if any
if (h == 0) {
for (int k = 0; k < padding_resize; k++) {
fputc(0x00, outptr);
}
}
h++;
if (h > diff){
h = 0;
}
}
它将在有限的条件下工作:f=0.5
以及图像宽度和
高度是偶数。但在一般情况下,它仍然不能很好地工作。
例如,如果我们将 f
设置为 0.4,
bi_resize.biWidth
和 bi_resize.biHeight
将计算为
将原始大小乘以 0.4,而 diff
将被计算为 1。
然后它们会导致 header 信息与
实际像素。
这是解决问题的另一个提示:
- 处理图像的常用方法是存储整个像素
一开始进入内存。
然后你可以random-access以下任意像素
过程和代码将更加直接。
基本思想是迭代 destination 坐标
并返回到源图像的坐标以进行拾取
像素值。
- 您发布的输入图像太小,无法确定问题所在,因为
细线和小点很容易折叠,即使过滤
如果算法合适。
最好用大图来评价。
这是我根据你的代码重写的:
#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 4) {
fprintf(stderr, "Usage: resize n infile outfile\n");
return 1;
}
// read the scaling factor
float f = atof(argv[1]);
if (f <= 0 || f > 1) {
fprintf(stderr, "f, the resize factor, must be between 0 and 1.\n");
return 1;
}
char *infile = argv[2];
char *outfile = argv[3];
// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL) {
fprintf(stderr, "Could not open %s.\n", infile);
return 2;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL) {
fclose(inptr);
fprintf(stderr, "Could not create %s.\n", outfile);
return 3;
}
// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
// ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
bi.biBitCount != 24 || bi.biCompression != 0) {
fclose(outptr);
fclose(inptr);
fprintf(stderr, "Unsupported file format.\n");
return 4;
}
BITMAPFILEHEADER bf_resize = bf;
BITMAPINFOHEADER bi_resize = bi;
bi_resize.biWidth = bi.biWidth * f;
bi_resize.biHeight = bi.biHeight * f;
int padding = bi.biWidth % 4; // you can simplify the calculation
int padding_resize = bi_resize.biWidth % 4;
bi_resize.biSizeImage = (bi_resize.biWidth * sizeof(RGBTRIPLE) + padding_resize) * bi_resize.biHeight;
bf_resize.bfSize = bi_resize.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// allocate mamory for the rgb triplets of the original (input) image
RGBTRIPLE *pix = malloc(sizeof(RGBTRIPLE) * bi.biWidth * bi.biHeight);
if (pix == NULL) {
fprintf(stderr, "malloc failed.\n");
return 5;
}
// temporary storage
RGBTRIPLE triple;
// read the entire pixels of the original image and store into the memory
for (int i = 0; i < bi.biHeight; i++) {
for (int j = 0; j < bi.biWidth; j++) {
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
pix[i * bi.biWidth + j] = triple;
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
}
// write outfile's header
fwrite(&bf_resize, sizeof(BITMAPFILEHEADER), 1, outptr);
fwrite(&bi_resize, sizeof(BITMAPINFOHEADER), 1, outptr);
// write the pixels of destination (resized) image
for (int i = 0; i < bi_resize.biHeight; i++) {
for (int j = 0; j < bi_resize.biWidth; j++) {
// calculate the corresponding coorinates in the original image
int m = (i / f + 0.5); // +0.5 for rounding
if (m > bi.biHeight - 1) { // limit the value
m = bi.biHeight - 1;
}
int n = (j / f + 0.5);
if (n > bi.biWidth - 1) {
n = bi.biWidth - 1;
}
// pick the pixel value at the coordinate
triple = pix[m * bi.biWidth + n];
// write RGB triplet to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
// padding for the output image, if any
for (int j = 0; j < padding_resize; j++) {
fputc(0x00, outptr);
}
}
free(pix);
fclose(inptr);
fclose(outptr);
return 0;
}
当缩放系数小于 1 时,我似乎需要一些帮助来调整 BMP 图像的大小。您可以在下面看到我的代码中最关键的部分。代码中的变量 f 是缩放因子。这对我来说似乎合乎逻辑,但它工作不正常。
此外,我尝试调整另一张图片的大小 - 这个美丽的笑脸:
这让我觉得 for 循环有问题,尽管对我来说这似乎完全合乎逻辑。
这就是 BMP 的组织方式。
// BMP-related data types based on Microsoft's own
#include <stdint.h>
// aliases for C/C++ primitive data types
// https://msdn.microsoft.com/en-us/library/cc230309.aspx
typedef uint8_t BYTE;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef uint16_t WORD;
// information about the type, size, and layout of a file
// https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx
typedef struct
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} __attribute__((__packed__))
BITMAPFILEHEADER;
// information about the dimensions and color format
// https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx
typedef struct
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} __attribute__((__packed__))
BITMAPINFOHEADER;
// relative intensities of red, green, and blue
// https://msdn.microsoft.com/en-us/library/dd162939(v=vs.85).aspx
typedef struct
{
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} __attribute__((__packed__))
RGBTRIPLE;
有人知道如何修复下面的代码吗?
// Copies a BMP file
#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 4)
{
fprintf(stderr, "Usage: resize n infile outfile\n");
return 1;
}
// remember filenames
float f=atof(argv[1]);
int n=f;
if(f<=0||f>=1){
fprintf(stderr, "f, the resize factor, must be between 0 and 1.\n");
return 1;
}
char *infile = argv[2];
char *outfile = argv[3];
// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", infile);
return 2;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
fprintf(stderr, "Could not create %s.\n", outfile);
return 3;
}
// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
// ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
bi.biBitCount != 24 || bi.biCompression != 0)
{
fclose(outptr);
fclose(inptr);
fprintf(stderr, "Unsupported file format.\n");
return 4;
}
BITMAPFILEHEADER bf_resize = bf;
BITMAPINFOHEADER bi_resize = bi;
bi_resize.biWidth = bi.biWidth * f;
bi_resize.biHeight = bi.biHeight * f;
int padding_resize = (4 - (bi_resize.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bi_resize.biSizeImage = (bi_resize.biWidth * sizeof(RGBTRIPLE) + padding_resize) * abs(bi_resize.biHeight);
bf_resize.bfSize = bi_resize.biSizeImage + sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
int fn = f * 10;
int x = 10 / fn;
int diff = x;
diff--;
int w = 0;
int h = 0;
// write outfile's BITMAPFILEHEADER
fwrite(&bf_resize, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi_resize, sizeof(BITMAPINFOHEADER), 1, outptr);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
if(h==0){
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
if(w==0){
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
w++;
}
else{
w++;
}
if(w==diff){
w=0;
}
}
}
h++;
if(h==diff){
h=0;
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// then add it back (to demonstrate how)
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}
我假设您想通过使用变量 w
、h
和 diff
跳过行和列来缩小图像。例如,如果我们设置
比例因子f
赋值为0.5,diff
赋值为1,每隔一个
rows/columns 将被跳过以将图像缩放 0.5 倍。
然后在循环中有两个关键问题 i 和 j:
- 您正在
if(w==diff){ w=0; }
w++;
之后重置w
。然后w
一直为 0,没有跳过任何列。 - 您正在将
if(h==0){
条件放在外部块中。然后是像素 h==0 时不读取。为了缩小图像,你需要保持 无论条件如何读取每个像素,并写入像素 如果条件满足。
那么循环将被改进为:
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) {
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++) {
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
if (w == 0 && h == 0) {
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
w++;
if (w > diff){
w = 0;
}
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// padding to the output file, if any
if (h == 0) {
for (int k = 0; k < padding_resize; k++) {
fputc(0x00, outptr);
}
}
h++;
if (h > diff){
h = 0;
}
}
它将在有限的条件下工作:f=0.5
以及图像宽度和
高度是偶数。但在一般情况下,它仍然不能很好地工作。
例如,如果我们将 f
设置为 0.4,
bi_resize.biWidth
和 bi_resize.biHeight
将计算为
将原始大小乘以 0.4,而 diff
将被计算为 1。
然后它们会导致 header 信息与
实际像素。
这是解决问题的另一个提示:
- 处理图像的常用方法是存储整个像素 一开始进入内存。 然后你可以random-access以下任意像素 过程和代码将更加直接。 基本思想是迭代 destination 坐标 并返回到源图像的坐标以进行拾取 像素值。
- 您发布的输入图像太小,无法确定问题所在,因为 细线和小点很容易折叠,即使过滤 如果算法合适。 最好用大图来评价。
这是我根据你的代码重写的:
#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 4) {
fprintf(stderr, "Usage: resize n infile outfile\n");
return 1;
}
// read the scaling factor
float f = atof(argv[1]);
if (f <= 0 || f > 1) {
fprintf(stderr, "f, the resize factor, must be between 0 and 1.\n");
return 1;
}
char *infile = argv[2];
char *outfile = argv[3];
// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL) {
fprintf(stderr, "Could not open %s.\n", infile);
return 2;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL) {
fclose(inptr);
fprintf(stderr, "Could not create %s.\n", outfile);
return 3;
}
// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
// ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
bi.biBitCount != 24 || bi.biCompression != 0) {
fclose(outptr);
fclose(inptr);
fprintf(stderr, "Unsupported file format.\n");
return 4;
}
BITMAPFILEHEADER bf_resize = bf;
BITMAPINFOHEADER bi_resize = bi;
bi_resize.biWidth = bi.biWidth * f;
bi_resize.biHeight = bi.biHeight * f;
int padding = bi.biWidth % 4; // you can simplify the calculation
int padding_resize = bi_resize.biWidth % 4;
bi_resize.biSizeImage = (bi_resize.biWidth * sizeof(RGBTRIPLE) + padding_resize) * bi_resize.biHeight;
bf_resize.bfSize = bi_resize.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// allocate mamory for the rgb triplets of the original (input) image
RGBTRIPLE *pix = malloc(sizeof(RGBTRIPLE) * bi.biWidth * bi.biHeight);
if (pix == NULL) {
fprintf(stderr, "malloc failed.\n");
return 5;
}
// temporary storage
RGBTRIPLE triple;
// read the entire pixels of the original image and store into the memory
for (int i = 0; i < bi.biHeight; i++) {
for (int j = 0; j < bi.biWidth; j++) {
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
pix[i * bi.biWidth + j] = triple;
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
}
// write outfile's header
fwrite(&bf_resize, sizeof(BITMAPFILEHEADER), 1, outptr);
fwrite(&bi_resize, sizeof(BITMAPINFOHEADER), 1, outptr);
// write the pixels of destination (resized) image
for (int i = 0; i < bi_resize.biHeight; i++) {
for (int j = 0; j < bi_resize.biWidth; j++) {
// calculate the corresponding coorinates in the original image
int m = (i / f + 0.5); // +0.5 for rounding
if (m > bi.biHeight - 1) { // limit the value
m = bi.biHeight - 1;
}
int n = (j / f + 0.5);
if (n > bi.biWidth - 1) {
n = bi.biWidth - 1;
}
// pick the pixel value at the coordinate
triple = pix[m * bi.biWidth + n];
// write RGB triplet to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
// padding for the output image, if any
for (int j = 0; j < padding_resize; j++) {
fputc(0x00, outptr);
}
}
free(pix);
fclose(inptr);
fclose(outptr);
return 0;
}