見出し画像

MATLAB でカーソル込みのキャプチャー GIF を作る ~ カーソルも自動移動

まえがき

MATLAB R2023b (偏った)アップデートメモ円グラフのところに上げた GIF ですが、カーソル込みになっています。

キャプチャー画像

また、カーソル自体もスクリプト中で移動させています。

カーソルはシステム表示なので、通常、カーソル込みでキャプチャーするには、MATLAB 外のキャプチャーソフトを使うしかありません。(R2020a 以降であれば、マウスポインター形状は figurePointer プロパティで変更可能です。カスタム形状も可)

Pointer プロパティ


しかし、MATLAB 以外を使うのはイヤですよね?(¬_¬)

そこで、ムリヤリ MATLAB 上で完結できるよう作ってみましたのでご紹介します。


準備

必要なことは、以下の2点のみです。
・スクリプトでマウスカーソルを移動
・キャプチャー画面にマウスアイコンをオーバーレイ

以下、詳しく見ていきましょう。

・スクリプトでマウスカーソルを移動

Java の標準クラスである java.awt.Robot を使用します。

GUI の操作を自動化できます。

import java.awt.Robot;
robot = Robot;
robot.mouseMove (X, Y);

使い方は簡単、これだけです!

ちなみに MATLAB のコンソール自体も、(多分)Java 上で動いています。

・キャプチャー画面にマウスアイコンをオーバーレイ

どこかから画像を取ってきても良いですし、Windows であれば C:\Windows\Cursors\ にカーソルファイルがあります。

MATLAB の imread で読み込めるのと読み込めないのがあるようでその辺はよく分かりませんが、今回は arrow_i.cur を使いました。

ただ読み込み結果が単なるロジカルでαチャネルもないようなので、リサイズ後、縁取りも表示させるために領域を拡大してそれをαチャネルとして用います。

cImage = imread("C:\Windows\Cursors\arrow_i.cur");
curSize = [40 40];
cImage = imresize(cImage,curSize);
se = [0 1 0; 1 1 1; 0 1 0];
cAlpha = conv2(cImage,se,'same');  % make border
cAlpha = cAlpha > 0;
cAlpha = repmat(cAlpha,1,1,3);  % alpha map with border
cImage = repmat(cImage*255,1,1,3);  % cursor image


カーソル位置は、簡単のためまずは極座標で円グラフの各パート上を指定し、直交座標に変換します。

移動量は、piechart オブジェクト の Proportions が各領域のパーセンテージなので、極座標のθにそれを反映すれば簡単です。

figurepiechart オブジェクトの位置情報はどちらも Position プロパティで参照できます。

画像を行列として指定する場合は、「行、列」の順番ですので「y、x」の順になることに注意してください。

PC画面の縦サイズが必要になりますが

r = groot;
screenH = r.ScreenSize(4);

で取れます。

座標は、左下が (1,1) です。


マウスカーソル座標をチェックしたい場合は、

f=figure;
set (f, 'WindowButtonMotionFcn', "C = get (gcf, 'CurrentPoint')");

とすると、カーソルを移動させるとその座標が表示されます。

set (f, 'WindowButtonMotionFcn', '');

で戻しておきましょう。


スクリプト例

clear
close all

str = "You say yes, I say no, You say stop and I say go go go.";

% split words
pat = " " | "," | "." | "and";
words = split(str,pat);
words = lower(words);
words(words == "") = []


[w,~,idx] = unique(words);
hNum = histcounts(idx,numel(w));
[hNumSorted,idx2] = sort(hNum,'descend');
wSorted = w(idx2)


f = figure('Visible','on');
w2 = categorical(words);
rank2 = reordercats(w2,wSorted)
p = piechart(rank2);


% 以降、キャプチャースクリプト
we = true;  % trueでgifファイル保存、falseで表示のみ
st = true;  % 1st frame
filename = 'piechart.gif';  % 出力 GIF ファイル名

r = groot;
screenH = r.ScreenSize(4);  % get screen size

import java.awt.Robot;  % set java robot
robot = Robot;

fPos = f.Position;
p.Units = "pixels";
% center of the circle
cX = p.Position(3)/2 + p.Position(1);
cY = p.Position(4)/2 + p.Position(2);

cImage = imread("C:\Windows\Cursors\arrow_i.cur");
curSize = [40 40];
cImage = imresize(cImage,curSize);
se = [0 1 0; 1 1 1; 0 1 0];
cAlpha = conv2(cImage,se,'same');  % make border
cAlpha = cAlpha > 0;
cAlpha = repmat(cAlpha,1,1,3);  % alpha map with border
cImage = repmat(cImage*255,1,1,3);  % cursor image

% 極座標
cR = cX/2;
th = - pi/2;  % start angle of the pie chart
for k = 1:length(p.Proportions)
    % calculate cursor position
    th = th + 2 * pi * p.Proportions(k);  % move to next border
    cTh = th - 2 * pi * p.Proportions(k)/2;  % back to center of the each partiton

    [x,y] = pol2cart(cTh, cR)  % 直交座標に変換
    % relative screen position
    x = cX + x;
    y = cY - y;

    % absolute world position 
    cPosX = fPos (1) + x;
    cPosY = screenH - (fPos (2) + y);
    robot.mouseMove (cPosX, cPosY);  % move cursor

    drawnow

    if we
        % graphic cursor position
        curGX = round(x);
        curGY = round(f.Position(4) - y);

        % capture frame with cursor
        [bA,map] = getImageWithCursor(f,curGX,curGY,cImage,cAlpha);
        
        if (st)  % 1st frame
            imwrite(bA,map,filename,'gif','LoopCount',Inf,'DelayTime',0.5);
            st = false;
        else
            imwrite(bA,map,filename,'gif','WriteMode','append','DelayTime',0.5);
        end
        pause(0.5)  % wait for data tips
        [bA,map] = getImageWithCursor(f,curGX,curGY,cImage,cAlpha);
        imwrite(bA,map,filename,'gif','WriteMode','append','DelayTime',1);
        pause(1)  % unnecesarry
    end

end
if we  %  additional frames at end
    imwrite(bA,map,filename,'gif','WriteMode','append','DelayTime',2);
end


function [bA,map]  = getImageWithCursor(f,x,y,cImage,cAlpha)
frame = getframe(f);
I = frame2im(frame);
dS = length(cImage) - 1;

% cursor overlay
cROI = I(y:y+dS, x:x+dS, :);  % get data of overlay region
cROI(cAlpha) = cImage(cAlpha);  % overwrite by cursor icon data
I(y:y+dS, x:x+dS, :) = cROI;  % write back to frame data

[bA,map] = rgb2ind(I,256);
end


あとがき

多少座標変換が面倒ですが、特に難しいところはないかと思います。

やはり、全て MATLAB で完結していた方が気分が良いですよね。(u_u)


タイトル画像モデル:海老澤一恵
The title image was created using Adobe Generative Fill based on this picture.

Original Image



この記事が気に入ったらサポートをしてみませんか?