0
function multiObjectTracking()

% 创建用于读取视频、检测移动对象、% 并显示结果的系统对象

obj = setupSystemObjects();

tracks = initializeTracks(); % create an empty array of tracks

nextId = 1; % ID of the next track

% 检测移动物体,并在视频帧中跟踪它们

while ~isDone(obj.reader)
    frame = readFrame();
    [centroids, bboxes, mask] = detectObjects(frame);
    predictNewLocationsOfTracks();
    [assignments, unassignedTracks, unassignedDetections] = ...
        detectionToTrackAssignment();

    updateAssignedTracks();
    updateUnassignedTracks();
    deleteLostTracks();
    createNewTracks();

    displayTrackingResults();
end

%% 创建系统对象 % 创建用于读取视频帧、检测 % 前景对象和显示结果的系统对象。

    function obj = setupSystemObjects()

% 初始化视频 I/O % 创建用于从文件中读取视频的对象,在每一帧中绘制跟踪的 % 对象,并播放视频。

        vid = videoinput('winvideo', 1, 'YUY2_320x240');
        src = getselectedsource(vid);

        vid.FramesPerTrigger = 1;

% TriggerRepeat 从零开始,并且始终比触发器数量少 1%。

        vid.TriggerRepeat = 899;

        preview(vid);

        start(vid);

        stoppreview(vid);

        savedvideo = getdata(vid);

% 创建一个视频文件阅读器

        obj.reader = vision.VideoFileReader(savedvideo);

% 创建两个视频播放器,一个显示视频,一个显示前景遮罩

        obj.videoPlayer = vision.VideoPlayer('Position', [20, 400, 700, 400]);
        obj.maskPlayer = vision.VideoPlayer('Position', [740, 400, 700, 400]);

        obj.detector = vision.ForegroundDetector('NumGaussians', 3, ...
            'NumTrainingFrames', 40, 'MinimumBackgroundRatio', 0.7);

        obj.blobAnalyser = vision.BlobAnalysis('BoundingBoxOutputPort', true, ...
            'AreaOutputPort', true, 'CentroidOutputPort', true, ...
            'MinimumBlobArea', 400);
    end

    function tracks = initializeTracks()

% 创建一个空的轨道数组

        tracks = struct(...
            'id', {}, ...
            'bbox', {}, ...
            'kalmanFilter', {}, ...
            'age', {}, ...
            'totalVisibleCount', {}, ...
            'consecutiveInvisibleCount', {});
    end

%% 读取一个视频帧 % 从视频文件中读取下一个视频帧。

    function frame = readFrame()
        frame = obj.reader.step();
    end


    function [centroids, bboxes, mask] = detectObjects(frame)

% 检测前景

        mask = obj.detector.step(frame);

% 应用形态学运算来去除噪声并填充孔洞

        mask = imopen(mask, strel('rectangle', [3,3]));
        mask = imclose(mask, strel('rectangle', [15, 15])); 
        mask = imfill(mask, 'holes');

% 执行 blob 分析以查找连接的组件

        [~, centroids, bboxes] = obj.blobAnalyser.step(mask);
    end

%% 预测现有轨道的新位置 % 使用卡尔曼滤波器预测当前帧中每个轨道的质心,并相应地更新其边界框。

    function predictNewLocationsOfTracks()
        for i = 1:length(tracks)
            bbox = tracks(i).bbox;

% 预测轨道的当前位置

            predictedCentroid = predict(tracks(i).kalmanFilter);

% 移动边​​界框,使其中心位于 % 预测位置

            predictedCentroid = int32(predictedCentroid) - bbox(3:4) / 2;
            tracks(i).bbox = [predictedCentroid, bbox(3:4)];
        end
    end


    function [assignments, unassignedTracks, unassignedDetections] = ...
            detectionToTrackAssignment()

        nTracks = length(tracks);
        nDetections = size(centroids, 1);

% 计算将每个检测分配给每个轨道的成本

        cost = zeros(nTracks, nDetections);
        for i = 1:nTracks
            cost(i, :) = distance(tracks(i).kalmanFilter, centroids);
        end

% 解决分配问题

        costOfNonAssignment = 20;
        [assignments, unassignedTracks, unassignedDetections] = ...
            assignDetectionsToTracks(cost, costOfNonAssignment);
    end

    function updateAssignedTracks()
        numAssignedTracks = size(assignments, 1);
        for i = 1:numAssignedTracks
            trackIdx = assignments(i, 1);
            detectionIdx = assignments(i, 2);
            centroid = centroids(detectionIdx, :);
            bbox = bboxes(detectionIdx, :);

% 使用新的检测纠正对物体位置的估计

            correct(tracks(trackIdx).kalmanFilter, centroid);

% 用检测到的 % 边界框替换预测的边界框

            tracks(trackIdx).bbox = bbox;

% 更新曲目的年龄

            tracks(trackIdx).age = tracks(trackIdx).age + 1;

% 更新可见性

            tracks(trackIdx).totalVisibleCount = ...
                tracks(trackIdx).totalVisibleCount + 1;
            tracks(trackIdx).consecutiveInvisibleCount = 0;
        end
    end

%% 更新未分配的曲目 % 将每个未分配的曲目标记为不可见,并将其年龄增加 1。

    function updateUnassignedTracks()
        for i = 1:length(unassignedTracks)
            ind = unassignedTracks(i);
            tracks(ind).age = tracks(ind).age + 1;
            tracks(ind).consecutiveInvisibleCount = ...
                tracks(ind).consecutiveInvisibleCount + 1;
        end
    end


    function deleteLostTracks()
        if isempty(tracks)
            return;
        end

        invisibleForTooLong = 10;
        ageThreshold = 8;

% 计算它可见的轨道年龄的比例

        ages = [tracks(:).age];
        totalVisibleCounts = [tracks(:).totalVisibleCount];
        visibility = totalVisibleCounts ./ ages;

% 找到“丢失”曲目的索引

        lostInds = (ages < ageThreshold & visibility < 0.6) | ...
            [tracks(:).consecutiveInvisibleCount] >= invisibleForTooLong;

% 删除丢失的曲目

        tracks = tracks(~lostInds);
    end

    function createNewTracks()
        centroids = centroids(unassignedDetections, :);
        bboxes = bboxes(unassignedDetections, :);

        for i = 1:size(centroids, 1)

            centroid = centroids(i,:);
            bbox = bboxes(i, :);

% 创建卡尔曼滤波器对象

            kalmanFilter = configureKalmanFilter('ConstantVelocity', ...
                centroid, [200, 50], [100, 25], 100);

% 创建一个新轨道

            newTrack = struct(...
                'id', nextId, ...
                'bbox', bbox, ...
                'kalmanFilter', kalmanFilter, ...
                'age', 1, ...
                'totalVisibleCount', 1, ...
                'consecutiveInvisibleCount', 0);

% 将其添加到曲目数组中

            tracks(end + 1) = newTrack;

% 增加下一个 id

            nextId = nextId + 1;
        end
    end

    function displayTrackingResults()

% 将帧和掩码转换为 uint8 RGB

        frame = im2uint8(frame);
        mask = uint8(repmat(mask, [1, 1, 3])) .* 255;

        minVisibleCount = 8;
        if ~isempty(tracks)

% 噪声检测往往会导致短暂的轨道 % 仅显示已可见超过 % 最小帧数的轨道。

            reliableTrackInds = ...
                [tracks(:).totalVisibleCount] > minVisibleCount;
            reliableTracks = tracks(reliableTrackInds);

% 显示对象。如果在此帧中未检测到对象,则显示其预测的边界框。

            if ~isempty(reliableTracks)

% 获取边界框

                bboxes = cat(1, reliableTracks.bbox);

% 获取 ID

                ids = int32([reliableTracks(:).id]);

% 为对象创建标签,指示我们显示预测而不是实际 % 位置的对象

                labels = cellstr(int2str(ids'));
                predictedTrackInds = ...
                    [reliableTracks(:).consecutiveInvisibleCount] > 0;
                isPredicted = cell(size(labels));
                isPredicted(predictedTrackInds) = {' predicted'};
                labels = strcat(labels, isPredicted);

% 在框架上绘制

                frame = insertObjectAnnotation(frame, 'rectangle', ...
                    bboxes, labels);

% 画在面具上

                mask = insertObjectAnnotation(mask, 'rectangle', ...
                    bboxes, labels);
            end
        end

% 显示蒙版和边框

        obj.maskPlayer.step(mask);        
        obj.videoPlayer.step(frame);
    end

displayEndOfDemoMessage(mfilename)
end
4

1 回答 1

0

Your problem is that you are trying to use vision.VideoFileReader, while trying to read frames from a camera. vision.VideoFileReader is only for reading video files. If you are getting frames from the camera you do not need it at all. You should add your videoinput object to the obj struct, and you should try using getsnapshot inside readFrame().

于 2014-04-03T17:28:50.993 回答