Image#
Image 库用于处理k210的图像,为Camera和KPU提供图像类以及操作方法,包括图像裁剪缩放、rgb像素格式转换、bmp读取保存等功能。
构造函数#
描述#
构造函数
语法#
1.通过create参数决定是否自动创建图像缓存
Image(uint32_t width, uint32_t height, image_format_t f, bool create = false);
2.使用用户提供的缓存创建Image
Image(uint32_t width, uint32_t height, image_format_t f, uint8_t *buffer);
参数#
width
图像宽height
图像高image_format_t
图像格式
enum image_format_t : uint32_t
{
IMAGE_FORMAT_GRAYSCALE = 0, // bpp 1
IMAGE_FORMAT_RGB565, // bpp 2
IMAGE_FORMAT_RGB888, // bpp 3
IMAGE_FORMAT_R8G8B8, // bpp 3
IMAGE_FORMAT_INVALID = 4,
};
create
是否创建图像内存buffer
用户分配的图像内存
返回值#
无
示例说明#
使用buff.disply缓存创建显示Image
img_display = new Image(cam.width(), cam.height(), IMAGE_FORMAT_RGB565, buff.disply);
cut#
描述#
图像裁剪
语法#
1.从src裁剪r矩形大小的图像到dst,create决定是否创建dst图像缓存
static int cut(Image *src, Image *dst, rectangle_t &r, bool create);
2.src_Image对象调用cut方法裁剪r矩形大小图像到dst
int cut(Image *dst, rectangle_t &r, bool create = true);
3.src_Image对象调用cut方法裁剪r矩形大小图像并返回结果图像
Image * cut(rectangle_t &r);
参数#
src
源图像dst
生成的图像rectangle_t
裁剪的矩形框信息
typedef struct rectangle
{
uint32_t x;
uint32_t y;
uint32_t w;
uint32_t h;
} rectangle_t;
create
是否为生成的图像创建内存
返回值#
- 语法1,2
0:成功,其他值:失败
- 语法3
目标图像指针
示例说明#
Image *img_cut = img_ai->cut(cut_rect);
resize#
描述#
图像缩放
语法#
1.从src图resize图像到dst,create决定是否创建dst图像缓存
static int resize(Image *src, Image *dst, uint32_t width, uint32_t height, bool create);
2.src_Image对象调用resize方法缩放图像到dst
int resize(Image *dst, uint32_t width, uint32_t height, bool create = true);
3.src_Image对象调用resize方法并返回结果图像
Image * resize(uint32_t width, uint32_t height);
参数#
src
源图像dst
生成的图像width
裁剪图像宽height
裁剪图像高create
是否为生成的图像创建内存
返回值#
- 语法1,2
0:成功,其他值:失败
- 语法3
目标图像指针
示例说明#
img_128x128 = new Image(128, 128, IMAGE_FORMAT_R8G8B8, true);
img_cut->resize(img_128x128, 128, 128, false);
to_grayscale#
描述#
生成灰度图
语法#
1
int to_grayscale(Image *dst, bool create = true);
2
Image * to_grayscale(void);
参数#
dst
生成的图像create
是否为生成的图像创建内存
返回值#
- 语法1
0:成功,其他值:失败
- 语法2
目标图像指针
示例说明#
Image *img_gray;
img_gray = img_display->to_grayscale();
to_rgb565#
描述#
转换为 rgb565 格式
语法#
1
int to_rgb565(Image *dst, bool create = true);
2
Image * to_rgb565(void);
参数#
dst
生成的图像create
是否为生成的图像创建内存
返回值#
- 语法1
0:成功,其他值:失败
- 语法2
目标图像指针
示例说明#
Image *img_rgb565;
img_rgb565 = img_gray->to_rgb565();
to_rgb888#
描述#
转换为 rgb888 格式
语法#
1
int to_rgb888(Image *dst, bool create = true);
2
Image * to_rgb888(void);
参数#
dst
生成的图像create
是否为生成的图像创建内存
返回值#
- 语法1
0:成功,其他值:失败
- 语法2
目标图像指针
示例说明#
Image *img_rgb888;
img_rgb888 = img_gray->to_rgb888();
to_r8g8b8#
描述#
转换为 r8g8b8 格式,作为KPU输入图需要的格式
语法#
1
int to_r8g8b8(Image *dst, bool create = true);
2
Image * to_r8g8b8(void);
参数#
dst
生成的图像create
是否为生成的图像创建内存
返回值#
- 语法1
0:成功,其他值:失败
- 语法2
目标图像指针
示例说明#
Image *img_r8g8b8;
img_r8g8b8 = img_gray->to_r8g8b8();
load_bmp#
描述#
读取bmp图
语法#
1.加载bmp图到dst
static int load_bmp(Image *dst, fs::FS &fs, const char *name);
2.加载bmp图并返回Image对象
static Image * load_bmp(fs::FS &fs, const char *name);
参数#
dst
读取到的图像fs
文件系统,使用SD卡则为 FFatname
bmp图像文件路径
返回值#
- 语法1
0:成功,其他值:失败
- 语法2
目标图像指针
示例说明#
Image *img = Image::load_bmp(FFat, "/2.bmp");
save_bmp#
描述#
保存为bmp图
语法#
1
static int save_bmp(Image *img, fs::FS &fs, const char *name);
2
int save_bmp(fs::FS &fs, const char *name);
参数#
img
要保存的图像fs
文件系统,使用SD卡则为 FFatname
bmp图像文件路径
返回值#
0:成功,其他值:失败
示例说明#
if((rgb888 = img_display->to_rgb888()))
{
result = rgb888->save_bmp(FFat, "/img1.bmp");
delete rgb888;
}
例程 - color_convert_test.ino#
颜色格式转换
以下是一个图像格式转换处理示例程序。它使用OV2640摄像头获取图像数据,并通过ST7789V液晶屏将不同格式的图像数据(RGB565、RGB888和R8G8B8)显示出来。
程序中使用了OV2640库、ST7789V库、FFat库和K210图像处理库来实现图像采集和显示,通过调用Image类的方法(to_grayscale,to_rgb565,to_rgb888,to_r8g8b8)实现不同格式间的转换。
需要注意的点包括:需要正确配置OV2640摄像头和ST7789V液晶屏的参数、需要正确挂载SD卡、需要正确获取图像缓冲区并创建Image对象、需要正确调用Image类的方法进行图像格式转换,并在合适的时候释放内存以避免内存泄漏。
#include "Arduino.h"
#include "Image.h"
#include "OV2640.h"
#include "ST7789V.h"
#include "FFat.h"
using namespace K210;
// 定义摄像头和LCD
OV2640 cam;
ST7789V lcd(240, 320);
// 定义图像指针
Image *img_ai, *img_display, *img_128x128;
void setup()
{
camera_buffers_t buff;
Serial.begin(115200);
while (!Serial) {
;
}
// 初始化LCD
lcd.begin();
// lcd.invertDisplay(1);
lcd.setRotation(3);
// lcd.setTextSize(2);
lcd.setTextColor(0x07E0);
lcd.setCursor(0,0);
lcd.fillScreen(0xFFFF);
// 挂载文件系统
if(!FFat.begin()){
lcd.printf("FFat Mount Failed\n");
Serial.printf("FFat Mount Failed");
while(1) {}
}
// 初始化摄像头
if(0x00 != cam.reset(FRAMESIZE_QVGA))
{
lcd.printf("camera reset failed\n");
Serial.printf("camera reset failed\n");
while(1) {}
}
cam.set_vflip(true);
cam.set_hmirror(true);
// 获取摄像头缓存
cam.get_buffers(&buff);
if((NULL == buff.disply) || (NULL == buff.ai.r))
{
lcd.printf("get camera buffers failed\n");
Serial.printf("get camera buffers failed\n");
while(1) {}
}
// 初始化图像指针
img_ai = new Image(cam.width(), cam.height(), IMAGE_FORMAT_R8G8B8, buff.ai.r);
img_display = new Image(cam.width(), cam.height(), IMAGE_FORMAT_RGB565, buff.disply);
}
void loop()
{
// 拍摄照片
if(0x00 != cam.snapshot())
{
lcd.setCursor(0,0);
lcd.printf("camera snapshot failed\n");
lcd.refresh();
Serial.printf("camera snapshot failed\n");
return;
}
Image *img_gray, *img_rgb565, *img_rgb888, *img_r8g8b8;
// 生成灰度图像
img_gray = img_display->to_grayscale();
if(!img_gray)
{
lcd.setCursor(0,0);
lcd.printf("to gray failed\n");
lcd.refresh();
Serial.printf("to gray failed\n");
return;
}
// 将灰度图像转换为RGB565图像
img_rgb565 = img_gray->to_rgb565();
lcd.drawImage(img_rgb565);
delete img_rgb565;
// 显示转换后的图像
lcd.setCursor(0,0);
lcd.printf("gray to rgb565");
lcd.refresh();
delay(500);
// 将灰度图像转换为RGB888图像
img_rgb888 = img_gray->to_rgb888();
if(!img_rgb888)
{
lcd.setCursor(0,0);
lcd.printf("to rgb888 failed\n");
lcd.refresh();
Serial.printf("to rgb888 failed\n");
return;
}
// 将RGB888图像转换为RGB565图像
img_rgb565 = img_rgb888->to_rgb565();
lcd.drawImage(img_rgb565);
delete img_rgb565;
delete img_rgb888;
// 显示转换后的图像
lcd.setCursor(0,0);
lcd.printf("gray to rgb888 to rgb565");
lcd.refresh();
delay(500);
// 将灰度图像转换为R8G8B8图像
img_r8g8b8 = img_gray->to_r8g8b8();
if(!img_r8g8b8)
{
lcd.setCursor(0,0);
lcd.printf("to img_r8g8b8 failed\n");
lcd.refresh();
Serial.printf("to img_r8g8b8 failed\n");
return;
}
// 将r8g8b8图像转换为RGB565图像
img_rgb565 = img_r8g8b8->to_rgb565();
lcd.drawImage(img_rgb565);
delete img_rgb565;
delete img_r8g8b8;
// 显示转换后的图像
lcd.setCursor(0,0);
lcd.printf("gray to img_r8g8b8 to rgb565");
lcd.refresh();
delay(500);
delete img_gray;
// 将RGB565图像转换为RGB888图像
img_rgb888 = img_display->to_rgb888();
if(!img_rgb888)
{
lcd.setCursor(0,0);
lcd.printf("to rgb888 failed\n");
lcd.refresh();
Serial.printf("to rgb888 failed\n");
return;
}
// 将RGB888图像转换为RGB565图像
img_rgb565 = img_rgb888->to_rgb565();
lcd.drawImage(img_rgb565);
delete img_rgb565;
delete img_rgb888;
// 显示转换后的图像
lcd.setCursor(0,0);
lcd.printf("rgb565 to rgb888 to rgb565");
lcd.refresh();
delay(500);
// 将rgb888图像转换为R8G8B8图像
img_r8g8b8 = img_rgb888->to_r8g8b8();
if(!img_r8g8b8)
{
lcd.setCursor(0,0);
lcd.printf("to img_r8g8b8 failed\n");
lcd.refresh();
Serial.printf("to img_r8g8b8 failed\n");
return;
}
// 将R8G8B8图像转换为RGB565图像
img_rgb565 = img_r8g8b8->to_rgb565();
lcd.drawImage(img_rgb565);
delete img_rgb565;
delete img_r8g8b8;
}
例程 - load_and_display.ino#
加载bmp图并显示
以下是一个BMP格式图片显示示例程序。它使用FFat库从SD卡中读取指定的BMP格式图片,并通过ST7789V液晶屏将其显示出来。
程序中使用了ST7789V库、FS库和FFat库来实现图像读取和显示,通过调用Image类的方法实现将BMP格式图片转换为RGB565格式并在液晶屏上显示。
需要注意的点包括:需要正确挂载SD卡、需要正确获取图像文件并创建Image对象、需要正确调用Image类的方法进行格式转换,并在合适的时候释放内存以避免内存泄漏。同时需要确保所读取的BMP格式图片分辨率不超过液晶屏分辨率,否则可能会导致图像显示异常。
#include "Arduino.h"
#include "Image.h"
#include "ST7789V.h"
#include "FS.h"
#include "FFat.h"
using namespace K210;
ST7789V lcd(240, 320);
// 初始化函数
void setup()
{
Serial.begin(115200);
while (!Serial) {
;
}
// 挂载文件系统
if(!FFat.begin()){
Serial.println("FFat Mount Failed");
return;
}
// 初始化LCD
lcd.begin();
// lcd.invertDisplay(1);
lcd.setRotation(3);
lcd.setTextSize(3);
// 加载nmp图片
Image *img = Image::load_bmp(FFat, "/2.bmp");
if(img)
{
Serial.printf("width %d, height %d, bpp %d, pixel %p\n", img->w, img->h, img->bpp, img->pixel);
// 转换图片格式为rgb565
Image *rgb565 = img->to_rgb565();
if(rgb565)
{
// 显示
lcd.drawImage(rgb565);
delete rgb565;
}
delete img;
}
lcd.refresh();
}
// 主循环
void loop()
{
// lcd.setCursor(0, 0);
// lcd.printf("Test1234");
// lcd.refresh();
}
例程 - save_camera_image.ino#
获取Camera图像并保存为bmp文件
以下是一个摄像头拍照并保存为bmp的应用示例,可以将拍摄的画面显示在ST7789V液晶屏幕上,并支持通过按键保存拍摄的照片到SD卡上。
程序中使用了多个外部库和组件,包括OV2640驱动、ST7789V液晶屏驱动、FS文件系统和FFat库。在setup函数中进行了初始化操作,包括初始化串口、LCD屏幕、文件系统以及相机驱动等。在loop函数中实现了拍照、保存照片的功能,通过按键控制保存行为。
需要注意的点有:
确保外部组件连接正确并且工作正常,例如摄像头模块、LCD屏幕、SD卡等。
对于每个外部组件,需要按照其提供的API或者手册进行正确的初始化操作,否则可能会导致程序无法正常工作。
在保存照片时,需要确保SD卡已经正确挂载,并且具有写入权限。
#include "Arduino.h" // 引入Arduino库
#include "Image.h" // 引入Image库
#include "GC0328.h"
#include "OV2640.h" // 引入OV2640库
#include "ST7789V.h" // 引入ST7789V库
#include "FS.h" // 引入FS库
#include "FFat.h" // 引入FFat库
using namespace K210; // 使用K210命名空间
#define KEY_PIN (16) // 定义按键引脚
// GC0328 cam;
OV2640 cam; // 定义OV2640摄像头对象
ST7789V lcd(240, 320); // 定义ST7789V显示屏对象
Image *img_ai, *img_display; // 定义Image对象指针
void setup() // 初始化函数
{
camera_buffers_t buff; // 定义摄像头缓存对象
Serial.begin(115200); // 初始化串口通信
while (!Serial) { // 等待串口连接
;
}
lcd.begin(); // 初始化显示屏
// lcd.invertDisplay(1);
lcd.setRotation(3); // 设置显示屏旋转方向
lcd.setTextSize(2); // 设置显示屏字体大小
lcd.setCursor(0,0); // 设置显示屏光标位置
if(!FFat.begin()){ // 挂载FFat文件系统
lcd.printf("FFat Mount Failed\n"); // 显示挂载失败信息
Serial.printf("FFat Mount Failed"); // 输出挂载失败信息
while(1) {} // 挂载失败,进入死循环
}
if(false == FFat.mkdir("/img")) // 创建/img目录
{
lcd.printf("FFat mkdir Failed\n"); // 显示创建失败信息
Serial.printf("FFat mkdir Failed"); // 输出创建失败信息
while(1) {} // 创建失败,进入死循环
}
if(0x00 != cam.reset(FRAMESIZE_QVGA)) // 摄像头复位
{
lcd.printf("camera reset failed\n"); // 显示复位失败信息
Serial.printf("camera reset failed\n"); // 输出复位失败信息
while(1) {} // 复位失败,进入死循环
}
cam.get_buffers(&buff); // 获取摄像头缓存
if((NULL == buff.disply) || (NULL == buff.ai.r)) // 判断缓存是否获取成功
{
lcd.printf("get camera buffers failed\n"); // 显示获取失败信息
Serial.printf("get camera buffers failed\n"); // 输出获取失败信息
while(1) {} // 获取失败,进入死循环
}
img_ai = new Image(cam.width(), cam.height(), IMAGE_FORMAT_R8G8B8, buff.ai.r); // 创建Image对象
img_display = new Image(cam.width(), cam.height(), IMAGE_FORMAT_RGB565, buff.disply); // 创建Image对象
lcd.setFrameBuffer(img_display); // 设置显示屏帧缓存
pinMode(KEY_PIN, INPUT_PULLUP); // 设置按键引脚为输入模式
}
void loop() // 主循环函数
{
if(0x00 != cam.snapshot()) // 拍摄照片
{
lcd.setCursor(0,0); // 设置光标位置
lcd.printf("camera snapshot failed\n"); // 显示拍摄失败信息
lcd.refresh(); // 刷新显示屏
Serial.printf("camera snapshot failed\n"); // 输出拍摄失败信息
return; // 返回
}
if(0x00 == digitalRead(KEY_PIN)) // 判断按键是否按下
{
char name[32]; // 定义文件名
int result = -1; // 定义保存结果
Image *rgb888 = NULL; // 定义Image对象指针
snprintf(name, sizeof(name), "/img/img_%ld.bmp", millis()); // 格式化文件名
if((rgb888 = img_display->to_rgb888())) // 将Image对象转换为RGB888格式
{
result = rgb888->save_bmp(FFat, name); // 保存照片
delete rgb888; // 释放Image对象
}
if(0x00 == result) // 判断保存结果
{
lcd.setCursor(0,0); // 设置光标位置
lcd.printf("Save succ, %s", name); // 显示保存成功信息
}
else
{
lcd.setCursor(0,0); // 设置光标位置
lcd.printf("Save fail, %s", name); // 显示保存失败信息
}
lcd.refresh(); // 刷新显示屏
delay(300); // 延时300ms
}
else
{
lcd.refresh(); // 刷新显示屏
}
}