10074浏览
查看: 10074|回复: 1

AI驱动的牙模分类器—基于Edge Impulse平台

[复制链接]
本帖最后由 小同学 于 2022-11-15 13:43 编辑

牙科行业最近涌现一股热潮,越来越多相关机构通过3D打印牙模(印模)来识别和检测牙齿问题。对牙科技师来说,与石膏牙模相比,3D打印的牙模更坚固更稳定也更精确,因为3D打印牙膜可以在经过多次检查后却不丢失细节。与铣削牙模相比,3D打印牙模的结构更复杂,细节更丰富。最重要的是,3D打印牙模很省时间,牙医对病人进行几秒口内扫描后,就可以复制下牙齿数据并传输给牙科技师。3D打印牙模除了能为患者提供更舒适的体验,基于此的数字化工作流程通常能让扫描结果更准确,消除潜在的错误和不准确。
尽管3D打印对牙科行业来说还是比较新的技术,但对于做牙科产品来说,3D打印牙模有无数好处,也蕴藏许多机会。然而,在细读了最近关于3D打印在牙科行业应用的文章后,我注意到,其实能用来帮助牙科技师检查3D打印牙模准确性和效能的工具或方法并不多。因此,我决定搭建一个能给用户良好体验的易使用的设备,它采用物体检测模型对3D打印牙模进行分类,希望能帮助牙科技师检测牙模的准确性及故障。
AI驱动的牙模分类器—基于Edge Impulse平台图66

物料清单
硬件
索尼 Spresense主板 × 1
索尼Spresense扩展板 × 1
索尼Spresense相机板 × 1
DFRobot微型(嵌入式)热敏打印机 × 1
2.8'' 240x320 TFT LCD触摸屏(ILI9341) × 1
Creality CR-200B 3D打印机 × 1
Creality HALOT-ONE CL-60 SLA 3D打印机 × 1
闪存卡,MicroSD存储卡 × 1
RGB LED模块 × 1
Sparkfun按钮(6x6) × 3
小米20000 mAh 3 Pro Type-C充电宝 × 1
USB降压/升压转换器 × 1
半尺寸面包板 × 1
迷你面包板 × 1
10mm M3公对母黄铜六角支架垫片 × 4
M3螺丝 × 1
跳线 × 1
软件:
Edge Impulse Studio
Arduino IDE
Autodesk Fushion 360
Ultimaker Cura
HALOT BOX
工具:
热熔枪(通用)

背景:
为了准确预测和解释牙模的类别,我需要从3D打印牙模实物中收集数据,以此有效训练我的物体检测模型。因此,我购买了许多牙模STL文件,用不同的标签进行标识,并用我的SLA(HALOT-ONE)和FDM(CR-200B)3D打印机将它们打印出来。
  • 中央大牙
  • 拮抗牙
  • 上排牙正畸
  • 反颌
  • 上排畸形牙
  • 下排畸形牙
  • 失败的牙模
  • 植牙
由于索尼Spresense开发板具备高性能,主要用于传感器分析、机器学习、图像处理和数据过滤的边缘计算,所以我决定在这个项目中使用索尼Spresense。为了捕捉图像并将其存储在SD卡上来训练我的物体检测模型,我将Spresense扩展板和Spresense相机板连接到Spresense主板(CXD5602)。然后我用TFT LCD触摸屏(ILI9341)来显示视频和捕捉的图像。此外,我还连接了一台微型热敏打印机,在索尼Spresense上运行物体检测模型后,打印机就能打印出检测结果。
在拍摄了3D打印牙模照片,完成数据采集后,我用Edge Impulse建立了物体检测模型来对牙模的准确性级别进行预测。我用Edge Impulse FOMO算法来训练我的模型,这是种新型的机器学习算法,支持对高度有限的设备进行物体检测。Edge Impulse几乎兼容所有微控制器和开发板,所以我在索尼Spresense上上传和运行模型时没有遇到任何问题。并且在捕捉和存储图片时,我就已经在文件名中添加上三个主要类别作为标签。
  • Cast(牙模)
  • Failed(失败的牙模)
  • Implant(植牙)
在用FOMO算法训练和测试了物体检测模型后,我在索尼Spresense上配置并上传了该模型。所以设备能够独立运行模型来检测牙模精度级别,无需添加任何额外的程序。
最后,为了使设备3D打印牙模时尽可能坚固、结实、紧凑,我以牙科为主题设计了一个带有侧滑盖(可3D打印)的外壳。
以上就是我的项目概况。
AI驱动的牙模分类器—基于Edge Impulse平台图63
AI驱动的牙模分类器—基于Edge Impulse平台图64
AI驱动的牙模分类器—基于Edge Impulse平台图65


步骤
在下面的步骤中,你可以找到更详细的信息,包括代码、捕捉牙模图片、在SD卡上存储图片、用Edge Impulse建立物体检测模型(FOMO),以及在Sony Spresense上运行。
1. 设计并打印牙科主题的外壳
为了给用户提供良好体验,我决定设计一个坚固结实的外壳,以便完美地捕捉牙模图像。为了尽可能隔绝灰尘以及避免接线松动,我还加了一个侧滑盖。此外,我还在设备上固定一个牙齿仿制品,并在滑动侧盖上印了个牙齿标志来强调主题
我在Autodesk Fusion 360中设计了外壳机构和侧滑盖。你可以在下方下载相关STL文件。
AI驱动的牙模分类器—基于Edge Impulse平台图1
AI驱动的牙模分类器—基于Edge Impulse平台图2
AI驱动的牙模分类器—基于Edge Impulse平台图3
AI驱动的牙模分类器—基于Edge Impulse平台图4
对于固定在微型热敏打印机顶部的牙齿仿制品,我用了Thingiverse的这个模型制作:
然后,我在Ultimaker Cura中对所有3D模型(STL文件)进行了切片。
AI驱动的牙模分类器—基于Edge Impulse平台图6
AI驱动的牙模分类器—基于Edge Impulse平台图7
AI驱动的牙模分类器—基于Edge Impulse平台图5
我希望整套外壳更结实更贴合主题,所以我用了这些PLA材料:
  • 红色材料
  • 哑光乳白ePLA材料
最后,我用Creality CR-200B 3D打印机打印了所有部件(模型)。这是我第一台打印机,真的很好用,打印效果真的很棒。
AI驱动的牙模分类器—基于Edge Impulse平台图8
在第一次使用前,请移除不必要的电线,在导轨上涂点润滑油。
AI驱动的牙模分类器—基于Edge Impulse平台图9
AI驱动的牙模分类器—基于Edge Impulse平台图10
测试喷头和热床温度。
AI驱动的牙模分类器—基于Edge Impulse平台图11
进入Settings➡Levelling,利用调平螺母调整四个预定义的点。
AI驱动的牙模分类器—基于Edge Impulse平台图12
AI驱动的牙模分类器—基于Edge Impulse平台图13
AI驱动的牙模分类器—基于Edge Impulse平台图14
AI驱动的牙模分类器—基于Edge Impulse平台图15
最后,装上线轴支架,向挤出机输送机丝。
AI驱动的牙模分类器—基于Edge Impulse平台图16
由于Cura不支持CR-200B,所以选择Ender-3文件,并将构建的文件尺寸改为200*200*200毫米。另外,为了补偿喷嘴位置,在挤出机1(Extruder 1)页面上将喷嘴的X和Y偏移值设置为-10毫米。
AI驱动的牙模分类器—基于Edge Impulse平台图17
AI驱动的牙模分类器—基于Edge Impulse平台图18

之后组装外壳,进行连接和调整。
  1. // Connections
  2. // Sony Spresense (w/ Extension Board) :  
  3. //                                2.8'' 240x320 TFT LCD Touch Screen (ILI9341)
  4. // D7   --------------------------- CS
  5. // D8   --------------------------- RESET
  6. // D9   --------------------------- D/C
  7. // MOSI --------------------------- SDI (MOSI)
  8. // SCK  --------------------------- SCK
  9. // 3.3V --------------------------- LED
  10. // MISO --------------------------- SDO(MISO)
  11. //                                Tiny (Embedded) Thermal Printer
  12. // TX   --------------------------- RX
  13. // RX   --------------------------- TX
  14. // GND  --------------------------- GND
  15. //                                Control Button (A)
  16. // D2   --------------------------- +
  17. //                                Control Button (B)
  18. // D4   --------------------------- +
  19. //                                Control Button (C)
  20. // D14  --------------------------- +
  21. //                                Keyes 10mm RGB LED Module (140C05)
  22. // D3   --------------------------- R
  23. // D5   --------------------------- G
  24. // D6   --------------------------- B
复制代码
首先,把Spresense扩展板和Spresense相机板连接到Spresense主板上,以便于捕捉图像和连接其他元件。Spresense主板的工作电压为1.8V,如果没有支持5V或3.3V电压的扩展板,最好不要将元件直接连接到主板上。
然后,在扩展板上连接了TFT液晶触摸屏(ILI9341),以便在运行物体检测(FOMO)模型后,显示视频、捕获的图像和预测的牙膜精度类别(等级)。我还用微型(嵌入式)热敏打印机来打印预测的类别及详细信息,以便于推断出牙模的规格和精度。
由于Spresense扩展板由于其工作电压和电流而不能为微型(嵌入式)热敏打印机供电,我将USB降压/升压转换器连接到我的小米充电宝,来为热敏打印机稳定供应9V电压。输入电压(电压范围为5~9V)更高,打印速度会更快,打印的内容也会更清晰。
为了在采集牙模图像并存储在SD卡上时给文件名附加标签,我添加了三个控制按钮(6x6),如下意图所示。此外,我还添加了共阳极RGB LED模块来指示功能运行的结果。
连接好面包板后,用热胶枪加固面包板的连接点。
AI驱动的牙模分类器—基于Edge Impulse平台图19
AI驱动的牙模分类器—基于Edge Impulse平台图20
打印完所有部件(模型)后,我用热胶枪将所有部件固定在外壳上的相应插槽中。
然后,我将侧滑盖插入外壳凹痕中放置好。
AI驱动的牙模分类器—基于Edge Impulse平台图21
AI驱动的牙模分类器—基于Edge Impulse平台图22
AI驱动的牙模分类器—基于Edge Impulse平台图23
AI驱动的牙模分类器—基于Edge Impulse平台图24
最后我用热胶枪把牙齿仿制品固定在热敏打印机上。
AI驱动的牙模分类器—基于Edge Impulse平台图25

2. 打印大量牙模(模型)
因为我需要采集大量牙模的图像,我决定用我的树脂3D打印机打印真实的牙模。所以我从Cults公司购买了大量带有不同标签的牙模。
  • 中央大牙
  • 拮抗牙
  • 上排牙正畸
  • 反颌
  • 上排畸形牙
  • 下排畸形牙
  • 失败的牙模
  • 植牙
我用最新的HALOT BOX打印机把所有STL文件的3D模型切割出来。
AI驱动的牙模分类器—基于Edge Impulse平台图26
AI驱动的牙模分类器—基于Edge Impulse平台图27
为了打印出和牙科技师做的模型类似的牙模,我用了一种特殊的树脂:
  • 对紫外线敏感的可用于做牙模的树脂(绿色)
然后用Creality HALOT-ONE树脂3D打印机打印出所有的牙模,效果很好。
另外,我也在Ultimaker Cura中再次对3D牙模进行切片,用FDM 3D打印机(CR-200B)打印,来提高物体检测模型对于不同材料或方法做的牙模的准确性。
AI驱动的牙模分类器—基于Edge Impulse平台图28
打印完牙模后,我通过Spresense摄像板捕捉它们的图像,具体步骤如下。
AI驱动的牙模分类器—基于Edge Impulse平台图29

3. 在Arduino IDE上设置Sony Spresense
在进行之后的步骤之前,我需要先在Arduino IDE上设置Sony Spresense,并安装本项目所需的库。

(1)首先,将提供的遮光膜安装在Spresense主板的IC4上,以防止直接或接近强烈光源时发生故障,比如阳光或明亮的灯光。
(2)之后在Arduino IDE中添加Sony Spresense板子所需的文件包,进入File➡Preferences,并在Additional Boards Manager URLs中粘贴下面链接。
(3)这个文件包还包括扩展板模块(如SD卡模块)和相机板所需的库。
https://github.com/sonydevworld/spresense-arduino-compatible/releases/download/generic/package_spresense_index.json
AI驱动的牙模分类器—基于Edge Impulse平台图30
(4)然后,进入Tools➡Board➡Boards Manager搜索Spresense,来安装所需芯片。
AI驱动的牙模分类器—基于Edge Impulse平台图32
AI驱动的牙模分类器—基于Edge Impulse平台图31
(5)安装好芯片后,进入Tools➡Board➡Spresense Boards,选择Spresense。

AI驱动的牙模分类器—基于Edge Impulse平台图33
(6)为了使用最新版本的Spresense Arduino库,要更新Spresense固件。进入Tools➡Programmer➡Spresense Firmware Updater。然后,选择Burn Bootloader

AI驱动的牙模分类器—基于Edge Impulse平台图34
AI驱动的牙模分类器—基于Edge Impulse平台图35
(7)最后,下载热敏打印机和TFT LCD触摸屏所需的库。
Adafruit热敏打印机库 | 下载
Adafruit_ILI9341 | 下载
Adafruit-GFX-库 | 下载


3.1 在TFT LCD触摸屏上显示图像
为了在TFT LCD触摸屏上成功显示图像(RGB),我要将PNG或JPG文件转换成.c(源)文件格式。
(1)首先,下载GIMP
(2)然后,上传一张图片(RGB),并进入Image➡Scale Image来调整上传图片的大小。
(3)最后导出.c(源)格式文件。
(4)为了生成正确格式的数据组,仅选择Save as RGB565 (16-bit)。
AI驱动的牙模分类器—基于Edge Impulse平台图36
(5)导出图像后,把生成的数据组导入进代码,在显示屏中打印出来。
  1. tft.drawRGBBitmap(10, 10, (uint16_t*)(data_collect.pixel_data), (int16_t)data_collect.width, (int16_t)data_collect.height);
复制代码
AI驱动的牙模分类器—基于Edge Impulse平台图37

3.2. 用微型热敏打印机打印位图
(1)首先,用图像编辑程序将图像(黑白)保存为1-bit BMP,比如Windows上内置的画画程序。
(2)然后,安装并运行LCD Assistant
(3)上传单色位图,设置里的Byte orientation选择Horizontal。
(4)必要的话可以改变图像宽度,因为LCD Assistant和热敏打印机按八个像素的水平组处理图像。所以如果图像宽度不是8的倍数,就会被裁剪到8像素。
(5)转换位图为数据组并保存。
(6)最后,将数据数组添加到代码中,用热敏打印机打印。
  1. printer.printBitmap(80, 80, dental_logo);
复制代码
AI驱动的牙模分类器—基于Edge Impulse平台图38

4. 用Sony Spresense捕捉并存储图像

设置了Sony Spresense并安装了所需的库后,对Sony Spresense进行编程来捕捉牙模图像,将其存储在SD卡上,创建为有标签的样本集,供Edge Impulse物体检测(FOMO)模型使用。
同时我用Sony Spresense上的控制按钮,来选择牙模精度类别。在选择精度类别,Sony Spresense会捕捉一张图片,将所选类别的编号与当前日期和时间附加到文件名上,然后将捕捉的图片保存到SD卡上。
  • 控制按钮 (A) ➡ Cast [0]
  • 控制键(B) ➡ Failed [1]
  • 控制按钮 (C) ➡ Implant [2]
你可以下载dental_model_classifier_collect.ino文件,尝试检查通过Sony Spresense捕捉图像并存储到SD卡的代码。
(1)Include所需的库。
  1. #include <Camera.h>
  2. #include <SDHCI.h>
  3. #include <RTC.h>
  4. #include <Adafruit_GFX.h>
  5. #include <Adafruit_ILI9341.h>
复制代码
(2)Include用于ILI9341 TFT LCD触摸屏的显卡(彩色位图)。
(3)定义相机设置。
(4)定义相机错误对象。
(5)初始化SD速度等级。
(6)定义LCD 触摸屏(ILI9341)所需的引脚。
(7)使用硬件(Spresense、SCK、MISO、MOSI上的)SPI,并定义所需的引脚来初始化ILI9341 TFT LCD触摸屏。

  1. CamErr err;
  2. // Initialize the SD class.
  3. SDClass  theSD;
  4. // Define the required pins for the 240x320 TFT LCD Touch Screen (ILI9341):
  5. #define TFT_CS   7
  6. #define TFT_RST  8
  7. #define TFT_DC   9
  8. // Use hardware SPI (on Spresense, SCK, MISO, MOSI) and the above for DC/CS/RST.
  9. Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
复制代码
(8)初始化RTC定时器,并将日期和时间设置为编译后的日期和时间。
  1. RTC.begin();
  2.   RtcTime compiledDateTime(__DATE__, __TIME__);
  3.   RTC.setTime(compiledDateTime);
复制代码
(9)初始化ILI9341 TFT LCD触摸屏。
  1. tft.begin();
  2.   tft.setRotation(TFT_ROTATION);
  3.   tft.fillScreen(ILI9341_NAVY);
  4.   tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(2);
  5.   tft.setCursor(10, 10);
  6.   tft.println("Initializing...");
复制代码
(10)检查Spresense主板和扩展板上SD卡的连接状态。
  1. while(!theSD.begin()){
  2.     Serial.println("Insert SD card.");
  3.     adjustColor(1,0,0);
  4.     sleep(1);
  5.   }
  6.   Serial.println("SD card is detected successfully!\n");
复制代码
(11)初始化相机板
(12)启动播放视频。
(13)设置自动白平衡参数。
(14)设置静态图片参数。
(15)如果相机板出现错误,在串扣监视器上打印错误细节。
  1. Serial.println("Camera initializing...");
  2.   err = theCamera.begin();
  3.   if(err != CAM_ERR_SUCCESS) printError(err);
  4.   // Start video stream and print errors, if any.
  5.   Serial.println("Starting streaming...");
  6.   err = theCamera.startStreaming(true, CamCB);
  7.   if(err != CAM_ERR_SUCCESS) printError(err);
  8.   // Set the Auto white balance parameter and print errors, if any.
  9.   Serial.println("Setting the Auto white balance parameter...");
  10.   err = theCamera.setAutoWhiteBalanceMode(g_wb);
  11.   if(err != CAM_ERR_SUCCESS) printError(err);
  12.   // Set the still picture parameters and print errors, if any.
  13.   Serial.println("Setting the still picture parameters...\n");
  14.   err = theCamera.setStillPictureImageFormat(g_width, g_height, g_img_fmt, g_divisor);
  15.   if(err != CAM_ERR_SUCCESS) printError(err);
复制代码
在CamCB函数中:
  • 检查 img 实例是否可用。
  • 转换图像数据格式为RGB565,以便在TFT触摸屏上显示捕获的图像。
  • 在串口监视上打印最近捕获的图像数据。
  • 注意:除非视频被中断,否则该函数连续循环运行。
  1. void CamCB(CamImage img){
  2.   // Check whether the img instance is available or not.
  3.   if (img.isAvailable()){   
  4.     // Convert the image data format to RGB565 so as to display images on the ILI9341 TFT screen.
  5.     img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);
  6.     /* You can use image data directly by using getImgSize() and getImgBuff().
  7.      * for displaying image to a display, etc. */
  8.     tft.drawRGBBitmap(0, 0, (uint16_t *)img.getImgBuff(), 320, 240);
  9.     Serial.print("Image data size => "); Serial.print(img.getImgSize(), DEC); Serial.print(" , ");
  10.     Serial.print("Image buffer address => "); Serial.println((unsigned long)img.getImgBuff(), HEX);
  11.   }else{
  12.     Serial.println("Failed to get video stream image!");
  13.   }
  14. }
复制代码
takePicture函数中:
  • 以当下设置拍摄图片。
  • 暂停播放视频,出现错误的话打印错误。
  • 获取由RTC模块计算的当前日期和时间。
  • 定义文件名,包括选定的等级号和当前日期和时间。
  • 如果给定的文件名在SD卡上已经存在,请提前删除。
  • 将最近拍摄的图片保存到SD卡上。
  • 在触摸屏上显示最近保存的图像信息。
  • 恢复播放视频,出现错误的话打印错误。
  1. void takePicture(int _class){
  2.   char filename[30] = {0};
  3.   // Take a picture with the given still picture settings.
  4.   CamImage img = theCamera.takePicture();
  5.   if(img.isAvailable()){
  6.     // Pause video stream and print errors, if any.
  7.     adjustColor(1,1,0);
  8.     Serial.println("\nPausing streaming...\n");
  9.     err = theCamera.startStreaming(false, CamCB);
  10.     if(err != CAM_ERR_SUCCESS) printError(err);
  11.     // Get the current date and time.
  12.     RtcTime rtc;
  13.     rtc = RTC.getTime();
  14.     // Define the file name.
  15.     sprintf(filename, "%d_D_%04d.%02d.%02d__%02d.%02d.%02d.%s", _class, rtc.year(), rtc.month(), rtc.day(), rtc.hour(), rtc.minute(), rtc.second(), "JPG");
  16.     // If the same file name exists, remove it in advance to prevent file appending.
  17.     theSD.remove(filename);
  18.     // Save the recently captured picture to the SD card.
  19.     File myFile = theSD.open(filename, FILE_WRITE);
  20.     myFile.write(img.getImgBuff(), img.getImgSize());
  21.     myFile.close();
  22.     Serial.println("Image captured successfully!");
  23.     Serial.print("Selected Class: "); Serial.println(_class);
  24.     Serial.printf("Name: %s\n", filename);
  25.     Serial.printf("Resolution: %dx%d\n", img.getWidth(), img.getHeight());
  26.     Serial.printf("Memory Size: %.2f / %.2f [KB]\n", img.getImgSize() / 1024.0, img.getImgBuffSize() / 1024.0);
  27.     // Display the recently saved image information on the ILI9341 TFT screen.
  28.     int c_x = 10, c_y = 100, r_x = 300, r_y = 120, r = 10, offset = 10, l = 15;
  29.     tft.drawRGBBitmap(10, 10, (uint16_t*)(data_collect.pixel_data), (int16_t)data_collect.width, (int16_t)data_collect.height);
  30.     tft.fillRoundRect(c_x, c_y, r_x, r_y, r, ILI9341_WHITE);
  31.     tft.fillRoundRect(c_x+offset, c_y+offset, r_x-(2*offset), r_y-(2*offset), r, ILI9341_DARKGREEN);
  32.     tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1);
  33.     tft.setCursor(c_x+(2*offset), c_y+(2*offset));
  34.     tft.printf("Name: %s\n", filename);
  35.     tft.setCursor(c_x+(2*offset), c_y+(2*offset)+l);
  36.     tft.printf("Resolution: %dx%d\n", img.getWidth(), img.getHeight());
  37.     tft.setCursor(c_x+(2*offset), c_y+(2*offset)+(2*l));
  38.     tft.printf("Selected Class: %d", _class);
  39.     sleep(5);
  40.     // Resume video stream and print errors, if any.
  41.     adjustColor(0,1,0);
  42.     sleep(2);
  43.     Serial.println("\nResuming streaming...\n");
  44.     err = theCamera.startStreaming(true, CamCB);
  45.     if(err != CAM_ERR_SUCCESS) printError(err);
  46.   }else{
  47.     Serial.println("Failed to take a picture!");
  48.     adjustColor(1,0,0);
  49.     sleep(2);
  50.   }
  51. }
复制代码
  • 根据按下的控制按钮(A,B,或C),将选定的牙模精度类别编号与当前日期和时间附加到文件名中,并将最近拍摄的图片保存到SD卡中。
  1.   if(!digitalRead(button_A)) make_a_get_request("0");
  2.   if(!digitalRead(button_B)) make_a_get_request("1");
  3.   if(!digitalRead(button_C)) make_a_get_request("2");
复制代码
然后将捕捉的图片作为样本保存到SD卡上。
在Sony Spresense上传并运行用于捕捉图片并保存到SD卡的代码后:
如果SD卡和相机板与Spresense主板连接成功,设备的LED灯就会变成蓝色。
AI驱动的牙模分类器—基于Edge Impulse平台图39
然后,LED变成默认的红色,并且触摸屏上显示视频。
如果按下控制按钮(A),设备会暂停播放视频并捕捉图片。如果设备成功捕捉到图片,LED变成黄色,将Cast[0]牙模类别编号与当前日期和时间附加到文件名上,并将最近捕捉到的图片存储在SD卡上。
然后触摸屏上显示捕获的图像信息。

  • 名称
  • 分辨率
  • 选择的类别
最后,设备恢复播放视频,LED变成绿色。
AI驱动的牙模分类器—基于Edge Impulse平台图40
AI驱动的牙模分类器—基于Edge Impulse平台图41
AI驱动的牙模分类器—基于Edge Impulse平台图42
AI驱动的牙模分类器—基于Edge Impulse平台图43
如果控制按钮(B)被按下,设备会暂停视频并捕捉图片。如果设备成功捕捉到图片,LED变成黄色,将Failed[1]牙模类别编号与当前日期和时间附加到文件名上,并将最近捕捉到的图片存储在SD卡上。
然后触摸屏上显示捕获的图像信息。
  • 名称
  • 分辨率
  • 选择的类别
最后,设备恢复播放视频,LED变成绿色。
AI驱动的牙模分类器—基于Edge Impulse平台图68
AI驱动的牙模分类器—基于Edge Impulse平台图69

AI驱动的牙模分类器—基于Edge Impulse平台图70
如果控制按钮(C)被按下,设备会暂停视频并捕捉图片。如果设备成功捕捉到图片,LED变成黄色,将Implant[2]牙模类别编号与当前日期和时间附加到文件名上,并将最近捕捉到的图片存储在SD卡上。
然后触摸屏上显示拍摄的图像信息。
  • 名称
  • 分辨率
  • 选择的类别
最后,设备恢复播放视频,LED变成绿色。
如果Sony Spresense在操作过程中出现错误,LED变成红色,并且串口监视器打印出错误细节。

AI驱动的牙模分类器—基于Edge Impulse平台图71
此外,串口监视器还会打印通知事项和捕获的图像数据,以便进行调试。
目前实验进行得很顺利。
AI驱动的牙模分类器—基于Edge Impulse平台图67

在捕捉并保存这么多不同标签的牙模图像后,我建立了我的数据集,可以作为物体检测(FOMO)模型的训练和测试样

5. 用Edge Impulse建立物体检测(FOMO)模型
现在我开始着手用Edge Impulse建立我的物体检测(FOMO)模型,来对牙模类别进行预测。
虽然Edge Impulse支持JPG或PNG文件直接作为样本上传,但每个训练或测试样本都需要手动标注。所以我需要遵循以下步骤来格式化我的数据集,以便准确地训练我的物体检测模型。
  • 数据缩放(调整大小)
  • 数据标签化
现在预处理数据集,在Edge Impulse上给图像样本加标签。
  • 0 - Cast
  • 1 - Failed
  • 2 - Implant
Edge Impulse会自动优化预测模型的大小和准确性,并将训练好的模型作为Arduino库。所有我在调整好数据集并进行预处理标记后,我的物体检测模型较准确,并能在索尼Spresense上运行。你可以在Edge Impulse上检查我的物体检测(FOMO)模型


5.1 将图像上传到Edge Impulse并进行标记
我把测试好的图像样本上传到Edge Impulse上的项目中,并给每个样本贴上牙模精度类别的标签。
(1)首先,注册Edge Impulse并创建一个新项目。
AI驱动的牙模分类器—基于Edge Impulse平台图44
(2)进入Dashboard➡Project info➡Labeling method,选择Bounding boxes (object detection)。
AI驱动的牙模分类器—基于Edge Impulse平台图45
(3)进入Data acquisition页面,点击Upload existing data。
AI驱动的牙模分类器—基于Edge Impulse平台图46
(4)选择训练或测试的类别,选择图像文件,点击Begin upload
AI驱动的牙模分类器—基于Edge Impulse平台图47
(5)在成功上传数据集后,为每个图像样本标注了精度类别(Cast,Failed或者 Implant)。
(6)进入Data acquisition➡Labeling queue (Object detection labeling),显示了在给定数据集中剩余的所有未标记的图像。
(7)最后,选择一张未标记的图像,在物体周围拖动边界框,点击Save labels,重复这个过程,直到整个数据集被标记。
AI驱动的牙模分类器—基于Edge Impulse平台图48

AI驱动的牙模分类器—基于Edge Impulse平台图49

5.2 以牙模类别训练FOMO模型
在成功地标记了测试样本后,我设计了一个FOMO模型并在牙模精度类别上训练它。
(1)进入Create impulse页面,将图像宽度和高度参数设置为160。然后,选择调整大小模式参数为Fit shortest axis
(2)选择Image preprocessing和Object Detection (Images) learning,点击Save Impulse。
AI驱动的牙模分类器—基于Edge Impulse平台图50
(3)生成特征之前,进入Image页面,将Color depth设置为Grayscale。然后点击Save parameters。
AI驱动的牙模分类器—基于Edge Impulse平台图51
(4)保存后,点击Generate features,将Image preprocessing应用于训练图像样本。
AI驱动的牙模分类器—基于Edge Impulse平台图52
AI驱动的牙模分类器—基于Edge Impulse平台图53
(5)进入object detection页面,点击Start training。
AI驱动的牙模分类器—基于Edge Impulse平台图54

我修改了neural network设置和架构,建立了一个具有高准确性和有效性的物体检测模型。
Neural network设置:
  • 训练周期数➡150
  • 学习率➡0.025
  • 验证数据集大小➡10
神经网络结构:
  • FOMO (Faster Objects, More Objects) MobileNetV2 0.35
在生成特征并使用训练样本训练我的FOMO模型后,Edge Impulse评估的F1分数(准确率)为84.6%。并且我仍在收集数据以改进数据集。
AI驱动的牙模分类器—基于Edge Impulse平台图55


5.3 评估模型准确性
之后我用测试图像样本来测试模型准确性和有效性。
该模型的评估准确率为87.50%。
(1)为了更好验证,进入Model testing页面,点击Classify all。
AI驱动的牙模分类器—基于Edge Impulse平台图56
验证模型后,将它优化为可自定义的Arduino库。
(2)进入Deployment页面,选择Arduino
(3)然后选择Quantized (int8) optimization。
(4)最后,点击Build将模型下载为Arduino库。

AI驱动的牙模分类器—基于Edge Impulse平台图57
AI驱动的牙模分类器—基于Edge Impulse平台图58
AI驱动的牙模分类器—基于Edge Impulse平台图59

6. 在索尼Spresense上设置Edge Impulse FOMO模型
将模型下载为ZIP格式的Arduino库后,进入Sketch➡Include Library➡Add.ZIP Library...
然后,把Dental_Model_Classifier_inferencing.h文件导入Edge Impulse物体检测模型。
  1. #include <Dental_Model_Classifier_inferencing.h>
复制代码
将模型成功导入Arduino IDE后,我采用了Sony Spresense的控制按钮(B)来运行inferences。
(1)进入Press➡Run Inference
你可以下载dental_model_classifier_run_model.ino文件来检查在Sony Spresense上运行Edge Impulse神经网络模型的代码。
(2)Include所需的库。
  1. #include <Camera.h>
  2. #include <Adafruit_GFX.h>
  3. #include <Adafruit_ILI9341.h>
  4. #include "Adafruit_Thermal.h"
  5. // Include the Edge Impulse FOMO model converted to an Arduino library:
  6. #include <Dental_Model_Classifier_inferencing.h>
复制代码
(3)定义所需的参数,用Edge Impulse模型运行inference。
(4)定义牙模精度类别名称和颜色代码。
  1. #define EI_CAMERA_RAW_FRAME_BUFFER_COLS   1280
  2. #define EI_CAMERA_RAW_FRAME_BUFFER_ROWS   960
  3. #define CAPTURED_IMAGE_BUFFER_COLS        320
  4. #define CAPTURED_IMAGE_BUFFER_ROWS        320
  5. static uint8_t *ei_camera_capture_out = NULL;
  6. // Define the dental model category (class) names and color codes:
  7. const char *classes[] = {"Cast", "Failed", "Implant"};
  8. uint32_t color_codes[] = {ILI9341_GREEN, ILI9341_MAGENTA, ILI9341_ORANGE};
复制代码
(5)Include触摸屏的图形(彩色位图)。
(6)Include微型(嵌入式)热敏打印机的图标。
(7)定义相机设置。
(8)定义相机错误对象。
(9)定义热敏打印机对象,通过Spresense的硬串口(Serial2)发送命令。
  1. Adafruit_Thermal printer(&Serial2);
复制代码
(10)初始化硬串口(Serial2)和微型(嵌入式)热敏打印机。
  1. Serial2.begin(9600);
  2.   // Initialize the thermal printer.  
  3.   printer.begin();
复制代码
(11)在print_thermal函数中,打印预测的牙模精度类别信息和细节。
  1. void print_thermal(int _class){
  2.   printer.printBitmap(80, 80, dental_logo);
  3.   printer.boldOn();
  4.   printer.justify('R');
  5.   printer.setSize('L');
  6.   printer.println(classes[_class]);
  7.   if(_class == 0){
  8.     printer.boldOff();
  9.     printer.justify('L');
  10.     printer.setSize('M');
  11.     printer.println("Dental Casts:\n");
  12.     printer.setSize('S');
  13.     printer.println("Big Central");
  14.     printer.println("Antagonist");
  15.     printer.println("Orthodontic");
  16.     printer.println("Prognathous");
  17.     printer.println("Strange Inf.");
  18.     printer.println("Strange Sup.");
  19.   }
  20.   printer.feed(5);
  21.   printer.setDefault(); // Restore printer to defaults.
  22. }
复制代码
在run_inference_to_make_predictions函数中:
  • 总结对象检测(FOMO)模型inference设置(来自model_metadata.h),并在串口打印。
  • 用给定的设置拍摄一张图片。
  • 暂停视频,有错误的话打印错误。
  • 根据给定的物体检测(FOMO)模型调整当前捕获图像的大小。
  • 将调整后的图像数据格式转换为GRAYSCALE,以便用该模型运行inferences。
  • 用转换后的图像创建一个信号对象。
  • 然后运行分类器。
  • 串口打印inference timings。
  • 串口打印对象位置(中心点)的变量。
  • 获得由FOMO模型预测的标签(类别)。
  • 如果FOMO模型成功预测了一个标签(类别)。
  • 修改返回的对象位置(中心点)变量,以生成边界框测量。
  • 在触摸屏上显示预测的标签(类别)和生成的检测物体的边界框,以及颜色代码。
  • 热敏打印机打印预测的标签(类别)信息和细节。
  • 清除预测的标签(类别)。
  • 恢复视频播放,有错误的话打印错误。
  1. void run_inference_to_make_predictions(){
  2.   // Summarize the Edge Impulse FOMO model inference settings (from model_metadata.h):
  3.   ei_printf("\nInference settings:\n");
  4.   ei_printf("\tImage resolution: %dx%d\n", EI_CLASSIFIER_INPUT_WIDTH, EI_CLASSIFIER_INPUT_HEIGHT);
  5.   ei_printf("\tFrame size: %d\n", EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
  6.   ei_printf("\tNo. of classes: %d\n", sizeof(ei_classifier_inferencing_categories) / sizeof(ei_classifier_inferencing_categories[0]));
  7.   
  8.   // Take a picture with the given still picture settings.
  9.   CamImage img = theCamera.takePicture();
  10.   
  11.   if(img.isAvailable()){
  12.     // Pause video stream and print errors, if any.
  13.     adjustColor(1,1,0);
  14.     Serial.println("\nPausing streaming...\n");
  15.     err = theCamera.startStreaming(false, CamCB);
  16.     if(err != CAM_ERR_SUCCESS) printError(err);
  17.     // Resize the currently captured image depending on the given FOMO model.
  18.     CamImage res_img;
  19.     img.resizeImageByHW(res_img, EI_CLASSIFIER_INPUT_WIDTH, EI_CLASSIFIER_INPUT_HEIGHT);
  20.     Serial.printf("Captured Image Resolution: %d / %d\nResized Image Resolution: %d / %d", img.getWidth(), img.getHeight(), res_img.getWidth(), res_img.getHeight());
  21.     // Convert the resized (sample) image data format to GRAYSCALE so as to run inferences with the model.
  22.     res_img.convertPixFormat(CAM_IMAGE_PIX_FMT_GRAY);
  23.     Serial.print("\nResized Image Format: ");
  24.     Serial.println((res_img.getPixFormat() == CAM_IMAGE_PIX_FMT_GRAY) ? "GRAYSCALE" : "ERROR");
  25.     // Run inference:
  26.     ei::signal_t signal;
  27.     ei_camera_capture_out = res_img.getImgBuff();
  28.     // Create a signal object from the resized and converted sample image.
  29.     signal.total_length = EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT;
  30.     signal.get_data = &ei_camera_cutout_get_data;
  31.     // Run the classifier:
  32.     ei_impulse_result_t result = { 0 };
  33.     EI_IMPULSE_ERROR _err = run_classifier(&signal, &result, false);
  34.     if(_err != EI_IMPULSE_OK){
  35.       ei_printf("ERR: Failed to run classifier (%d)\n", err);
  36.       return;
  37.     }
  38.     // Print the inference timings on the serial monitor.
  39.     ei_printf("\nPredictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
  40.         result.timing.dsp, result.timing.classification, result.timing.anomaly);
  41.     // Obtain the object detection results and bounding boxes for the detected labels (classes).
  42.     bool bb_found = result.bounding_boxes[0].value > 0;
  43.     for(size_t ix = 0; ix < EI_CLASSIFIER_OBJECT_DETECTION_COUNT; ix++){
  44.       auto bb = result.bounding_boxes[ix];
  45.       if(bb.value == 0) continue;
  46.       // Print the detected bounding box measurements on the serial monitor.
  47.       ei_printf("    %s (", bb.label);
  48.       ei_printf_float(bb.value);
  49.       ei_printf(") [ x: %u, y: %u, width: %u, height: %u ]\n", bb.x, bb.y, bb.width, bb.height);
  50.       b_b_x = bb.x; b_b_y =  bb.y; b_b_w = bb.width; b_b_h = bb.height;
  51.       // Get the predicted label (class).
  52.       if(bb.label == "cast") predicted_class = 0;
  53.       if(bb.label == "failed") predicted_class = 1;
  54.       if(bb.label == "implant") predicted_class = 2;
  55.       Serial.print("\nPredicted Class: "); Serial.println(predicted_class);
  56.     }
  57.     if(!bb_found) ei_printf("    No objects found!\n");
  58.     // Detect anomalies, if any:
  59.     #if EI_CLASSIFIER_HAS_ANOMALY == 1
  60.       ei_printf("Anomaly: ");
  61.       ei_printf_float(result.anomaly);
  62.       ei_printf("\n");
  63.     #endif   
  64.     // If the Edge Impulse FOMO model predicted a label (class) successfully:
  65.     if(predicted_class != -1){
  66.       // Scale the detected bounding box.
  67.       int box_scale_x = tft.width() / EI_CLASSIFIER_INPUT_WIDTH;
  68.       b_b_x = b_b_x * box_scale_x;
  69.       b_b_w = b_b_w * box_scale_x * 16;
  70.       if((b_b_w + b_b_x) > (tft.width() - 10)) b_b_w = tft.width() - b_b_x - 10;
  71.       int box_scale_y = tft.height() / EI_CLASSIFIER_INPUT_HEIGHT;
  72.       b_b_y = b_b_y * box_scale_y;
  73.       b_b_h = b_b_h * box_scale_y * 16;
  74.       if((b_b_h + b_b_y) > (tft.height() - 10)) b_b_h = tft.height() - b_b_y - 10;
  75.       
  76.       // Display the predicted label (class) and the detected bounding box on the ILI9341 TFT screen.
  77.       for(int i=0; i<5; i++){
  78.         tft.drawRect(b_b_x+i, b_b_y+i, b_b_w-(2*i), b_b_h-(2*i), color_codes[predicted_class]);
  79.       }
  80.       int c_x = 10, c_y = 10, r_x = 120, r_y = 40, r = 3, offset = 6;
  81.       tft.drawRGBBitmap(10, c_y+r_y+10, (uint16_t*)(dental.pixel_data), (int16_t)dental.width, (int16_t)dental.height);
  82.       tft.fillRoundRect(c_x, c_y, r_x, r_y, r, ILI9341_WHITE);
  83.       tft.fillRoundRect(c_x+offset, c_y+offset, r_x-(2*offset), r_y-(2*offset), r, color_codes[predicted_class]);
  84.       tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2);
  85.       tft.setCursor(c_x+(2*offset), c_y+(2*offset));
  86.       tft.printf(classes[predicted_class]);
  87.       // Print the predicted label (class) information via the thermal printer.
  88.       print_thermal(predicted_class);
  89.       // Clear the predicted class (label).
  90.       predicted_class = -1;
  91.     }
  92.      
  93.     sleep(10);
  94.    
  95.     // Resume video stream and print errors, if any.
  96.     adjustColor(0,1,0);
  97.     sleep(2);
  98.     Serial.println("\nResuming streaming...\n");
  99.     err = theCamera.startStreaming(true, CamCB);
  100.     if(err != CAM_ERR_SUCCESS) printError(err);
  101.   }else{
  102.     Serial.println("Failed to take a picture!");
  103.     adjustColor(1,0,0);
  104.     sleep(2);
  105.   }
  106. }
复制代码
  • 如果已按下控制按钮(B),用物体检测(FOMO)模型运行inference来预测牙模精度类别。
  1. if(!digitalRead(button_B)) run_inference_to_make_predictions();
复制代码
AI驱动的牙模分类器—基于Edge Impulse平台图60


7. 在Sony Spresense上运行FOMO模型对牙模进行分类

检测到的物体对应于三个不同的牙模准确性类别[0-2],如步骤5所示。
  • 0 - Cast
  • 1 - Failed
  • 2 - Implant
在Sony Spresense上执行了dental_model_classifier_run_model.ino文件后:
  • 如果相机板与Spresense主板连接成功,设备的LED灯就会变成蓝色。
  • 然后,LED变成默认的红色,并且触摸屏上显示视频。
  • 如果按下控制按钮(B),设备会暂停播放视频并捕捉图片。如果设备成功捕捉到图片,LED变成黄色,改变当前图像的尺寸,将数据格式转换为GRAYSCALE。
  • 然后设备通过Edge Impulse物体检测(FOMO)模型运行inference。
  • 最后,显示屏上显示检测结果和生成的边界框。
  • 每个牙模精度类别都有唯一的颜色代码,在被FOMO模型预测时显示在屏幕上。
       Cast➡绿色
       Failed➡品红色
       Implant➡ 橙色
  • 同时,设备通过热敏打印机打印出预测的标签信息和细节。
  • 打印信息成功后,设备恢复视频播放,LED变为绿色。
在完成上述步骤后,我已经采用该设备预测和检测各种3D打印牙模的精度类别,看是否有潜在的错误和不准确。

总结
通过用牙模训练物体检测模型,并应用于牙模精度检测,我们可以:
  • 获得更稳定精确的3D打印牙模。
  • 排除3D打印牙模的潜在错误和不准确之处。
  • 更快、更直接地预测和检测牙齿问题。
  • 帮助在3D打印方面是新手的牙科技师。

相关STL文件及原理图点击此处下载:下载附件自定义部件(STL文件及原理图).zip
相关代码点击此处下载:下载附件代码.zip


原文:AI-driven Dental Cast (Model) Classifier w/ Edge Impulse
项目作者:Kutluhan Aktar 2022/7/14










云天  初级技神

发表于 2022-11-17 10:19:28

我这里连接Edge Impulse网站,总出问题,有时能访问,有时不能访问。这段时间就访问不了。能访问时,上传数据,有时能传,有时不能传。不稳定,用着不舒服。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail