一、基礎理論
- 採用區域生長法分割下列數字圖像,分別以圖中的灰色點 P(5,3)、Q(5,7)爲起始生長點, 生長準則爲相鄰像素的灰度差不超過 2。畫出分割後的二值圖像,並計算目標區域的面積和歐拉數。
參考答案: 採用8連通方式時,整張數字圖像所有像素被標記爲灰色。採用4連通方式時,分割後的二值圖像如下圖所示,其中,第1個目標區域的面積爲54個像素面積和,第2個目標區域的面積爲1個像素面積和;二值圖像分析中的歐拉數等於連通組件的個數減去連通組件內部洞(孔)的個數,故兩個目標區域的歐拉數皆爲1。
圖像歐拉數的定義可以參考博文利用OpenCV實現歐拉數的計算。
- 分別採用4連通或8連通準則,標識出下圖中的目標區域, 並簡要寫出算法步驟。
參考答案: 算法描述如下。
(1) 設置全局變量,第一遍逐行掃描像素。若遇到0像素,則直接跳過。否則,根據4連通準則或8連通準則觀察當前像素的鄰居像素,若存在鄰居像素的編號大於1,則以該鄰居像素的編號作爲當前像素的編號;若不存在鄰居像素的編號大於1,則將作爲當前像素的編號,同時設置,同時需觀察鄰居像素的編號是否存在衝突(產生衝突的2個相鄰像素的編號不同且皆大於1),若存在編號衝突,則在等價表中記錄下這對編號。
(2) 第二遍逐行掃描像素。檢查當前像素的編號是否存在於等價表中,若是,則將當前像素的編號替換成等價關係中最小的那個編號。
- 給出圖像中灰色區域的邊界描述:寫出各自的原鏈碼、差分碼和形狀數(※號表示起點像素)。
參考答案: 原鏈碼、差分碼和形狀數的定義很好理解,認真複習即可。
圖像區域的邊界編碼
區域邊界 | 圖像A(4方向碼) | 圖像B(8方向碼) |
---|---|---|
原鏈碼 | 12101010303033232212 | 322170167665443 |
差分碼 | 31331313313130313031 | 070761151707707 |
形狀數 | 03130313133131331313 | 070707611517077 |
- 通過文獻閱讀和分析,闡明張正友相機標定方法的原理、步驟與特點。若已知一個相機的標定矩陣,如何確定其內外部參數?
參考答案: 相機成像系統中,共包含四個座標系:世界座標系、相機座標系、圖像座標系、像素座標系,四個座標系關係如下圖所示,
這四個座標系之間的轉化關係爲
其中爲在世界座標系下一點的物理座標,爲該點對應的在像素座標系下的像素座標,爲尺度因子。此外,
爲相機的內參矩陣,其中爲像距,分別爲方向上的一個像素在相機感光板上的物理長度,分別表示相機感光板中心在像素座標系下的座標,表示感光板的軸和軸之間的夾角(理想情況下)。而矩陣
爲相機的外參矩陣,爲旋轉矩陣,右下角表示矩陣維度3行3列,爲平移矢量。上述座標系關係具體推導可參考:https://www.cnblogs.com/zyly/p/9366080.html。
原理: 張正友教授將世界座標系固定於西洋棋棋盤上,並假設。由於西洋棋棋盤的世界座標系是人爲事先定義好的,且每一個格子的大小已知,通過計算可得到每一個角點在世界座標系下的物理座標。通過圖像識別與匹配,可進一步得到每一個角點的像素座標。我們利用成對的物理座標和像素座標來求解相機的標定矩陣(內參矩陣與外參矩陣的積),進而求解內參矩陣和外參矩陣。原理的具體推導可參考:https://zhuanlan.zhihu.com/p/94244568和https://blog.csdn.net/qq_40369926/article/details/89251296。
步驟: 打印一張西洋棋棋盤模板貼在一個固定的平面上;從不同角度拍攝若干張模板圖像,檢測出圖像中的角點並記錄成對的物理座標與像素座標;求解理想無畸變情況下的相機內參和外參,並用極大似然估計法優化估計值;應用最小二乘法求出實際的徑向畸變係數;綜合內參、外參和畸變係數,使用極大似然法優化估計值,提升估計精度,最終輸出相機內參、外參和畸變係數。
特點: 傳統標定法需要精確製作的三維標定板來完成標定工作。張正友教授提出的方法介於傳統標定法和自標定法之間,該方法克服了傳統標定法需要的高精度標定板的缺點,它僅需打印一個西洋棋棋盤模板即可完成標定工作;此外,相對於自標定法而言,該方法提高了標定精度。
注意. 這裏的外參矩陣是相對外參。
- 如圖,已知物點Q在左相機中的像點a 的數字圖像座標爲, 試確定在右相機成像平面中與像點a 對應的像點b 的位置範圍(繪圖示意)。假定兩個相機的標定矩陣和已知,試給出求解像點b數字圖像座標的方法和步驟。
參考答案: 僅知道像點a的數字圖像座標,無法確切知道物點Q的物理座標,因此Q有可能是射線aQ上的某一點。連接像點a與右相機的光心,得到像點;接着連接像點a與像點至右相機成像平面邊界線,紅色線即像點b的位置範圍。
極線十分有用,在右圖像上用模板匹配尋找點屬於暴力搜索較爲耗時,且可能存在多個相似點,加上極線約束後就可以減少耗時且篩選無關相似點。確定了左、右相機的成像點座標和,就可以估計點Q的大致世界座標。
認真聽課,題目大致都可以做的出來。
二、綜合應用
- 圖1是一張大米籽粒圖片,試設計適當的圖像分析算法統計其中顯示完整的大米粒數;並找出其中體積最大的米粒位置(標註出其質心位置)。
參考答案: 以下是matlab代碼,
close all;
clear all;
clc;
I = imread('images/MV2001.png');
% 消除光照不均的圖像處理(形態學濾波)
background = imopen(I, strel('disk',15));
I2 = I - background;
% figure, subplot(121), imshow(I2), title('消除光照影響後的圖像');
% subplot(122), imhist(I2), title('光照處理圖像的直方圖')
% 圖像增強
I3 = imadjust(I2);
% figure, imshow(I3); title('對比度增強後的處理圖像');
% 全局閾值分割
% figure, imhist(I3); title('增強處理圖像的直方圖'); % 直方圖分析
bw = (I3 > 130);
bw = bwareaopen(bw, 50); % 消除二值圖像中面積小於50的對象
% 顯示分割結果
% figure, subplot(121), imshow(I), title('原始圖像');
% subplot(122), imshow(bw), title('光照處理的全局閾值分割');
% 進行二次分割
% 在一次分割基礎上檢測米粒邊緣以消除細小黏連部分
Ik = bw;
hy = fspecial('prewit');
hx = hy';
Iy = imfilter(double(Ik), hy, 'replicate');
Ix = imfilter(double(Ik), hx, 'replicate');
% 分水嶺分割法
gradmag = sqrt(Ix.^2 + Iy.^2);
L = watershed(gradmag);
Lrgb = label2rgb(L, 'spring', 'c', 'shuffle');
% figure, imshow(Lrgb);
% 消除面積小於40的對象
num_of_regions = max(max(L));
J = zeros(size(L));
% 編號0是背景,編號1是邊界,故從2開始循環
for i = 2:num_of_regions
if sum(sum(L == i)) >= 40
J(L == i) = 1;
end
end
% figure, imshow(J);
% 統計連通區域,一塊連通區域即一粒大米
[r, num] = bwlabel(J, 8);
% 打印米粒個數
disp(['完整大米的粒數:', num2str(max(max(r)))]);
% 在原圖中繪製邊界框
rects = regionprops(r, 'boundingbox');
rects = cat(1, rects.BoundingBox);
figure, imshow(I);
for i = 1:size(rects, 1)
rectangle('position', rects(i, :), 'Edgecolor', 'y', 'LineWidth', 1);
end
% 找出體積最大的米粒的位置(標註出其質心位置)
% 找出體積最大的米粒的編號
max_size = 0;
max_size_index = 0;
for i = 1:num
size_i = sum(sum(r == i));
if size_i > max_size
max_size = size_i;
max_size_index = i;
end
end
% 計算質心位置
seg_of_max_size = (r == max_size_index);
t_x = zeros(size(r));
t_y = zeros(size(r));
[y_len, x_len] = size(r);
for x = 1:x_len
t_x(:, x) = x .* seg_of_max_size(:, x);
end
for y = 1:y_len
t_y(y, :) = y .* seg_of_max_size(y, :);
end
c_x = sum(sum((r == max_size_index) .* t_x)) / max_size;
c_y = sum(sum((r == max_size_index) .* t_y)) / max_size;
disp(['最大米粒的質心位置', '(', num2str(c_x), ',', num2str(c_y), ')']);
hold on;
plot(c_x, c_y, 'r.', 'MarkerSize', 20);
figure, subplot(1, 2, 1), imshow(I);
subplot(1, 2, 2), imshow(I);
for i = 1:size(rects, 1)
rectangle('position', rects(i, :), 'Edgecolor', 'y', 'LineWidth', 1);
end
hold on
plot(c_x, c_y, 'r.', 'MarkerSize', 20);
% 一次分割與二次分割的對比
% figure, subplot(1, 3, 1), imshow(I), title('原圖');
% [bw_r, bw_n] = bwlabel(bw, 8);
% bwrgb = label2rgb(bw_r, 'spring', 'c', 'shuffle');
% subplot(1, 3, 2), imshow(bwrgb), title('一次分割');
% subplot(1, 3, 3), imshow(Lrgb), title('二次分割');
% 直接在原圖上使用分水嶺分割的結果
% Ik = I;
% hy = fspecial('prewit');
% hx = hy';
% Iy = imfilter(double(Ik), hy, 'replicate');
% Ix = imfilter(double(Ik), hx, 'replicate');
% % 分水嶺分割法
% gradmag = sqrt(Ix.^2 + Iy.^2);
% L = watershed(gradmag);
% Lrgb = label2rgb(L, 'spring', 'c', 'shuffle');
% figure, imshow(Lrgb), title('直接使用分水嶺分割');
運行效果圖
- 圖2是一幅動物圖像,試分別採用K-Means和Mean Shift算法實現對目標區域的分割;並分析兩種算法各自的優缺點。
參考答案: K-Means算法原理:https://www.jianshu.com/p/e4d5a0fbcefe,和Mean Shift算法原理:https://blog.csdn.net/hjimce/article/details/45718593。K-Means算法和Mean Shift算法都是聚類算法,通過將前景像素聚爲一個類簇、將背景像素聚爲一個類簇,從而實現圖像分割。爲了完成像素的聚類,需要提取像素的特徵,可以從顏色、空間等方面入手設計特徵提取器。
開源代碼可直接從Matlab社區下載:https://www.mathworks.com/matlabcentral/fileexchange/52698-k-means-mean-shift-and-normalized-cut-segmentation,Overview頁面是算法接口說明,Functions頁面是詳細代碼。
Km.m
function Ikm = Km(I, K)
% K-means Segmentation (option: K (Number of Clusters))
I = im2double(I);
F = reshape(I, size(I, 1) * size(I, 2), 3); % Color Features
% K-means
CENTS = F(ceil(rand(K, 1) * size(F, 1)), :); % Cluster Centers
DAL = zeros(size(F, 1), K + 2); % Distances and Labels
KMI = 10; % K-means Iteration
for n = 1:KMI
for i = 1:size(F, 1)
for j = 1:K
DAL(i, j) = norm(F(i, :) - CENTS(j, :));
end
[Distance, CN] = min(DAL(i, 1:K)); % 1:K are Distance from Cluster Centers 1:K
DAL(i, K + 1) = CN; % K+1 is Cluster Label
DAL(i, K + 2) = Distance; % K+2 is Minimum Distance
end
for i = 1:K
A = (DAL(:, K + 1) == i); % Cluster K Points
CENTS(i, :) = mean(F(A, :)); % New Cluster Centers
if sum(isnan(CENTS(:))) ~= 0 % If CENTS(i,:) Is Nan Then Replace It With Random Point
NC = find(isnan(CENTS(:, 1)) == 1); % Find Nan Centers
for Ind = 1:size(NC, 1)
CENTS(NC(Ind), :) = F(randi(size(F, 1)), :);
end
end
end
end
X = zeros(size(F));
for i = 1:K
idx = find(DAL(:, K + 1) == i);
X(idx, :) = repmat(CENTS(i, :), size(idx, 1), 1);
end
Ikm = reshape(X, size(I, 1), size(I, 2), 3);
end
MeanShiftCluster.m
function [clustCent, data2cluster, cluster2dataCell] = MeanShiftCluster(dataPts, bandWidth, plotFlag)
% perform MeanShift Clustering of data using a flat kernel
%
% ---INPUT---
% dataPts - input data, (numDim x numPts)
% bandWidth - is bandwidth parameter (scalar)
% plotFlag - display output if 2 or 3 D (logical)
% ---OUTPUT---
% clustCent - is locations of cluster centers (numDim x numClust)
% data2cluster - for every data point which cluster it belongs to (numPts)
% cluster2dataCell - for every cluster which points are in it (numClust)
%
% Bryan Feldman 02/24/06
% MeanShift first appears in
% K. Funkunaga and L.D. Hosteler, "The Estimation of the Gradient of a
% Density Function, with Applications in Pattern Recognition"
%*** Check input ****
if nargin < 2
error('no bandwidth specified')
end
if nargin < 3
plotFlag = false;
end
%**** Initialize stuff ***
[numDim, numPts] = size(dataPts);
numClust = 0;
bandSq = bandWidth^2;
initPtInds = 1:numPts;
maxPos = max(dataPts, [], 2); % biggest size in each dimension
minPos = min(dataPts, [], 2); % smallest size in each dimension
boundBox = maxPos - minPos; % bounding box size
sizeSpace = norm(boundBox); % indicator of size of data space
stopThresh = 1e-3 * bandWidth; % when mean has converged
clustCent = []; % center of clust
beenVisitedFlag = zeros(1, numPts, 'uint8'); % track if a points been seen already
numInitPts = numPts; % number of points to posibaly use as initilization points
clusterVotes = zeros(1, numPts, 'uint16'); % used to resolve conflicts on cluster membership
while numInitPts
tempInd = ceil((numInitPts - 1e-6) * rand); % pick a random seed point
stInd = initPtInds(tempInd); % use this point as start of mean
myMean = dataPts(:, stInd); % intilize mean to this points location
myMembers = []; % points that will get added to this cluster
thisClusterVotes = zeros(1, numPts, 'uint16'); % used to resolve conflicts on cluster membership
while 1 %loop untill convergence
sqDistToAll = sum((repmat(myMean, 1, numPts) - dataPts).^2); % dist squared from mean to all points still active
inInds = find(sqDistToAll < bandSq); %points within bandWidth
thisClusterVotes(inInds) = thisClusterVotes(inInds) + 1; % add a vote for all the in points belonging to this cluster
myOldMean = myMean; % save the old mean
myMean = mean(dataPts(:, inInds), 2); % compute the new mean
myMembers = [myMembers inInds]; % add any point within bandWidth to the cluster
beenVisitedFlag(myMembers) = 1; % mark that these points have been visited
%*** plot stuff ****
if plotFlag
figure(12345), clf, hold on
if numDim == 2
plot(dataPts(1, :), dataPts(2, :), '.')
plot(dataPts(1, myMembers), dataPts(2, myMembers), 'ys')
plot(myMean(1), myMean(2), 'go')
plot(myOldMean(1), myOldMean(2), 'rd')
pause(.1)
end
end
%**** if mean doesn't move much stop this cluster ***
if norm(myMean - myOldMean) < stopThresh
% check for merge posibilities
mergeWith = 0;
for cN = 1:numClust
distToOther = norm(myMean - clustCent(:, cN)); % distance from posible new clust max to old clust max
if distToOther < bandWidth / 2 %if its within bandwidth/2 merge new and old
mergeWith = cN;
break;
end
end
if mergeWith > 0 % something to merge
clustCent(:,mergeWith) = 0.5*(myMean+clustCent(:,mergeWith)); % record the max as the mean of the two merged (I know biased twoards new ones)
% clustMembsCell{mergeWith} = unique([clustMembsCell{mergeWith} myMembers]); % record which points inside
clusterVotes(mergeWith, :) = clusterVotes(mergeWith, :) + thisClusterVotes; % add these votes to the merged cluster
else % its a new cluster
numClust = numClust+1; % increment clusters
clustCent(:, numClust) = myMean; % record the mean
% clustMembsCell{numClust} = myMembers; %store my members
clusterVotes(numClust, :) = thisClusterVotes;
end
break;
end
end
initPtInds = find(beenVisitedFlag == 0); % we can initialize with any of the points not yet visited
numInitPts = length(initPtInds); % number of active points in set
end
[val, data2cluster] = max(clusterVotes, [], 1); % a point belongs to the cluster with the most votes
%*** If they want the cluster2data cell find it for them
if nargout > 2
cluster2dataCell = cell(numClust, 1);
for cN = 1:numClust
myMembers = find(data2cluster == cN);
cluster2dataCell{cN} = myMembers;
end
end
end
Ms.m
function [Ims, Kms] = Ms(I, bandwidth)
% Mean Shift Segmentation (option: bandwidth)
I = im2double(I);
X = reshape(I, size(I, 1) * size(I, 2), 3); % Color Features
% MeanShift
[clustCent, point2cluster, clustMembsCell] = MeanShiftCluster(X', bandwidth); % MeanShiftCluster
for i = 1:length(clustMembsCell) % Replace Image Colors With Cluster Centers
X(clustMembsCell{i}, :) = repmat(clustCent(:, i)', size(clustMembsCell{i}, 2), 1);
end
Ims = reshape(X, size(I, 1), size(I, 2), 3); % Segmented Image
Kms = length(clustMembsCell);
end
main.m
clc;
clear all;
close all;
% input
I = imread('images/MV2003.png');
% parameters
% K-means parameter
K = 2; % Cluster Numbers
% Mean-Shift parameter
bw = 0.2; % Mean-Shift Bandwidth
% segmentation
% K-means
Ikm = Km(I, K); % Kmeans (color)
% Mean-Shift
[Ims, Nms] = Ms(I, bw); % Mean-Shift (color)
% show
figure, subplot(1, 3, 1), imshow(I), title('原圖');
subplot(1, 3, 2), imshow(Ikm), title('K-Means分割結果(k = 2)');
subplot(1, 3, 3), imshow(Ims), title('Mean Shift分割結果');
運行效果圖
Reference
提取二值圖中連通域的包圍框 matlab
MATLAB中用rectangle在圖像上畫出個矩形,線的顏色默認是黑色,怎麼去改變它?
Convert label matrix into RGB image
MATLAB:regionprops函數求取最大連通域面積