簡易ベクトルスコープ表示
Speresenseカメラで撮影したカラー画像の特徴を把握するために、UV平面の簡易ベクトルスコープを作成してみました。YCbCr(YUV)のYはR+2G+Bで簡略化しています。ヒストグラムのスケールも固定しています。RGB→YUVへの色変換は、Spresenseの内部処理機能(convertPixFormat関数)を使っても実装可能ですが、このサンプルコードでは直に書いています。画像処理では、RGBの零や飽和の取り扱いが課題になりますが、ベクトルスコープの表示では対象としない扱いにしています。
// Spresenseカメラ ベクトルスコープ表示 2024.6.16
#include <stdio.h>
#include <Camera.h>
#include <Adafruit_ILI9341.h>
#define TFT_DC 9
#define TFT_CS 10
Adafruit_ILI9341 display = Adafruit_ILI9341(TFT_CS, TFT_DC);
uint8_t R[160][120]; // [0-255]
uint8_t G[160][120]; // [0-255]
uint8_t B[160][120]; // [0-255]
uint8_t Y[160][120]; // [0-255]
int8_t U[160][120]; // [-128-0-127]
int8_t V[160][120]; // [-128-0-127]
uint8_t buf_R[160][120]; // [0-255]
uint8_t buf_G[160][120]; // [0-255]
uint8_t buf_B[160][120]; // [0-255]
uint16_t* image_buffer;
uint16_t drawImageBuf[160*120];
void displayImage() {
for (int n = 0; n < 160*120; n++) {
uint8_t r5 = buf_R[n%160][n/160];
uint8_t g6 = buf_G[n%160][n/160];
uint8_t b5 = buf_B[n%160][n/160];
drawImageBuf[n] = (uint16_t)(((r5 & 0x00f8)<<8) | ((g6 & 0x00fc)<<3) | (b5 & 0x00f8)>>3);
}
display.drawRGBBitmap(160, 0, drawImageBuf, 160, 120);
}
void CamCB(CamImage img)
{
if (img.isAvailable()) {
image_buffer = (uint16_t *)img.getImgBuff();
}
}
void setup() {
CamErr err;
display.begin();
display.setRotation(3);
display.fillScreen(ILI9341_BLACK);
err = theCamera.begin(1, CAM_VIDEO_FPS_5, CAM_IMGSIZE_QQVGA_H, CAM_IMGSIZE_QQVGA_V, CAM_IMAGE_PIX_FMT_RGB565);
err = theCamera.setAutoWhiteBalanceMode(CAM_WHITE_BALANCE_AUTO);
err = theCamera.startStreaming(true, CamCB);
}
void loop() {
int x, y, n, tY, h, val;
delay(200);
// 入力画像を描画
display.drawRGBBitmap(0, 0, image_buffer, CAM_IMGSIZE_QQVGA_H, CAM_IMGSIZE_QQVGA_V);
// 入力画像をRGB各チャンネル毎に8btにスケーリングして格納
uint16_t* imgbuf_RGB565 = (uint16_t*)image_buffer;
for (n = 0; n < 160*120; n++) {
x = n%160;
y = n/160;
R[x][y] = (imgbuf_RGB565[n] >> 8) & 0x00f8; // [0-255]
G[x][y] = (imgbuf_RGB565[n] >> 3) & 0x00fc; // [0-255]
B[x][y] = (imgbuf_RGB565[n] << 3) & 0x00f8; // [0-255]
}
// RGBからYCbCr(YUV)に変換し、各チャンネル毎に8btにスケーリングして格納。YはR+2G+Bで近似
for (x = 0; x < 160; x++) {
for(y = 0; y < 120 ; y++) {
tY = ((int)R[x][y]+(int)G[x][y]*2+(int)B[x][y]) / 4;
Y[x][y] = tY; // [0-255]
val = (int)B[x][y] - tY;
if(val > 127) {
U[x][y] = 127;
} else if(val < -128) {
U[x][y] = -128;
} else
U[x][y] = val;
val = (int)R[x][y] - tY;
if(val > 127) {
V[x][y] = 127;
} else if(val < -128) {
V[x][y] = -128;
} else
V[x][y] = val;
}
}
// ベクトルスコープ表示 CbCrカラー平面を描画
// 枠線描画
display.fillRect(160, 120, 160, 120, ILI9341_BLACK);
display.drawRect(180, 120, 120, 120, ILI9341_WHITE);
display.drawLine(180, 180, 299, 180, ILI9341_DARKGREY);
display.drawLine(240, 120, 240, 239, ILI9341_DARKGREY);
// CbCr(UV)値に基づき、ピクセル色でドットを描画
for(y = 0; y < 120; y++) {
for(x = 0; x < 160; x++) {
if(R[x][y]==0 || G[x][y]==0 || B[x][y]==0|| R[x][y]>=240 || G[x][y]>=248 || B[x][y]>=240) break; // RGBの各値が上限下限で飽和していたら描画をスキップ
display.drawPixel(240+(int)U[x][y]/2, 180-(int)V[x][y]/2, display.color565(R[x][y], G[x][y], B[x][y]));
}
}
// ヒストグラム表示
int hR[256], hG[256], hB[256]; // ヒストグラム配列表
// ヒストグラム表の初期化
for(n = 0; n < 256; n++) {
hR[n] = 0;
hG[n] = 0;
hB[n] = 0;
}
// 頻度のカウント
for(y = 0; y < 120; y++) {
for(x = 0; x < 160; x++) {
hR[R[x][y]]++;
hG[G[x][y]]++;
hB[B[x][y]]++;
}
}
// ヒストグラム軸描画
display.fillRect(0, 120, 159, 239, ILI9341_BLACK);
display.drawLine(20, 119, 20, 159, ILI9341_RED);
display.drawLine(20, 159, 148, 159, ILI9341_RED);
display.drawLine(20, 160, 20, 199, ILI9341_GREEN);
display.drawLine(20, 199, 148, 199, ILI9341_GREEN);
display.drawLine(20, 200, 20, 239, ILI9341_BLUE);
display.drawLine(20, 239, 148, 239, ILI9341_BLUE);
#define BarGain 50
// 頻度バー表示(R, G); RとBの分解能は5bitの32段階なので、4ピクセルおきに4ピクセル幅のおバー表示する。
for(x = 0; x < 256; x+=8) {
h = hR[x]/BarGain;
if(h >= 39) h = 39;
display.fillRect(x/2+20, 159-h, 4, h, ILI9341_RED);
h = hB[x]/BarGain;
if(h >= 39) h = 39;
display.fillRect(x/2+20, 239-h, 4, h, ILI9341_BLUE);
}
// 頻度バー表示(G); Gの分解能は6bitの64段階なので、2ピクセルおきに2ピクセル幅のおバー表示する。
for(x = 0; x < 256; x+=4) {
h = hG[x]/BarGain;
if(h >= 39) h = 39;
display.fillRect(x/2+20, 199-h, 2, h, ILI9341_GREEN);
}
}
投稿者の人気記事
-
eMoi-pic
さんが
2024/06/15
に
編集
をしました。
(メッセージ: 初版)
-
eMoi-pic
さんが
2024/06/16
に
編集
をしました。
-
eMoi-pic
さんが
2024/06/16
に
編集
をしました。
ログインしてコメントを投稿する