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卡则为 FFat
- namebmp图像文件路径
返回值#
- 语法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卡则为 FFat
- namebmp图像文件路径
返回值#
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(); // 刷新显示屏
    }
}