画像処理・基本変換
2021.05.16

YouTube でも紹介しています。画像をクリックすると再生できます。
Interface 2020年11月号特集「ESP32で画像処理プログラム100」では画像処理の仕組みをソースコードとともに解説しています。
画像処理ライブラリを使用しないで、pixel単位での変換処理を行っているので、画像処理の本質を理解するのには最適です。
pythonでは扱えないポインタを使ったプログラミングになるので、C/C++言語の知識が必要になりまが、
短時間に幅広く学習されたい方にはよい教材だと思います。
画像処理用ソースコードは少しの変更で別環境への転用・応用も可能です。
Interface 2020年11月号
特集「ESP32で画像処理プログラム100」
第1章 画像処理プログラムを試す準備
第2章 明るさ&色
第3章 形&大きさ変換
第4章 フィルタ
第5章 拡大/回転/移動/膨張
第6章 画像分析
第7章 特殊加工
まずは、この特集で紹介しているプログラムの実行環境を整えます。
特集では、M5StackとESP32-DevKitCでの実装方法を紹介しています。
ここでは、ESP32-DevKitC (ESP32 General Development Kit)を使用します。
■実行環境

ノートパソコンから、TeraTermによりラズベリーパイにSSH接続して操作します。
■Sparkfun ESP32 Thing Plus

Espressif Systems社のESP32-WROOM-32Dモジュールを搭載し、強力なWi-FiとBluetooth MCUを備えた開発プラットフォームThing Plusの新しいバージョンです。Featherボード互換で、Qwiicシステムの利用によりハンダ付けやシールドなしで接続が可能です。
静電容量式タッチセンサ、ホール(磁気)センサ、SDカードインターフェース、イーサネット、高速SPI、UART、I2S、 I2Cなど、豊富な周辺機器を統合し、16 MBのフラッシュメモリ、520 KBの内蔵SRAM、802.11 b/g/n Wi-Fi、デュアルモードBluetooth機能、およびLiPoバッテリー用JST型コネクタを特徴としています。
プログラムのビルドには、Platformioを使用します。
まずは、ラズベリーパイに繋いで、接続デバイスを確認します
$ pio device list
/dev/ttyUSB0
------------
Hardware ID: USB VID:PID=21F3:EC73 SER=86B13DA3 LOCATION=1-1.3
Description: CP2104 USB to UART Bridge Controller
/dev/ttyAMA0
------------
Hardware ID: 4e836500.serial
Description: ttyAMA0
利用できるボードを確認します
$ pio boards "sparkfun"
.....
Platform: espressif32
==========================================================================
ID MCU Frequency Flash RAM Name
--------------- ----- --------- ----- ----- -------------------------
esp32thing ESP32 240MHz 4MB 320KB SparkFun ESP32 Thing
esp32thing_plus ESP32 240MHz 16MB 320KB SparkFun ESP32 Thing Plus
.....
プロジェクトを作成します
$ mkdir ~/Interface521
$ cd ~/Interface521
$ pio init -b esp32thing_plus
■Adafruit 2.2" 18-bit color TFT LCD display with microSD card breakout
ILI9340搭載の解像度240x320、TFT液晶モジュールを使用しました。
・2.2" diagonal true TFT LCD display has 320x240 colour pixels
・The TFT driver (ILI9340) can display full 18-bit color (262,144 shades!)
・ultra-low-dropout 3.3V regulator and a 3/5V level shifter so you can use it with 3.3V or 5V power and logic.
・comes with a microSD card holder so you can easily load full color bitmaps from a FAT16/FAT32 formatted microSD card (card not included)
| SparkFun ESP32 | - | Adafruit TFT display |
| GND | - | GND |
| 3V3 | - | VIN |
| GPIO[26] | - | DC:SPI data / command selection |
| 3V3 | - | RESET |
| GPIO[16] | - | SD CS:Chipselect |
| GPIO[17] | - | LCD CS:Chipselect |
| MOSI[18] | - | SDI(MOSI):SPI data line |
| MISO[19] | - | SDO(MISO):SPI data line |
| SCK[5] | - | SCK:SPI clock line |
描画テスト
■adafruit/Adafruit_ILI9341 - graphictest.ino
Ref.graphictest.ino
$ vi plaformio.ini
[env:esp32thing_plus]
platform = espressif32
board = esp32thing_plus
framework = arduino
lib_deps =
adafruit/Adafruit ILI9341
adafruit/Adafruit ST7735 and ST7789 Library
adafruit/Adafruit GFX Library
adafruit/Adafruit BusIO
$ vi src/graphictest.ino
#include "SPI.h"
#include "Wire.h" ← 追加
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
//#define TFT_DC 9
//#define TFT_CS 10
#define TFT_DC 26
#define TFT_CS 17
$ pio run -t upload

画像基本変換
CQ出版社 Interface のサイトからソースプログラムをダウンロードします。
https://www.cqpub.co.jp/interface/download/2020/11/IF2011T.zip
IF2011T.zip を解凍→[IF2011T]→[ESP32_LCD]→[ImageProcessing]
この中のファイルすべてをプロジェクトのsrcディレクトリ(~/Interface521/src)に配置します。
DCとCSのGPIO番号を配線に合わせて変更します。
$ vi src/LcdWrapper.cpp
//#define TFT_DC 5
//#define TFT_CS 16
#define TFT_DC 26
#define TFT_CS 17
ino ファイルにヘッダーファイルを追記します。
$ vi ImageProcessing.ino
#include "LcdWrapper.h"
#include "ModeManage.h"
#include <FS.h>
#include <SPIFFS.h>
#include <Wire.h> ← 追加
■画像ファイルのアップロード
PlatformIOでESP32のSPIFFS(SPI Flash File System)にサンプル画像ファイルを書き込みます。
1.プロジェクト直下に data という名前で新しいフォルダーを作成します
$ cd ~/Interface521
$ mkdir data
2.ダウンロードしたソースコードを解凍すると、オリジナル画像というディレクトリ内に、サンプル画像が格納されています。
これらのファイルをdataディレクトリー内にコピーします。
3.ファイルアップロード用オプションを付加して、コマンドを実行します
$ pio run --target uploadfs
削除する場合は
$ pio run --target erase
自分の好みで画像を変更する場合は、画像ファイル名を、src/Original.h 記載のファイル名に変更するか、
SPIフラッシュ・メモリーに書き込む画像ファイル名に合わせて、ヘッダーファイルを修正します。
$ vi src/Original.h
class Original {
public:
const char* fname = "/mandrill.jpg";
const char* noise_fname = "/mandrill_noise.jpg";
const char* small_fname = "/mandrill_small.jpg";
const char* lena_fname = "/lena.jpg";
// const char* interface_fname = "/interface_noise_white.jpg";
const char* interface_fname = "/interface_noise_black.jpg";
オリジナル画像ディレクトリーにあるマンドリルというサルの画像では向学心に火が付かないと思いますので、サンプル画像に三乳亭しん太さんのカバーイラストを使わせていただきました。
本当はやってはいけない刑罰マニュアル
ギロチン、絞首刑、電気椅子、ガス殺、断手。あまたの刑罰を既に絶滅したもの、未だに行われているもの、現実には存在しなかったものに分類し、刑罰が発明・採用された当時の法典、社会情勢、宗教的な意義、登場する作品を解説した刑罰本がついに登場。この本を読んでも、絶対に真似をしないでください……。
三乳亭 しん太 (イラスト)
$ pio run -t upload

10秒ごとにオリジナル画像の画像変換処理が切り替わって表示されます。
その中のいくつかの変換処理を紹介します。
Chchange
チャネル入れ替え
画素のRGBをBGRに入れ替えています。
ReverseColor
色の反転
画素のRGB値それぞれを、最大値(255)から引いて反転させています。
R = 255 - R
G = 255 - G
B = 255 - B
GrayScale
グレー・スケール化
CIE(国際照明委員会) XYZ を用いた、輝度変換です。
RGBとは別の色空間XYZを用います。この空間では、XとZが色味、Yが輝度になります。
Y = 0.2126 * R + 0.7152 * G + 0.0722 * B
ThreshBinary
2値化
しきい値(Thresh)を決めて、画素の輝度がしきい値より大きければ白、小さければ黒に変換します。
AvePooling
平均プーリング化
グリッド・サイズを、8x8 にして、各領域内の平均値で領域を埋めています。
■応用
今回の説明の中で用いている変換処理画像は、Interface掲載プログラムを書き換え、変換処理画像をTGA画像ファイルとしてSDカードに書き出したものを、JPEG画像に変換して使用しています。
JPEG - MCU(Minimum Coded Unit)
JPEGは、画像をデータにする時に一片を8pixelの倍数の小さい画像に分割し、
個々の画像をデータに変換します。この小さい画像をMCU(Minimum Coded Unit)と呼びます。
Ref.JPEGファイルの理解を深める
特集記事では、1 MCUごとに画像情報を読み込み、その都度、画像変換をおこなっているものが多く、SRAMの少ないESP32でも処理が可能です。
例えば、掲載プログラムのOriginal.cpp に下記の太字部分を追記して、ビルドします。
$ vi src/Original.cpp
void Original::display(const char *fname){
uint8_t *pImg;
int x,y;
Lcd.clearDisplay(BLACK);
JpegDec.decode(fname);
// Image Information
Serial.print("JpegDec.width :");
Serial.println(JpegDec.width);
Serial.print("JpegDec.height :");
Serial.println(JpegDec.height);
Serial.print("JpegDec.MCUWidth :");
Serial.println(JpegDec.MCUWidth);
Serial.print("JpegDec.MCUHeight :");
Serial.println(JpegDec.MCUHeight);
Serial.print("JpegDec.MCUSPerRow :");
Serial.println(JpegDec.MCUSPerRow);
Serial.print("JpegDec.MCUSPerCol :");
Serial.println(JpegDec.MCUSPerCol);
Serial.println("");
......
platformio のデバイスモニターを起動、ESP32の[RESET]ボタンを押して、ビルドしたプログラムを再起動すると、JPEG画像の情報が表示されます。
$ pio device monitor -p /dev/ttyUSB0 -b 115200
JpegDec.width :320
JpegDec.height :240
JpegDec.MCUWidth :16
JpegDec.MCUHeight :16
JpegDec.MCUSPerRow :20
JpegDec.MCUSPerCol :15
width と height が画像サイズです。
MCUWidth と MCUHeight がMCUのブロックサイズです。
MCUSPerRow が行に含まれるMCUのブロック数、MCUSPerCol が列に含まれるMCUのブロック数になります。
MCUのブロックサイズごとに画像処理を行い、ディスプレイに表示するのは容易なことですが、
実用面で考えると、SDカードに保存したデータを読み取り、画像処理をして、SDカードに処理後の画像を書き出せれば利便性が向上します。
実用に際しては2つの点が課題となります。
1.画像情報をRGB情報を保持したまま、すべて読み込んでから処理しようとするとESP32のSRAMが足りない。
2.SDカード使用の際には、複数個のファイルを同時に開けない。
対策としては、
1.画像横サイズ分のMCUブロックに対して画像処理を行い、TGA形式の画像配列(BGRBGRBGR....)に並び替えて、メモリ上に格納します。
320x240のJPEG画像を例に取ると、
1MCU は 16x16 pixel (MCUWidth,MCUHeight)
横方向に、20ブロック(MCUSPerRow)
縦方向に15ブロック(MCUSPerCol)
となっています。
横方向1列分のブロックを格納するのに必要なメモリーは
16 x 16 x 20 x 3 → 15360バイト
SRAM 520KB の Sparkfun ESP32 Thing Plus では確保できました。
2.横1列分の処理が終わるたびに、SPI FLASHメモリー上のファイルに書き出します。
3.すべての処理が完了した時点で、SPI FLASH 上のファイルを、SDカードに書き出します。
ただし、一度に画像の全領域を処理対象にする変換に関しては、SRAMが不足して、画像ファイルを生成できない可能性があります。
ESP32-WROVER でしたら、PSRAMを使うことで回避できるかもしれません。
■参考文献
・ESP32でSPIFFSにファイルを読み書きする方法
・PlatformIOでESP32のSPIFFSにファイルを書き込むコマンド
・adafruit/SD - GitHub
・Hiletgoの2.8インチ TFT液晶でBMP画像を表示してみた
・Python/OpenCVで任意色を透過させたpng画像に変換
・Adafruit_ILI9340/examples/spitftbitmap/spitftbitmap.ino
|