はじめに
この記事は、
Raspberry Pi 4 で Psychtoolboxを動かす(1):準備編
Raspberry Pi 4 で Psychtoolboxを動かす(2):動作確認編
の続きです。よろしければそちらを先に確認していただけると幸いです。
RaspiOS + Octave + PTBの環境で日本語文字を表示させることに苦労したので、誰かの役に立てればと思い、作業の記録を公開します。
いきなり解決策
下記のプログラムを実行すると、グレー(RGBそれぞれ128)背景に黒文字で 「日本語でこんにちは!
」 と画面中央に3秒間表示されるはずです。
プログラム
clear;
clc;
% 文字列の設定
textStr = '日本語でこんにちは!';
fontSize = 60;
fontName = 'Noto Sans CJK Regular';
% 文字色と背景色
textColor = [0, 0, 0]; % 黒
bgColor = [128, 128, 128]; % 灰色
% 文字数に基づいて画像サイズを決定
numChars = length(textStr);
charWidthFactor = 0.4;
imgWidth = round(fontSize * numChars * charWidthFactor);
imgHeight = round(fontSize * 1.5);
% 背景画像を作成
img = uint8(repmat(reshape(bgColor, 1, 1, 3), imgHeight, imgWidth));
% Figureの作成とサイズ指定
finalFig = figure('Visible', 'off', 'Position', [100, 100, imgWidth, imgHeight]);
finalAx = axes('Position', [0 0 1 1]);
% 画像の描画
imshow(img);
hold on;
xlim([0, imgWidth]);
ylim([0, imgHeight]);
set(gca, 'YDir', 'reverse');
axis off;
axis tight;
% 文字の描画と画像化
text(imgWidth / 2, imgHeight / 2, textStr, ...
'Color', textColor / 255, ...
'FontSize', fontSize, ...
'FontName', fontName, ...
'HorizontalAlignment', 'center', ...
'VerticalAlignment', 'middle');
finalFrame = getframe(finalAx);
close(finalFig);
% PTBの画面に表示
Screen('Preference', 'SkipSyncTests', 1);
scrnNum = max(Screen('Screens'));
mainWindow = Screen('OpenWindow', scrnNum, [128 128 128]);
tex = Screen('MakeTexture', mainWindow, finalFrame.cdata);
Screen('DrawTexture', mainWindow, tex);
Screen('Flip', mainWindow);
WaitSecs(3);
Screen('CloseAll');
簡単な解説
これまでの経験からPTBが日本語文字をうまく扱えないことが原因だろうということで、PTB内の関数に頼らず、Octaveが用意している関数で文字列を画像化する手続きを取る案で行くことにしました。MATLABでは使えるけども、Octaveでは使えない関数がいくつかあり、「Octaveの関数だけでやる」という点に苦労しました(ChatGPTにもたくさん相談しました)。
まず 「% 文字列の設定
」、「% 文字色と背景色
」 あたりで必要な設定をします。
次に 「% 文字数に基づいて画像サイズを決定
」 の部分で文字列を画像化するにあたり、文字列の長さやフォントの大きさの情報に基づいて、画像の縦横サイズを算出しています。 算出された値に基づいて、「% 背景画像を作成
」 の位置でまずは文字列を載せる背景部分を作成しています。
続いて、「% Figureの作成とサイズ指定
」、「% 画像の描画
」 の位置で文字列を画像化するための準備です。この辺りはあまり理解していません(笑)
「% 文字の描画と画像化
」 で指定した文字列、文字色、フォント、フォントサイズ、配置位置を決めて、画像化したものを作成します。最終的に画像化された文字列は変数finalFrame
に格納されています。
あとは 「% PTBの画面に表示
」 で画像をScreenに表示する方法と同様の手続きで画面に描画します。
おわりに
RaspiOS + Octave + PTBで日本語文字を表示させるたびに上記のコードを書いていくのはあまりにも冗長すぎます。適当な名前で関数化して、引数として文字列を与えるだけで画面に描画する内容を作ってくれるようにするのが良いと思います。
ちなみにこのコードは(Windows +)MATLAB+PTBの環境ではうまく動きません。その原因は、文字列の長さに応じた画像サイズの算出の仕方がMATLABとOctaveでは違うからのようです。MATLABの方がもう少し簡単に画像サイズの算出ができます。
文字列を画像として保存するというのは意外とニーズがあることなのかなと思います。上記の「% PTBの画面に表示
」 から下の部分を下記のコードに書き換えると、textStrで指定した日本語文字列をtext_image.jpgという名前で保存してくれるようになります。こちらもお試しください。
% 画像の保存
outputFileName = 'text_image.jpg';
imwrite(imgWithText, outputFileName, 'Quality', 100);