大きな二次元配列から​不特定の間隔かつ特定​サイズの二次元行列を​for loopを使わずに取り出す方法に関して

目的としては、大きな二次元の配列(画像等)から特定の大きさを持つ配列を抽出し、それをCell配列や通常のArrayとして格納したいと考えています。
具体的な状況は添付の写真のように、大きな二次元配列中に決まった間隔ではない、特定の大きさで、互いに接触しない1で識別された領域が存在するとします。この大きな配列から添付写真に示すような1で構成された3x3の像を初期の大きな像における位置関係を保持したまま、cell配列ないしは通常の配列変数としてfor loopを行わずに並び替えたいと考えています。
現時点で試したこととして、findや論理インデックスを利用して取り出す方法などを試していますが、この方法では1に対応する配列が列ごとに並び替えられて出力されてしまい、目的の形状に変形し保存が難しい状況にあります。結局やりたいこととしては、0に対応するすべての配列を削除し、残っている配列を位置関係を保持したまま結合させたいということです。
こちらのコードもforとifを利用して実装することはできるのですが、仮に何らかの0などの数値を除去する関数等があってループを使用せずに目的配列のデータが取得する事ができればすごく助かります。
ご不明な点が有ればコメントいただけると嬉しいです。よろしくお願いいたします。

 Accepted Answer

Hernia Baby
Hernia Baby on 27 Feb 2021

2 votes

まずは下準備
clc,clear,close all;
% 抜き出す部分行列はk×kとする
k = 3;
% ランダムな行列を作成
X = randn(11,12);
% 図と同じ分布を作る
A = zeros(11,12);
strt = [2, 2; 3,9; 7,7; 8,3];
for i = 1:size(strt(:,1))
A(strt(i,1):strt(i,1)+k-1,strt(i,2):strt(i,2)+k-1) = 1;
end
次に各部分行列の最初の行番号と列番号のみを抽出します。
それにそってk×kの行列を抜き出せばいいからです。
% 各部分行列の最初の(行,列)のみ抽出
vec_l = zeros(size(A(1,:)));
vec_r = zeros(size(A(:,1)));
A_lslide = [vec_l; A(1:end-1,:)];
A_lxor = xor(A,A_lslide);
A_l = A.*A_lxor;
A_l_rslide = [vec_r,A_l(:,1:end-1)];
A_l_rs_xor = xor(A_l,A_l_rslide);
A_lr = A_l.*A_l_rs_xor
A_lr =
0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
ができました
次に該当するセルの行番号、列番号を抜き出します。
今回は列の低い順(左から右)に進む順に番号を割り当てています。
ここまで行けば部分行列を抜き出せるので好きに連結できます。
[x, y] = find(A_lr ~= 0);
[x, y] % チェック用
ans =
2 2
8 3
7 7
3 9
最後に部分行列を抜き出して連結します。
length(x)分の部分行列(今回は4つ)ができますので、2×2にいったん分けます。
縦方向に連結した後に、横方向に連結しています。
% 連結
X_ph_s = X_p(:,:,1);
X_ph_f = X_p(:,:,length(x)/2+1);
cnt = 1;
while cnt < length(x)/2
X_ph_s = vertcat(X_ph_s,X_p(:,:,cnt+1));
X_ph_f = vertcat(X_ph_f,X_p(:,:,length(x)/2+cnt+1));
cnt = cnt + 1;
end
X_ex = horzcat(X_ph_s,X_ph_f)
X_ex =
-0.7049 1.3229 -0.7501 -0.0275 3.4553 -0.4563
0.8029 -0.4669 1.3345 2.6822 0.2942 -0.6431
-0.3228 -0.1699 0.0159 1.9408 -0.8317 -0.6652
-0.5139 -0.5316 1.7985 -2.6426 2.3228 -1.1011
-0.0719 -0.3853 -0.3498 0.8617 -1.5182 0.9805
0.4289 1.2715 0.0501 -1.3336 -0.3510 -0.5748
確認用に元のXで該当する部分だけを見てみましょう
X.*A

6 Comments

作ったコードはこちら
clc,clear,close all;
% 抜き出す部分行列はk×kとする
k = 3;
% ランダムな行列を作成
X = randn(11,12);
% 図と同じ分布を作る
A = zeros(11,12);
strt = [2, 2; 3,9; 7,7; 8,3];
for i = 1:size(strt(:,1))
A(strt(i,1):strt(i,1)+k-1,strt(i,2):strt(i,2)+k-1) = 1;
end
% 各部分行列の最初の(行,列)のみ抽出
vec_l = zeros(size(A(1,:)));
vec_r = zeros(size(A(:,1)));
A_lslide = [vec_l; A(1:end-1,:)];
A_lxor = xor(A,A_lslide);
A_l = A.*A_lxor;
A_l_rslide = [vec_r,A_l(:,1:end-1)];
A_l_rs_xor = xor(A_l,A_l_rslide);
A_lr = A_l.*A_l_rs_xor;
[x, y] = find(A_lr ~= 0);
% 各部分行列を抽出
for i = 1:size(x)
X_p(:,:,i) = X(x(i):x(i)+k-1,y(i):y(i)+k-1);
end
% 連結
X_ph_s = X_p(:,:,1);
X_ph_f = X_p(:,:,length(x)/2+1);
cnt = 1;
while cnt < length(x)/2
X_ph_s = vertcat(X_ph_s,X_p(:,:,cnt+1));
X_ph_f = vertcat(X_ph_f,X_p(:,:,length(x)/2+cnt+1));
cnt = cnt + 1;
end
X_ex = horzcat(X_ph_s,X_ph_f)
K.S.
K.S. on 2 Mar 2021
ご回答誠にありがとうございます!
非常に参考になりました。まずお礼申し上げます。
しかし、依然として配置関係を維持したまま切り取りデータを並び替える部分がうまくいきません。
最終的な出力arrayであるX_exでは、右斜め下のデータが右上に配置されています。これを正確な位置に配置するためのルールをどのように設けるかを考察中です。
例えば第一セルに配置される初期インデックス(2,2)を2*2などとして並び替える方法を考えてみましたが、この場合は例えば以下の様な数列が得られている場合エラーになるのだろうか?という所で現在止まっています。
いずれにせよ非常に参考になったので、ありがとうございました!!
sortrows関数を使って並べ替えを行えばできます。
まず左から右順に座標を並べ替えて左半分と右半分の行列を分断します。
上から下順にそれぞれの行列を並べ替えます。
% 順番を並び替える
[x, y] = find(A_lr ~= 0);
% 左から右の順番に並び替える
idx = sortrows([x,y],2);
% 半分に区切って上から下の順番にそれぞれ並び替える
idx(1:2,:) = sortrows(idx(1:2,:),1);
idx(3:4,:) = sortrows(idx(3:4,:),1);
% x,yを更新
x = idx(:,1);
y = idx(:,2);
突貫で作りましたが、以下のコードになります。
4つの行列を抽出する場合、限定です。
clc,clear,close all;
% 抜き出す部分行列はk×kとする
k = 3;
% ランダムな行列を作成
X = randn(11,12);
% 図と同じ分布を作る
A = zeros(11,12);
strt = [2, 2; 3,9; 7,7; 8,3];
for i = 1:size(strt(:,1))
A(strt(i,1):strt(i,1)+k-1,strt(i,2):strt(i,2)+k-1) = 1;
end
% 各部分行列の最初の(行,列)のみ抽出
vec_l = zeros(size(A(1,:)));
vec_r = zeros(size(A(:,1)));
A_lslide = [vec_l; A(1:end-1,:)];
A_lxor = xor(A,A_lslide);
A_l = A.*A_lxor;
A_l_rslide = [vec_r,A_l(:,1:end-1)];
A_l_rs_xor = xor(A_l,A_l_rslide);
A_lr = A_l.*A_l_rs_xor;
% 順番を並び替える
[x, y] = find(A_lr ~= 0);
% 左から右の順番に並び替える
idx = sortrows([x,y],2);
% 半分に区切って上から下の順番にそれぞれ並び替える
idx(1:2,:) = sortrows(idx(1:2,:),1);
idx(3:4,:) = sortrows(idx(3:4,:),1);
% x,yを更新
x = idx(:,1);
y = idx(:,2);
% 各部分行列を抽出
for i = 1:size(x)
X_p(:,:,i) = X(x(i):x(i)+k-1,y(i):y(i)+k-1);
end
% 連結
X_ph_s = X_p(:,:,1);
X_ph_f = X_p(:,:,length(x)/2+1);
cnt = 1;
while cnt < length(x)/2
X_ph_s = vertcat(X_ph_s,X_p(:,:,cnt+1));
X_ph_f = vertcat(X_ph_f,X_p(:,:,length(x)/2+cnt+1));
cnt = cnt + 1;
end
X_ex = horzcat(X_ph_s,X_ph_f)
X_ex =
-0.0890 -0.3186 0.3060 -1.9272 0.2569 -0.9979
-1.7734 -0.4938 -1.2305 0.2996 0.4722 -0.8652
-2.2755 0.1394 0.4226 1.5051 -1.6795 0.1100
-2.5442 0.4766 1.0540 -1.0678 1.5880 -0.4633
0.7820 -0.8282 -0.7393 0.0909 -0.3851 -0.7445
1.2671 -1.7880 0.7750 -0.1203 0.3233 -1.5090
X.*A
Hernia Baby
Hernia Baby on 2 Mar 2021
Edited: Hernia Baby on 2 Mar 2021
ちなみにこのコードはパッと考える限り二つほど弱点があるなと思いました
1.以下のようなケース
001110011100000000
001110011100000000
001110011100000000
000000000111011100
000000000111011100
000000000111011100
順番がおかしくなります
2.以下のようなケース
000111000000111000
000111000000111000
000111000000111000
000111000000111000
000111000000111000
000111000000111000
上二つしか行列を抽出してくれません
(下の二つは消える)
----------------
これは別途考える必要があるなと思っています
並べ替えはここで書くと長くなりそうなので、
どうしてもだめでしたら再度MATLAB ANSWERSにご投稿ください
Hernia Baby
Hernia Baby on 20 Mar 2021
K.S.
K.S. on 21 Mar 2021
ありがとうございます!!!!!
こちらも、いくつか実行時エラーが出てきていましたので事前に領域区分した状態で数値が存在している部分を探索するようなコードを記述して解決してきました。まだ、Hernia様のコードは確認できておりませんが、取り急ぎお礼のご連絡まで。

Sign in to comment.

More Answers (0)

Categories

Products

Release

R2020a

Asked:

on 26 Feb 2021

Commented:

on 21 Mar 2021

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!