世界四强平均脸的Matlab实现

平均
截止2026年7月28日,马云和我的平均资产达到133亿美元。
内涵
平均,是一个非常激进也经常被批评的统计学概念。 平均抹去单个数据中蕴含的丰富信息,无视单个数据的特定故事, 把多个数据作为一个集合来考虑,忽略差异,用单一数据来表达整个集合。 这是一个非常强的概念。
平均,这个动作必须涉及到一个对象集合,这个集合至少应该有2个元素。对于古老的平均概念,特别是原始的平均,这个集合的元素个数必须为正整数,因此这个集合是有限可数的。
$$\{x_1, x_2, \ldots, x_n\}, n - 1 \in \mathbb{Z^+}$$算术平均数是最常用的平均数,它是集合中所有元素的和除以元素个数。
$$ \bar{x} = \frac{1}{n}\sum_{i=1}^{n}x_i $$隐藏一个集合$\{x_1, x_2, \ldots, x_n\}, n - 1 \in \mathbb{Z^+}$,提供单一的聚合量$\bar{x}$,体现了人类思维的抽象能力,忘却差异,归纳总结,排除细节,把个体数据特点置于背景之中,得到一个更有意义的整体信息。
算术平均数的应用
在英联邦体系中,长度的定义,foot,英尺。这个单词的原义是脚,也就是一英尺等于一个人的脚的长度。显然,每个人的脚的长度都不一样。那么应该选择英国国王的脚?那么每次更换国王是不是还需要重新丈量已有的土地?还是选择哪个领主的脚?还是选择哪个农民的脚?显然,这中单个个体的选择方法全部都是没有意义的(可以由反证法很容易得到)。并且,每个人的脚还会随着年龄发生变化。16世纪中期的雅各布·科贝尔,给出一个非常实用的定义:在教堂礼拜之后留下16为市民代表,让他们脚挨着脚站在一起,测量整个长度,然后分为16份,得到一个平均的脚长。

被平均的意义
从最内心的感觉和情感体验上看,平均的激进让很多人都不适应,每个人都觉得自己已经被平均了,自己的独特性、个性、特殊性全部不见了,简直是让人生气,如果不是绝望的话。的确,平均的唯一支撑理念就是:通过忘记、忽略,提供更少的信息,从而得到更加有意义的信息。概括得到的信息超越个体。要接受“更少的信息反而是更多的信息”,需要有很强的心理承受能力。
那么对于个体而言,被平均本身有没有什么意义呢?每一个个体都交叉的属于不同的集合(团体),那么这个团体的各种平均量有没有意义呢?是不是我们也在依赖各种各种的聚合量(包括平均值)来获取心理支撑呢?
1980年,我国城乡居民人均肉类消费为12.70kg;1990年增长到15.91kg;2000年为20.22kg;2011年为28.20kg;2025年,这个数据达到37.9kg1。在我出生的前10年(1980-1990),假设我的智商和知识面神奇地达到后互联网时代的水平,我可能会很容易感觉到自己被平均了,因为我整年都吃不到任何肉食;但是自从2000年之后,我的体重开始随着肉类摄入的整体增长而增长;到2011年,我爱人已经对我的体重增长趋势产生了严重的担忧;现在,我爱人甚至开始限制我的肉类摄入量。
总体而言,对个别的样本,平均值会掩盖其个体真实情况;但是平均值的趋势变化,会逐步反应到几乎所有的个体之上。
Matlab的mean函数2
函数帮助
我们已经知道了平均的内涵和意义,那么如何在计算机中实现平均呢?在Matlab中,提供了mean函数来计算平均值。
1A = rand(10, 1);
2mean(A)
这是最简单的平均值计算方法,mean函数可以计算向量的平均值,也可以计算矩阵的平均值。甚至mean函数还能对三维以上的数组进行平均值计算。
1M = mean(A)
2M = mean(A,"all")
3M = mean(A,dim)
4M = mean(A,vecdim)
5M = mean(___,outtype)
6M = mean(___,missingflag)
7M = mean(___,Weights=W)
这些是mean函数的不同用法,大概的含义可以很容易才出来,具体可以参考Matlab文档2。
加权平均
$$ \bar{x} = \frac{\sum_{i=1}^{n}w_ix_i}{\sum_{i=1}^{n}w_i} \tag{1} $$这里的$w_i$是权重,$x_i$是数据,$n$是数据个数。
实际上,最小二乘法也是一种平均方法,它是通过最小化误差平方和来求解最优参数,从而得到一个最优值,但是也可以从平均的角度来理解最小二乘法。
对于数据集合$\{(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)\}$,我们可以通过最小二乘法来拟合一条直线$y = kx + b$,其中$k$是斜率,$b$是截距。
$$ \begin{split} \hat{k} &= \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{n} (x_i - \bar{x})^2} \\ \hat{b} &= \bar{y} - \hat{k}\bar{x} \\ \end{split} $$这里的$\hat{k}$可以写为:
$$ \hat{k} = \frac{ \sum_{i=1}^{n} (x_i - \bar{x})^2 \frac{(y_i - \bar{y})}{(x_i - \bar{x})} }{ \sum_{i=1}^{n} (x_i - \bar{x})^2 }\tag{2} $$很容易观察到(2)式与(1)式的类似之处。 实际上,最小二乘法就是对数据点的斜率进行加权平均,权重为$(x_i - \bar{x})^2$,斜率为$\frac{(y_i - \bar{y})}{(x_i - \bar{x})}$。
四强
平均人
19世纪,均值已经在天文学、测地学等领域取得绝对的支配地位。 但是在社会学中,均值还是一个非常激进的概念,甚至被认为是一个非常不科学的概念。因为社会学中,个体差异非常大,平均值可能会掩盖个体的真实情况。
比利时的统计学家阿道夫·凯特勒(Adolphe Quetelet)在19世纪中期提出了“平均人”的概念,他认为通过对大量个体的测量,可以得到一个“平均人”,这个“平均人”可以代表整个群体的特征。这个概念在当时引起了很大的争议,因为它忽略了个体差异,甚至被认为是对个体的不尊重。
实际上,平均人是一个社会中不存在的人,但是,每个种群都有自己的平均人。这个观念被批评和攻击是非常容易理解的……毕竟……你懂的。
到19世纪70年代,弗朗西斯·高尔顿(Francis Galton)采用均值的思想来处理和分析非定量数据,通过叠加肖像照片,得到一个“平均脸”,这个“平均脸”可以代表整个群体的特征。这个概念在当时也引起了很大的争议……
下面,我们也来搞一点那啥的研究,得到什么结论你别管……
世界四强平均脸
我们在看世界杯的时候就经常讨论,特别是今年挪威队成绩特别好,而蠕动的阿根廷居然进四强简直是逆天……而大热门法国、英国、西班牙也不负众望……其中法国的绝对核心,从以前人见人爱的齐达内到没有存在感的里贝里,再到现在的姆巴佩……英国对虽然有哈里凯恩作为绝对核心,但是队员的构成跟贝克汉姆的时代已经发生了翻天覆地的变化……西班牙的核心现在是亚马尔……
实际上,这个话题还是挺敏感也挺明显的……就是最优秀的黑人运动员逐步也要占领足球的主流地位了……
下面是四强合照……我随便在网上找的,也没有太讲究,不知道是真的还是游戏建模……

我们就干脆用Matlab来计算一下四强的平均脸吧……
App3
这个程序实际还是非常先进的,利用Matlab提供的人脸识别功能,自动识别人脸,并且计算平均脸。
程序的核心算法包括三个部分:
用户界面设计非常简单,上方提供按钮来操作导入图像、检测人脸、计算平均、退出程序,下方提供一个图像显示区域来显示导入的图像、检测到的人脸、计算得到的平均脸。

程序提供了检测人脸之后,在图像上把人脸按序号标记出来。

有些时候,会产生错误的检测结果,程序提供了检查功能,可以查看检测到的人脸,并且可以反选删除错误的人脸,自动重新计算平均脸。

程序的代码如下,也可以通过3下载。
1classdef SimpleFaceAverageApp < matlab.apps.AppBase
2 % SimpleFaceAverageApp
3 % Modern MATLAB app using UIFigure and the App Framework.
4 % This is a new implementation and does not modify the original GUIDE version.
5 properties (Access = public)
6 UIFigure matlab.ui.Figure = matlab.ui.Figure.empty
7 end
8 properties (Access = private)
9 MainGrid matlab.ui.container.GridLayout = matlab.ui.container.GridLayout.empty
10 HeaderPanel matlab.ui.container.Panel = matlab.ui.container.Panel.empty
11 HeaderLabel matlab.ui.control.Label = matlab.ui.control.Label.empty
12 StatusLabel matlab.ui.control.Label = matlab.ui.control.Label.empty
13 LoadButton matlab.ui.control.Button = matlab.ui.control.Button.empty
14 DetectButton matlab.ui.control.Button = matlab.ui.control.Button.empty
15 AverageButton matlab.ui.control.Button = matlab.ui.control.Button.empty
16 CloseButton matlab.ui.control.Button = matlab.ui.control.Button.empty
17 ContentGrid matlab.ui.container.GridLayout = matlab.ui.container.GridLayout.empty
18 InputPanel matlab.ui.container.Panel = matlab.ui.container.Panel.empty
19 InputAxes matlab.ui.control.UIAxes = matlab.ui.control.UIAxes.empty
20 ResultPanel matlab.ui.container.Panel = matlab.ui.container.Panel.empty
21 ResultAxes matlab.ui.control.UIAxes = matlab.ui.control.UIAxes.empty
22 InstructionLabel matlab.ui.control.Label = matlab.ui.control.Label.empty
23 ImageData {mustBeNumeric} = []
24 FaceBoxes double = zeros(0, 4)
25 FaceIncluded logical = false(0, 1)
26 FaceTable matlab.ui.control.Table = matlab.ui.control.Table.empty
27 AveragedImage uint8 = uint8([])
28 LastResultClickTime double = NaN
29 InitialFolder char = '';
30 Detector vision.CascadeObjectDetector = vision.CascadeObjectDetector
31 end
32
33 methods (Access = public)
34 % Public entry point: create and show the app window.
35 function app = SimpleFaceAverageApp()
36 app.createUI();
37 app.InitialFolder = app.getAppFolder();
38 end
39 end
40
41 methods (Access = private)
42
43 function createUI(app)
44 app.UIFigure = uifigure('Name', 'Simple Face Average', ...
45 'Position', [100 100 1180 760], ...
46 'Color', [0.97 0.97 0.97], ...
47 'Resize', 'on');
48 app.UIFigure.CloseRequestFcn = @(src, event)app.closeApp();
49
50 app.MainGrid = uigridlayout(app.UIFigure, [2 1]);
51 app.MainGrid.RowHeight = {132, '1x'};
52 app.MainGrid.ColumnWidth = {'1x'};
53 app.MainGrid.Padding = [20 20 20 20];
54 app.MainGrid.RowSpacing = 14;
55 app.MainGrid.ColumnSpacing = 14;
56
57 app.HeaderPanel = uipanel(app.MainGrid, 'Title', '');
58 app.HeaderPanel.Layout.Row = 1;
59 app.HeaderPanel.Layout.Column = 1;
60 app.HeaderPanel.BackgroundColor = [1 1 1];
61
62 app.HeaderLabel = uilabel(app.HeaderPanel, ...
63 'Text', 'Simple Face Average', ...
64 'FontSize', 20, ...
65 'FontWeight', 'bold', ...
66 'Position', [16 78 260 24]);
67
68 app.LoadButton = uibutton(app.HeaderPanel, ...
69 'Text', 'Load Image', ...
70 'ButtonPushedFcn', @(src, event)app.loadImage(), ...
71 'Position', [16 38 100 26]);
72
73 app.DetectButton = uibutton(app.HeaderPanel, ...
74 'Text', 'Detect Faces', ...
75 'ButtonPushedFcn', @(src, event)app.detectFaces(), ...
76 'Position', [124 38 100 26]);
77
78 app.AverageButton = uibutton(app.HeaderPanel, ...
79 'Text', 'Create Average Face', ...
80 'ButtonPushedFcn', @(src, event)app.createAverageFace(), ...
81 'Position', [232 38 146 26]);
82
83 app.CloseButton = uibutton(app.HeaderPanel, ...
84 'Text', 'Close', ...
85 'ButtonPushedFcn', @(src, event)app.closeApp(), ...
86 'Position', [392 38 90 26]);
87
88 app.StatusLabel = uilabel(app.HeaderPanel, ...
89 'Text', 'Load an image to begin.', ...
90 'Position', [16 12 560 18]);
91
92 app.ContentGrid = uigridlayout(app.MainGrid, [1 2]);
93 app.ContentGrid.Layout.Row = 2;
94 app.ContentGrid.Layout.Column = 1;
95 app.ContentGrid.ColumnWidth = {'1x', '1x'};
96 app.ContentGrid.ColumnSpacing = 16;
97 app.ContentGrid.Padding = [0 0 0 0];
98
99 app.InputPanel = uipanel(app.ContentGrid, 'Title', 'Input Image');
100 app.InputPanel.Layout.Row = 1;
101 app.InputPanel.Layout.Column = 1;
102 app.InputPanel.BackgroundColor = [1 1 1];
103
104 innerInputGrid = uigridlayout(app.InputPanel, [2 1]);
105 innerInputGrid.RowHeight = {'fit', '1x'};
106 innerInputGrid.ColumnWidth = {'1x'};
107 innerInputGrid.Padding = [12 12 12 12];
108 innerInputGrid.RowSpacing = 8;
109
110 app.InstructionLabel = uilabel(innerInputGrid, ...
111 'Text', 'Choose an image, detect faces, and build an average.', ...
112 'Position', [12 12 420 22]);
113 app.InstructionLabel.Layout.Row = 1;
114 app.InstructionLabel.Layout.Column = 1;
115
116 app.InputAxes = uiaxes(innerInputGrid);
117 app.InputAxes.Layout.Row = 2;
118 app.InputAxes.Layout.Column = 1;
119 app.InputAxes.XTick = [];
120 app.InputAxes.YTick = [];
121 app.InputAxes.Box = 'on';
122
123 app.ResultPanel = uipanel(app.ContentGrid, 'Title', 'Average Face');
124 app.ResultPanel.Layout.Row = 1;
125 app.ResultPanel.Layout.Column = 2;
126 app.ResultPanel.BackgroundColor = [1 1 1];
127
128 innerResultGrid = uigridlayout(app.ResultPanel, [3 1]);
129 innerResultGrid.RowHeight = {'fit', '1x', 170};
130 innerResultGrid.ColumnWidth = {'1x'};
131 innerResultGrid.Padding = [12 12 12 12];
132 innerResultGrid.RowSpacing = 8;
133
134 resultLabel = uilabel(innerResultGrid, ...
135 'Text', 'The average face will appear here after processing.');
136 resultLabel.Layout.Row = 1;
137 resultLabel.Layout.Column = 1;
138
139 app.ResultAxes = uiaxes(innerResultGrid);
140 app.ResultAxes.Layout.Row = 2;
141 app.ResultAxes.Layout.Column = 1;
142 app.ResultAxes.XTick = [];
143 app.ResultAxes.YTick = [];
144 app.ResultAxes.Box = 'on';
145
146 app.FaceTable = uitable(innerResultGrid);
147 app.FaceTable.Layout.Row = 3;
148 app.FaceTable.Layout.Column = 1;
149 app.FaceTable.ColumnName = {'Face', 'Include', 'Bounds'};
150 app.FaceTable.ColumnEditable = [false true false];
151 app.FaceTable.CellEditCallback = @(src, event)app.onFaceTableEdited(event);
152 app.FaceTable.Data = app.buildEmptyFaceTable();
153 app.ResultAxes.ButtonDownFcn = @(src, event)app.onResultFaceClicked();
154
155 app.showPlaceholder();
156 end
157
158 function loadImage(app)
159 [fileName, folder] = uigetfile({ '*.jpg;*.jpeg;*.png;*.bmp;*.webp', 'Image files'; '*.*', 'All files' }, ...
160 'Select an image file', app.InitialFolder);
161 if isequal(fileName, 0)
162 return;
163 end
164
165 fullFileName = fullfile(folder, fileName);
166 app.ImageData = app.readImageFile(fullFileName);
167
168 app.FaceBoxes = [];
169 app.FaceIncluded = false(0, 1);
170
171 app.showImageOnInputAxes(app.ImageData);
172 app.showPlaceholderTable();
173 app.StatusLabel.Text = sprintf('Loaded %s', fileName);
174 end
175
176 function appFolder = getAppFolder(app)
177 classFile = which(class(app));
178 if isempty(classFile)
179 appFolder = pwd;
180 return;
181 end
182
183 appFolder = fileparts(classFile);
184 end
185
186 function detectFaces(app)
187 if isempty(app.ImageData)
188 msgbox('Load an image first.', 'No image', 'warn');
189 return;
190 end
191
192 % Detect candidate face regions using MATLAB's cascade detector.
193 app.FaceBoxes = step(app.Detector, app.ImageData);
194
195 if isempty(app.FaceBoxes)
196 msgbox('No faces were detected in the selected image.', 'No faces', 'warn');
197 app.StatusLabel.Text = 'No faces detected.';
198 app.refreshFaceTable();
199 return;
200 end
201
202 app.FaceIncluded = true(size(app.FaceBoxes, 1), 1);
203 app.refreshFaceTable();
204
205 app.renderDetectedFaces();
206 app.StatusLabel.Text = sprintf('Detected %d face(s).', size(app.FaceBoxes, 1));
207 end
208
209 function createAverageFace(app)
210 if isempty(app.ImageData)
211 msgbox('Load an image first.', 'No image', 'warn');
212 return;
213 end
214
215 if isempty(app.FaceBoxes)
216 app.detectFaces();
217 end
218
219 if isempty(app.FaceBoxes)
220 return;
221 end
222
223 includedFaces = find(app.FaceIncluded);
224 if isempty(includedFaces)
225 msgbox('Enable at least one face in the table before averaging.', 'No faces selected', 'warn');
226 app.StatusLabel.Text = 'No faces selected for averaging.';
227 return;
228 end
229
230 app.updateAverageFacePreview(includedFaces);
231 app.StatusLabel.Text = 'Average face created.';
232 end
233
234 function updateAverageFacePreview(app, includedFaces)
235 if nargin < 2
236 includedFaces = find(app.FaceIncluded);
237 end
238
239 if isempty(includedFaces)
240 app.AveragedImage = uint8([]);
241 cla(app.ResultAxes, 'reset');
242 placeholderHandle = imshow(zeros(200, 200, 3, 'uint8'), 'Parent', app.ResultAxes);
243 placeholderHandle.ButtonDownFcn = @(src, event)app.onResultFaceClicked();
244 axis(app.ResultAxes, 'image');
245 title(app.ResultAxes, 'Average face');
246 return;
247 end
248
249 resizedFaces = [];
250 % Core algorithm step 1: crop each selected face and normalize size.
251 for faceIndex = includedFaces(:)'
252 k = faceIndex;
253 crop = imcrop(app.ImageData, app.FaceBoxes(k, :));
254 crop = imresize(crop, [60 NaN]);
255 crop = im2double(crop);
256 [irow, icol, ~] = size(crop);
257 temp = reshape(crop, irow * icol, 3);
258 resizedFaces = cat(3, resizedFaces, temp);
259 end
260
261 if isempty(resizedFaces)
262 app.StatusLabel.Text = 'No face data available.';
263 return;
264 end
265
266 % Core algorithm step 2: average all selected faces pixel-wise in RGB.
267 meanImage = mean(resizedFaces, 3);
268 meanImage = reshape(meanImage, irow, icol, 3);
269 meanImage = im2uint8(meanImage);
270 meanImage = imresize(meanImage, 10);
271 app.AveragedImage = meanImage;
272
273 cla(app.ResultAxes, 'reset');
274 averageImageHandle = imshow(meanImage, 'Parent', app.ResultAxes);
275 averageImageHandle.ButtonDownFcn = @(src, event)app.onResultFaceClicked();
276 axis(app.ResultAxes, 'image');
277 title(app.ResultAxes, 'Average face');
278 end
279
280 function closeApp(app)
281 if isvalid(app.UIFigure)
282 delete(app.UIFigure);
283 end
284 end
285
286 function showImageOnInputAxes(app, imageData)
287 cla(app.InputAxes, 'reset');
288 imshow(imageData, 'Parent', app.InputAxes);
289 axis(app.InputAxes, 'image');
290 title(app.InputAxes, 'Input image');
291 end
292
293 function renderDetectedFaces(app)
294 app.showImageOnInputAxes(app.ImageData);
295
296 if isempty(app.FaceBoxes)
297 return;
298 end
299
300 hold(app.InputAxes, 'on');
301 for k = 1:size(app.FaceBoxes, 1)
302 faceBox = app.FaceBoxes(k, :);
303 isIncluded = isempty(app.FaceIncluded) || app.FaceIncluded(k);
304
305 if isIncluded
306 edgeColor = [1 0 0];
307 labelBackground = [1 0 0];
308 labelText = [1 1 1];
309 else
310 edgeColor = [0.5 0.5 0.5];
311 labelBackground = [0.5 0.5 0.5];
312 labelText = [1 1 1];
313 end
314
315 rectangle(app.InputAxes, 'Position', faceBox, ...
316 'EdgeColor', edgeColor, 'LineWidth', 2);
317
318 text(app.InputAxes, faceBox(1), max(faceBox(2) - 8, 1), sprintf('%d', k), ...
319 'Color', labelText, ...
320 'BackgroundColor', labelBackground, ...
321 'FontWeight', 'bold', ...
322 'FontSize', 11, ...
323 'Margin', 1, ...
324 'VerticalAlignment', 'bottom', ...
325 'Clipping', 'on');
326 end
327 hold(app.InputAxes, 'off');
328 end
329
330 function showPlaceholder(app)
331 cla(app.InputAxes, 'reset');
332 cla(app.ResultAxes, 'reset');
333 app.AveragedImage = uint8([]);
334 app.LastResultClickTime = NaN;
335 imshow(zeros(200, 200, 3, 'uint8'), 'Parent', app.InputAxes);
336 axis(app.InputAxes, 'image');
337 title(app.InputAxes, 'Input image');
338 placeholderHandle = imshow(zeros(200, 200, 3, 'uint8'), 'Parent', app.ResultAxes);
339 placeholderHandle.ButtonDownFcn = @(src, event)app.onResultFaceClicked();
340 axis(app.ResultAxes, 'image');
341 title(app.ResultAxes, 'Average face');
342 app.showPlaceholderTable();
343 end
344
345 function onResultFaceClicked(app)
346 if isempty(app.AveragedImage)
347 return;
348 end
349
350 currentClickTime = posixtime(datetime('now'));
351
352 if isnan(app.LastResultClickTime)
353 app.LastResultClickTime = currentClickTime;
354 return;
355 end
356
357 elapsedTime = currentClickTime - app.LastResultClickTime;
358 app.LastResultClickTime = currentClickTime;
359
360 if elapsedTime > 0.35
361 return;
362 end
363
364 [saveFileName, saveFolder] = uiputfile({'*.png', 'PNG Image (*.png)'}, ...
365 'Export average face as PNG', ...
366 fullfile(app.InitialFolder, 'average_face.png'));
367 if isequal(saveFileName, 0)
368 return;
369 end
370
371 outputPath = fullfile(saveFolder, saveFileName);
372 imwrite(app.AveragedImage, outputPath, 'png');
373 app.StatusLabel.Text = sprintf('Saved average face: %s', saveFileName);
374 app.LastResultClickTime = NaN;
375 end
376
377 function showPlaceholderTable(app)
378 app.FaceTable.Data = app.buildEmptyFaceTable();
379 end
380
381 function faceTableData = buildEmptyFaceTable(app)
382 faceTableData = table(string.empty(0, 1), false(0, 1), string.empty(0, 1), ...
383 'VariableNames', {'Face', 'Include', 'Bounds'});
384 end
385
386 function imageData = readImageFile(app, fullFileName)
387 imageData = imread(fullFileName);
388
389 imageData = app.normalizeImageChannels(imageData);
390 end
391
392 function imageData = normalizeImageChannels(~, imageData)
393 % Keep downstream processing consistent by always working in RGB.
394 if ismatrix(imageData)
395 imageData = repmat(imageData, 1, 1, 3);
396 return;
397 end
398
399 if ndims(imageData) == 3 && size(imageData, 3) == 1
400 imageData = repmat(imageData, 1, 1, 3);
401 return;
402 end
403
404 if ndims(imageData) == 3 && size(imageData, 3) >= 3
405 imageData = imageData(:, :, 1:3);
406 return;
407 end
408
409 error('SimpleFaceAverageApp:UnsupportedImageShape', ...
410 'Unsupported image shape %s.', mat2str(size(imageData)));
411 end
412
413 function refreshFaceTable(app)
414 if isempty(app.FaceBoxes)
415 app.FaceTable.Data = app.buildEmptyFaceTable();
416 return;
417 end
418
419 faceCount = size(app.FaceBoxes, 1);
420 if numel(app.FaceIncluded) ~= faceCount
421 app.FaceIncluded = true(faceCount, 1);
422 end
423
424 faceNames = strings(faceCount, 1);
425 faceNames(:) = compose("Face %d", (1:faceCount)');
426 boundsText = strings(faceCount, 1);
427 for k = 1:faceCount
428 boundsText(k) = sprintf('[%d %d %d %d]', ...
429 round(app.FaceBoxes(k, 1)), ...
430 round(app.FaceBoxes(k, 2)), ...
431 round(app.FaceBoxes(k, 3)), ...
432 round(app.FaceBoxes(k, 4)));
433 end
434
435 app.FaceTable.Data = table(faceNames, app.FaceIncluded, boundsText, ...
436 'VariableNames', {'Face', 'Include', 'Bounds'});
437 end
438
439 function onFaceTableEdited(app, event)
440 if isempty(app.FaceBoxes)
441 return;
442 end
443
444 rowIndex = event.Indices(1);
445 if rowIndex < 1 || rowIndex > numel(app.FaceIncluded)
446 return;
447 end
448
449 newValue = event.NewData;
450 if islogical(newValue) || isnumeric(newValue)
451 app.FaceIncluded(rowIndex) = logical(newValue);
452 app.renderDetectedFaces();
453 includedFaces = find(app.FaceIncluded);
454 app.updateAverageFacePreview(includedFaces);
455 app.StatusLabel.Text = sprintf('Face %d include set to %s.', rowIndex, string(app.FaceIncluded(rowIndex)));
456 end
457 end
458 end
459end
文章标签
|-->mean |-->average |-->aggregation |-->fifa |-->average_faces |-->world cup |-->argentina |-->france |-->spanish |-->england
- 本站总访问量:loading次
- 本站总访客数:loading人
- 可通过邮件联系作者:Email大福
- 也可以访问技术博客:大福是小强
- 也可以在知乎搞抽象:知乎-大福
- Comments, requests, and/or opinions go to: Github Repository