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

  • name 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卡则为 FFat

  • name 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函数中实现了拍照、保存照片的功能,通过按键控制保存行为。

需要注意的点有:

  1. 确保外部组件连接正确并且工作正常,例如摄像头模块、LCD屏幕、SD卡等。

  2. 对于每个外部组件,需要按照其提供的API或者手册进行正确的初始化操作,否则可能会导致程序无法正常工作。

  3. 在保存照片时,需要确保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(); // 刷新显示屏
    }
}