MATLABで遊ぼう:簡単に時計を作れるか


前書き

前回はマインスイーパの作り方を紹介したが、今回は時計を簡単に作れるかを考えてみる。
頑張れば時計の模様(円や数字など)もかけることはできるが、ここでは、画像として導入したケースを紹介する。その理由は、作成者オリジナルの時計模様画像に一般化できるためである。

環境

MATLABのR2019aバージョン
Windows10
MATLAB Toolbox: Image Processing Toolbox

全体のフロー

プログラム作成のフロー:
1.オリジナルの時計画像を作成
2.時計の中心や針などを定義
3.時間と共に時計の針の角度計算や位置更新

オリジナルの時計画像を作成

図1のような簡単な画像を作成した。今回紹介する事例では、作成者オリジナルの時計画像に対応するようにしているが、条件として、円形の時計のみが対象となる。

図1:著作者オリジナルの時計画像

時計の中心や針などを定義

まずは、MATLABのimfindcircles関数を利用して上記オリジナル画像よりその中心を求める。
以下コードではMATLABのビルトイン関数IMFINDCIRCLESで画像から中心を求めているが(汎用性の高度化を目的で)、
基本的に、画像ごと、中心の位置をメタデータとして記録しておけば、そのまま取り組んでも大丈夫。
次は、その中心から円周方向に伸びる3本の針(時、分、秒)を定義する。
時と分の針を時計らしく太めにするため、二本の直線で表現する。
これらの二本の直線は円の中心側でお互い隔ており、円周側で一体になっている。
コードは以下になる(コメントもご参照):

% パラメータ
offst = 1;
rH  = 60;
rM  = 80;
rS  = 110;
radrange = [100 180];

% 自作のオリジナル画像を取り組む
I = imread('BaseWatch3.jpg');%図1の画像はこのファイルにある
I = flipud(I);
[c,r] = imfindcircles(I,radrange);%画像中心計算
%画像の取り組み
figure('ToolBar','none','menubar','none')
hI = imshow(I); %画像表示
set(gca,'YDir','normal') %画像表示におけるY軸の方向を通常の直交座標に合わせる
hold on
plot(c(1),c(2),'k.','Markersize',15) %中心に例えば黒い丸を描く
%針の定義・初期化
hlH1= line([c(1) c(1)],[c(2) c(2)], 'Color','Blue', 'Linewidth',2);%時の針⓵
hlH2= line([c(1) c(1)],[c(2) c(2)], 'Color','Blue', 'Linewidth',2);%時の針⓶
hlM1= line([c(1) c(1)],[c(2) c(2)], 'Color','Red', 'Linewidth',1);%分の針⓵
hlM2= line([c(1) c(1)],[c(2) c(2)], 'Color','Red', 'Linewidth',1);%分の針⓶
hlS = line([c(1) c(1)],[c(2) c(2)], 'Color','Black', 'Linewidth',1);%秒の針

時間と共に時計の針の角度計算や位置更新

MATLABで簡単に現在時刻を読み込んだり、その中から、時、分や秒の値を分離することができる。
この値に応じて、それぞれの針の位置を更新すればいい。
それぞれの針は、片方(円中心側)の位置が常に円の中心にあるので、求めるべきことは、それぞれの針の円周側の位置(X,Y座標)がどうなるかとのことである。

そのため下記ワークフローを考える:
(1)時、分や秒について、その現在値に基づいてそれぞれの針の角度を求める。
このため、以下の関数getAngles()をご参照。

(2)求めた角度と針の長さ情報を利用して、円柱座標から直交座標に変換によって、針の円周側の位置を求める(X,Y座標)。

(3)それぞれの針の新規位置に関する表示を更新する
最後に、Whileループの中に上記(1)~(3)を繰り返していけば、時計の出来上がりである。

※ちなみに、今回は40行以下で済んだとのこと。

while isvalid(hI)
    %時、分、秒の値を分離
    hms = datestr(now,'hh-MM-ss');
    hr  = str2double(hms(1:2));
    if hr > 12
        hr = hr - 12;
    end
    mn  = str2double(hms(4:5));
    ss  = str2double(hms(7:8));

    %時間の針の処理
    [aH,aM,aS]  = getAngles(hr,mn,ss); 
    [x, y] = pol2cart(aH,rH);
    adjustPosition(c,x,y,[hlH1,hlH2],offst+1);
    %分の針の処理
    [x, y] = pol2cart(aM,rM);
    adjustPosition(c,x,y,[hlM1,hlM2],offst);
    %秒の針の処理
    [x, y] = pol2cart(aS,rS);
    adjustPosition(c,x,y,hlS,offst);
end

getAngles()関数

function [aH,aM,aS]  = getAngles(hr,mn,ss)
aH = deg2rad(90 - hr*30 - mn*0.5 - ss*(1/120));
aM = deg2rad(90 - mn*6 - ss*(1/10));
aS = deg2rad(90 - ss*6);
end

adjustPosition()関数

function adjustPosition(c,x,y,hndls,offst)
p   = [c(1)+x c(2)+y];
if numel(hndls)>1
    set(hndls(1),'XData',[c(1)-offst p(1)],'YData',[c(2)-offst p(2)]);
    set(hndls(2),'XData',[c(1)+offst p(1)],'YData',[c(2)+offst p(2)]);
else
    set(hndls,'XData',[c(1) pS(1)],'YData',[c(2) pS(2)]);
end
drawnow
end

最後に出来上がるのが、こんな時計。シンプルではあるものの、MATLABでとても簡単に作れるたのがうれしい。