如何在 ESP32-CAM 上裁剪位图图像?
How do I crop a bitmap image on an ESP32-CAM?
我整晚都在为这段代码苦苦思索。我正在尝试在 ESP32 CAM 上裁剪位图。我拍了一张照片,将照片转换为bmp,然后调用以下函数:
size_t crop_image(uint8_t *fb, size_t len, uint32_t width, uint32_t height, unsigned short crop_left, unsigned short crop_top, unsigned short crop_width, unsigned short crop_height)
{
uint8_t *buf = fb + BMP_HEADER_LEN;
size_t new_size = crop_width * crop_height * 3 + BMP_HEADER_LEN;
unsigned int write_idx = 0;
for(uint32_t y = crop_top * 3; y < (crop_top + crop_height) * width * 3; y += width * 3){
for(int x = crop_left * 3; x < (crop_left + crop_width) * 3; x += 3){
int pix_idx = x + y;
buf[write_idx++] = buf[pix_idx];
buf[write_idx++] = buf[pix_idx+1];
buf[write_idx++] = buf[pix_idx+2];
}
}
// Adjust the BMP Header
*(uint32_t *)(fb + BMP_HEADER_WIDTH_OFFSET) = crop_width;
*(uint32_t *)(fb + BMP_HEADER_HEIGHT_OFFSET) = -1 * crop_height;
*(uint32_t *)(fb + 6) = new_size;
*(uint32_t *)(fb + 34) = crop_width * crop_height * 3;
return new_size;
}
这几乎行得通。如果我将图像的宽度和高度作为 crop_width 和 crop_height 参数传递,则输出与原始图像相同。如果我通过较小的高度和全宽,那么它也可以。当我传入一个小于原始宽度的 crop_width 时,我得到一个图像,该图像具有从右上角到底部大约 1/3 的对角“移位”线 运行。看起来我的宽度偏移了一个字节,导致了 3/2 斜率线。但是我不知道是什么原因。
我附上了一张全尺寸的 jpg 和一张裁剪后的位图。请注意,jpg 不是来源(我的代码不保存原始文件),但与裁剪图像的来源非常相似。裁剪后的 bmitmap 是通过 crop_left = 0、crop_top = 0、crop_width = 799、crop_height = 600 的调用创建的。原始图像为 800x600。
我删除了所有额外的代码 - 所以没有错误处理,没有从位图中提取宽度/高度 header,等等
代码这么简单,快把我逼疯了。谢谢
Image very similar to the original
Cropped - note not original bmp - a screenshot - but shows issue
好吧,几个小时的睡眠可以做到这一点。裁剪后的图片宽度需要填充为4字节的倍数。我正在测试的数字恰好不能被 4 整除(整个图像测试除外)。位图图像宽度必须在 4 字节边界上 - 或者填充为如此,
感谢您的考虑
这是工作代码(仍然没有错误处理):
size_t crop_image(uint8_t *fb, size_t len, uint32_t width, uint32_t height, unsigned short crop_left, unsigned short crop_top, unsigned short crop_width, unsigned short crop_height)
{
uint8_t *buf = fb + BMP_HEADER_LEN;
size_t new_size = crop_width * crop_height * 3 + BMP_HEADER_LEN;
unsigned int write_idx = 0;
for(uint32_t y = crop_top * 3; y < (crop_top + crop_height) * width * 3; y += width * 3){
for(int x = crop_left * 3; x < (crop_left + crop_width) * 3; x += 3){
int pix_idx = x + y;
buf[write_idx++] = buf[pix_idx];
buf[write_idx++] = buf[pix_idx+1];
buf[write_idx++] = buf[pix_idx+2];
}
// Pad to four byte boundary
for (int i_pad=0; i_pad < (crop_width % 4); i_pad++) buf[write_idx++] = 0;
}
// Adjust the BMP Header
*(uint32_t *)(fb + BMP_HEADER_WIDTH_OFFSET) = crop_width;
*(uint32_t *)(fb + BMP_HEADER_HEIGHT_OFFSET) = -1 * crop_height;
*(uint32_t *)(fb + 6) = new_size;
*(uint32_t *)(fb + 34) = crop_width * crop_height * 3;
return new_size;
}
我整晚都在为这段代码苦苦思索。我正在尝试在 ESP32 CAM 上裁剪位图。我拍了一张照片,将照片转换为bmp,然后调用以下函数:
size_t crop_image(uint8_t *fb, size_t len, uint32_t width, uint32_t height, unsigned short crop_left, unsigned short crop_top, unsigned short crop_width, unsigned short crop_height)
{
uint8_t *buf = fb + BMP_HEADER_LEN;
size_t new_size = crop_width * crop_height * 3 + BMP_HEADER_LEN;
unsigned int write_idx = 0;
for(uint32_t y = crop_top * 3; y < (crop_top + crop_height) * width * 3; y += width * 3){
for(int x = crop_left * 3; x < (crop_left + crop_width) * 3; x += 3){
int pix_idx = x + y;
buf[write_idx++] = buf[pix_idx];
buf[write_idx++] = buf[pix_idx+1];
buf[write_idx++] = buf[pix_idx+2];
}
}
// Adjust the BMP Header
*(uint32_t *)(fb + BMP_HEADER_WIDTH_OFFSET) = crop_width;
*(uint32_t *)(fb + BMP_HEADER_HEIGHT_OFFSET) = -1 * crop_height;
*(uint32_t *)(fb + 6) = new_size;
*(uint32_t *)(fb + 34) = crop_width * crop_height * 3;
return new_size;
}
这几乎行得通。如果我将图像的宽度和高度作为 crop_width 和 crop_height 参数传递,则输出与原始图像相同。如果我通过较小的高度和全宽,那么它也可以。当我传入一个小于原始宽度的 crop_width 时,我得到一个图像,该图像具有从右上角到底部大约 1/3 的对角“移位”线 运行。看起来我的宽度偏移了一个字节,导致了 3/2 斜率线。但是我不知道是什么原因。
我附上了一张全尺寸的 jpg 和一张裁剪后的位图。请注意,jpg 不是来源(我的代码不保存原始文件),但与裁剪图像的来源非常相似。裁剪后的 bmitmap 是通过 crop_left = 0、crop_top = 0、crop_width = 799、crop_height = 600 的调用创建的。原始图像为 800x600。
我删除了所有额外的代码 - 所以没有错误处理,没有从位图中提取宽度/高度 header,等等
代码这么简单,快把我逼疯了。谢谢
Image very similar to the original Cropped - note not original bmp - a screenshot - but shows issue
好吧,几个小时的睡眠可以做到这一点。裁剪后的图片宽度需要填充为4字节的倍数。我正在测试的数字恰好不能被 4 整除(整个图像测试除外)。位图图像宽度必须在 4 字节边界上 - 或者填充为如此,
感谢您的考虑
这是工作代码(仍然没有错误处理):
size_t crop_image(uint8_t *fb, size_t len, uint32_t width, uint32_t height, unsigned short crop_left, unsigned short crop_top, unsigned short crop_width, unsigned short crop_height)
{
uint8_t *buf = fb + BMP_HEADER_LEN;
size_t new_size = crop_width * crop_height * 3 + BMP_HEADER_LEN;
unsigned int write_idx = 0;
for(uint32_t y = crop_top * 3; y < (crop_top + crop_height) * width * 3; y += width * 3){
for(int x = crop_left * 3; x < (crop_left + crop_width) * 3; x += 3){
int pix_idx = x + y;
buf[write_idx++] = buf[pix_idx];
buf[write_idx++] = buf[pix_idx+1];
buf[write_idx++] = buf[pix_idx+2];
}
// Pad to four byte boundary
for (int i_pad=0; i_pad < (crop_width % 4); i_pad++) buf[write_idx++] = 0;
}
// Adjust the BMP Header
*(uint32_t *)(fb + BMP_HEADER_WIDTH_OFFSET) = crop_width;
*(uint32_t *)(fb + BMP_HEADER_HEIGHT_OFFSET) = -1 * crop_height;
*(uint32_t *)(fb + 6) = new_size;
*(uint32_t *)(fb + 34) = crop_width * crop_height * 3;
return new_size;
}