UIButton按钮很小时,想增大点击区域

本文介绍了一种通过重写UIButton的方法来调整按钮点击区域的技术。通过对pointInside:withEvent:方法的重写,可以有效地扩大或缩小按钮的点击范围,从而改善用户体验。文中详细解释了CGRectInset函数的作用及参数设置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解决方法: 重写btn的 
	-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event; 方法
	-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    		//当前btn大小
    		CGRect btnBounds = self.bounds;
    		//扩大点击区域,想缩小就将-10设为正值
    		btnBounds = CGRectInset(btnBounds, -10, -10);
    
    		//若点击的点在新的bounds里,就返回YES
    		return CGRectContainsPoint(btnBounds, point);
	}
	对CGRectInset的解释
CGRectInset(CGRect rect, CGFloat dx, CGFloat dy)作用是将rect坐标按照(dx,dy)进行平移,对size进行 如下变换
新宽度 = 原宽度 - 2*dx;新高度 = 原高度 - 2*dy
即dx,dy为正,则为缩小点击范围;dx,dy为负的话,则为扩大范围
我的matlab版本是R2023a的,现在有一段代码是基于光流场的汽车运动检测:classdef CarMotionDetectorApp < matlab.apps.AppBase % 主应用程序类 - 基于光流场的汽车运动检测系统 properties (Access = public) % UI组件 UIFigure matlab.ui.Figure UIAxes matlab.ui.control.UIAxes LoadVideoButton matlab.ui.control.Button ProcessButton matlab.ui.control.Button ThresholdSlider matlab.ui.control.Slider ThresholdLabel matlab.ui.control.Label StatusLabel matlab.ui.control.Label FrameRateEdit matlab.ui.control.NumericEditField FrameRateLabel matlab.ui.control.Label SaveResultsButton matlab.ui.control.Button % 数据处理相关 VideoReader % 视频读取对象 VideoPath char = '' % 视频路径 FlowObj % 光流对象(修复:移除初始类型定义) FlowThreshold double = 0.2 % 光流幅值阈值 IsProcessing logical = false % 处理状态标志 OutputVideoWriter % 输出视频写入对象 end methods (Access = private) % 加载视频按钮回调 function onLoadVideoButtonPushed(app, ~) [file, path] = uigetfile({'*.mp4;*.avi;*.mov','视频文件 (*.mp4, *.avi, *.mov)'}, '选择视频文件'); if isequal(file,0) return; % 用户取消选择 end app.VideoPath = fullfile(path, file); try app.VideoReader = VideoReader(app.VideoPath); % 显示第一帧 if hasFrame(app.VideoReader) frame = readFrame(app.VideoReader); imshow(frame, 'Parent', app.UIAxes); title(app.UIAxes, '视频第一帧'); app.StatusLabel.Text = '视频加载完成,点击"开始处理"'; % 重置视频读取器 app.VideoReader = VideoReader(app.VideoPath); end % 初始化光流对象(关键修正) app.FlowObj = opticalFlowFarneback; app.FlowObj.PyramidScale = 0.5; app.FlowObj.NumPyramidLevels = 3; app.FlowObj.FilterSize = 15; app.FlowObj.NumIterations = 3; app.FlowObj.NeighborhoodSize = ceil(1.2 * 4); catch ME uialert(app.UIFigure, sprintf('视频加载失败: %s', ME.message), '错误'); end end % 处理按钮回调 function onProcessButtonPushed(app, ~) if isempty(app.VideoReader) || isempty(app.VideoPath) uialert(app.UIFigure, '请先加载视频文件', '未选择视频'); return; end % 切换处理状态 if ~app.IsProcessing app.IsProcessing = true; app.ProcessButton.Text = '停止处理'; app.LoadVideoButton.Enable = 'off'; app.SaveResultsButton.Enable = 'off'; processVideo(app); else app.IsProcessing = false; app.ProcessButton.Text = '开始处理'; app.LoadVideoButton.Enable = 'on'; app.SaveResultsButton.Enable = 'on'; app.StatusLabel.Text = '处理已停止'; end end % 保存结果按钮回调 function onSaveResultsButtonPushed(app, ~) if isempty(app.VideoReader) || isempty(app.VideoPath) uialert(app.UIFigure, '请先加载并处理视频', '无处理结果'); return; end [file, path] = uiputfile({'*.avi','AVI 视频文件 (*.avi)'}, '保存结果视频'); if isequal(file,0) return; % 用户取消保存 end outputPath = fullfile(path, file); try % 创建输出视频写入器 app.OutputVideoWriter = VideoWriter(outputPath, 'Motion JPEG AVI'); app.OutputVideoWriter.FrameRate = app.VideoReader.FrameRate; open(app.OutputVideoWriter); % 重置视频读取器和光流对象 app.VideoReader = VideoReader(app.VideoPath); reset(app.FlowObj); % 重置光流状态 app.IsProcessing = true; app.ProcessButton.Enable = 'off'; app.LoadVideoButton.Enable = 'off'; app.SaveResultsButton.Enable = 'off'; app.StatusLabel.Text = '正在保存结果...'; % 处理第一帧初始化 if hasFrame(app.VideoReader) frame = readFrame(app.VideoReader); currentGray = app.convertToGray(frame); estimateFlow(app.FlowObj, currentGray); % 初始化光流 end frameCount = 0; % 处理并保存每一帧 while hasFrame(app.VideoReader) && app.IsProcessing frame = readFrame(app.VideoReader); frameCount = frameCount + 1; currentGray = app.convertToGray(frame); % 计算光流(使用预初始化的对象) flowVectors = estimateFlow(app.FlowObj, currentGray); Vx = flowVectors.Vx; Vy = flowVectors.Vy; magnitude = sqrt(Vx.^2 + Vy.^2); motionMask = magnitude > app.FlowThreshold; % 可视化 imshow(frame, 'Parent', app.UIAxes); hold(app.UIAxes, 'on'); % 绘制运动区域(红色半透明) redMask = cat(3, ones(size(motionMask)), zeros(size(motionMask)), zeros(size(motionMask))); h = imshow(redMask, 'Parent', app.UIAxes); set(h, 'AlphaData', 0.3 * motionMask); % 绘制光流向量(采样绘制) [h, w] = size(magnitude); [X, Y] = meshgrid(1:10:w, 1:10:h); U = Vx(1:10:h, 1:10:w); V = Vy(1:10:h, 1:10:w); quiver(app.UIAxes, X(:), Y(:), U(:), V(:), 2, 'Color', 'g', 'LineWidth', 1); hold(app.UIAxes, 'off'); title(app.UIAxes, '汽车运动检测(红色区域为运动区域)'); % 获取当前帧图像并写入视频 frameWithOverlay = getframe(app.UIAxes); writeVideo(app.OutputVideoWriter, frameWithOverlay.cdata); % 每10帧释放内存(性能优化) if mod(frameCount, 10) == 0 drawnow; clear frameWithOverlay redMask motionMask magnitude Vx Vy; end app.StatusLabel.Text = sprintf('保存中... 帧: %d', frameCount); drawnow limitrate; % 优化刷新性能 end close(app.OutputVideoWriter); app.IsProcessing = false; app.ProcessButton.Enable = 'on'; app.LoadVideoButton.Enable = 'on'; app.SaveResultsButton.Enable = 'on'; app.StatusLabel.Text = sprintf('结果已保存至: %s', outputPath); catch ME uialert(app.UIFigure, sprintf('保存失败: %s', ME.message), '错误'); app.IsProcessing = false; app.ProcessButton.Enable = 'on'; app.LoadVideoButton.Enable = 'on'; app.SaveResultsButton.Enable = 'on'; if ~isempty(app.OutputVideoWriter) && isvalid(app.OutputVideoWriter) close(app.OutputVideoWriter); end end end % 视频处理主函数 function processVideo(app) % 重置视频读取器和光流对象 app.VideoReader = VideoReader(app.VideoPath); reset(app.FlowObj); % 重置光流状态 % 获取帧率控制值 frameRate = app.FrameRateEdit.Value; if frameRate <= 0 frameRate = app.VideoReader.FrameRate; end pauseTime = 1/frameRate; % 处理第一帧初始化 if hasFrame(app.VideoReader) frame = readFrame(app.VideoReader); currentGray = app.convertToGray(frame); estimateFlow(app.FlowObj, currentGray); % 初始化光流 imshow(frame, 'Parent', app.UIAxes); title(app.UIAxes, '视频第一帧(已初始化)'); end frameCount = 0; % 循环处理每一帧 while hasFrame(app.VideoReader) && app.IsProcessing frameCount = frameCount + 1; % 读取当前帧 frame = readFrame(app.VideoReader); % 转换为灰度图像(版本兼容) currentGray = app.convertToGray(frame); % 计算光流(使用预初始化的对象) flowVectors = estimateFlow(app.FlowObj, currentGray); % 获取光流向量 Vx = flowVectors.Vx; Vy = flowVectors.Vy; magnitude = sqrt(Vx.^2 + Vy.^2); % 光流幅值 % 检测运动区域(超过阈值的区域) motionMask = magnitude > app.FlowThreshold; % 可视化 imshow(frame, 'Parent', app.UIAxes); hold(app.UIAxes, 'on'); % 绘制运动区域(红色半透明) redMask = cat(3, ones(size(motionMask)), zeros(size(motionMask)), zeros(size(motionMask))); h = imshow(redMask, 'Parent', app.UIAxes); set(h, 'AlphaData', 0.3 * motionMask); % 绘制光流向量(采样绘制) [h, w] = size(magnitude); [X, Y] = meshgrid(1:10:w, 1:10:h); U = Vx(1:10:h, 1:10:w); V = Vy(1:10:h, 1:10:w); quiver(app.UIAxes, X(:), Y(:), U(:), V(:), 2, 'Color', 'g', 'LineWidth', 1); hold(app.UIAxes, 'off'); title(app.UIAxes, '汽车运动检测(红色区域为运动区域)'); % 更新状态 app.StatusLabel.Text = sprintf('处理中... 当前帧: %d', frameCount); % 每10帧释放内存(性能优化) if mod(frameCount, 10) == 0 drawnow; clear redMask motionMask magnitude Vx Vy; end % 控制处理速度 pause(pauseTime); end % 处理完成或停止 if app.IsProcessing app.IsProcessing = false; app.ProcessButton.Text = '开始处理'; app.LoadVideoButton.Enable = 'on'; app.SaveResultsButton.Enable = 'on'; app.StatusLabel.Text = '处理完成!'; end end % 灰度转换(版本兼容) function gray = convertToGray(~, frame) % 检查MATLAB版本 if verLessThan('matlab', '9.8') % R2020a之前版本 gray = rgb2gray(frame); else gray = im2gray(frame); end end % 阈值滑动条回调 function onThresholdSliderValueChanged(app, ~) app.FlowThreshold = app.ThresholdSlider.Value; app.ThresholdLabel.Text = sprintf('运动阈值: %.2f', app.FlowThreshold); end % 帧率编辑框回调 function onFrameRateEditValueChanged(app, ~) frameRate = app.FrameRateEdit.Value; if frameRate <= 0 app.FrameRateEdit.Value = app.VideoReader.FrameRate; end end end methods (Access = private) % 创建UI组件 function createComponents(app) % 创建主窗口 app.UIFigure = uifigure('Name', '基于光流场的汽车运动检测系统', ... 'Position', [100 100 900 650]); % 创建坐标轴 app.UIAxes = uiaxes(app.UIFigure); app.UIAxes.Position = [50, 180, 800, 450]; app.UIAxes.XTick = []; app.UIAxes.YTick = []; title(app.UIAxes, '视频显示'); % 创建加载视频按钮 app.LoadVideoButton = uibutton(app.UIFigure, 'push', ... 'Position', [50, 130, 100, 30], ... 'Text', '加载视频', ... 'ButtonPushedFcn', @(src, event) onLoadVideoButtonPushed(app, event)); % 创建处理按钮 app.ProcessButton = uibutton(app.UIFigure, 'push', ... 'Position', [170, 130, 100, 30], ... 'Text', '开始处理', ... 'ButtonPushedFcn', @(src, event) onProcessButtonPushed(app, event)); % 创建保存结果按钮 app.SaveResultsButton = uibutton(app.UIFigure, 'push', ... 'Position', [290, 130, 100, 30], ... 'Text', '保存结果', ... 'Enable', 'off', ... 'ButtonPushedFcn', @(src, event) onSaveResultsButtonPushed(app, event)); % 创建阈值滑动条 app.ThresholdSlider = uislider(app.UIFigure, ... 'Position', [420, 150, 200, 3], ... 'Limits', [0.05, 1], ... 'Value', app.FlowThreshold, ... 'ValueChangedFcn', @(src, event) onThresholdSliderValueChanged(app, event)); % 创建阈值标签 app.ThresholdLabel = uilabel(app.UIFigure, ... 'Position', [420, 120, 200, 22], ... 'Text', sprintf('运动阈值: %.2f', app.FlowThreshold)); % 创建帧率标签 app.FrameRateLabel = uilabel(app.UIFigure, ... 'Position', [650, 120, 80, 22], ... 'Text', '处理帧率:'); % 创建帧率编辑框 app.FrameRateEdit = uieditfield(app.UIFigure, 'numeric', ... 'Position', [730, 120, 60, 22], ... 'Value', 10, ... 'Limits', [1, 60], ... 'ValueChangedFcn', @(src, event) onFrameRateEditValueChanged(app, event)); % 创建状态标签 app.StatusLabel = uilabel(app.UIFigure, ... 'Position', [50, 80, 800, 22], ... 'Text', '请加载视频文件'); end end methods (Access = public) % 构造函数 function app = CarMotionDetectorApp % 创建UI组件 createComponents(app); % 初始化光流对象(避免空数组错误) app.FlowObj = []; % 初始化为空,稍后实例化 end end end 它能实现功能但是在处理的候道路也会有红色区域和绿色箭头,可以优化一下这个代码吗
最新发布
07-04
不是你没有结合之前的对话吗是你的这段代码:classdef OpticalFlowVehicleDetection < matlab.apps.AppBase properties (Access = public) % GUI组件 UIFigure matlab.ui.Figure LoadButton matlab.ui.control.Button StartButton matlab.ui.control.Button VideoAxes matlab.ui.control.UIAxes StatusLabel matlab.ui.control.Label % 视频处理组件 VideoReader = [] % 初始化为空数组 VideoPath char Timer timer IsProcessing logical = false % 光流处理参数 PrevGray % 前一帧灰度图像 Trajectories struct % 车辆轨迹数据 NextId uint32 = 1 % 下一个轨迹ID Scale double = 0.5 % 图像缩放因子 end methods (Access = private) function createComponents(app) % 创建主窗口 app.UIFigure = uifigure('Name', '光学流车辆检测系统', ... 'Position', [100 100 900 600], ... 'CloseRequestFcn', @(src,event) closeApp(app)); % 创建视频显示区域 app.VideoAxes = uiaxes(app.UIFigure, ... 'Position', [50, 150, 800, 400], ... 'XTick', [], 'YTick', []); title(app.VideoAxes, '视频预览'); axis(app.VideoAxes, 'image'); % 创建加载按钮 app.LoadButton = uibutton(app.UIFigure, 'push', ... 'Position', [50, 50, 150, 40], ... 'Text', '加载视频', ... 'FontSize', 12, ... 'ButtonPushedFcn', createCallbackFcn(app, @loadVideoCallback, true)); % 创建开始/停止按钮 app.StartButton = uibutton(app.UIFigure, 'push', ... 'Position', [220, 50, 150, 40], ... 'Text', '开始检测', ... 'FontSize', 12, ... 'Enable', 'off', ... 'ButtonPushedFcn', createCallbackFcn(app, @startStopCallback, true)); % 创建状态标签 app.StatusLabel = uilabel(app.UIFigure, ... 'Position', [400, 50, 400, 40], ... 'Text', '就绪: 请加载视频文件', ... 'FontSize', 12, ... 'FontColor', [0.3, 0.3, 0.9]); end function loadVideoCallback(app, ~) % 选择视频文件 [filename, pathname] = uigetfile(... {'*.mp4;*.avi;*.mov;*.m4v', '视频文件'}, ... '选择视频文件'); if isequal(filename, 0) return; % 用户取消选择 end app.VideoPath = fullfile(pathname, filename); % 验证文件存在性 if ~exist(app.VideoPath, 'file') errordlg('文件不存在或路径无效', '文件错误'); return; end try % 初始化VideoReader对象 app.VideoReader = VideoReader(app.VideoPath); % 验证视频属性 if app.VideoReader.NumFrames == 0 error('视频帧数为零,无效视频文件'); end % 读取并显示第一帧 frame = read(app.VideoReader, 1); imshow(frame, 'Parent', app.VideoAxes); title(app.VideoAxes, sprintf('已加载: %s (%dx%d)', ... filename, ... app.VideoReader.Width, ... app.VideoReader.Height)); % 重置处理状态 app.IsProcessing = false; app.StartButton.Enable = 'on'; app.StartButton.Text = '开始检测'; app.StatusLabel.Text = '就绪: 点击"开始检测"按钮启动'; catch ME errordlg(sprintf('视频加载错误: %s', ME.message), '读取错误'); end end function startStopCallback(app, ~) % 开始/停止处理切换 if app.IsProcessing % 停止处理 stop(app.Timer); delete(app.Timer); app.IsProcessing = false; app.StartButton.Text = '开始检测'; app.StatusLabel.Text = '已停止: 点击"开始检测"按钮继续'; else % 开始处理 app.IsProcessing = true; app.StartButton.Text = '停止检测'; % 初始化光流处理状态 app.PrevGray = []; app.Trajectories = struct('id', {}, 'points', {}, 'color', {}); app.NextId = 1; % 创建定器处理视频帧 app.Timer = timer(... 'ExecutionMode', 'fixedRate', ... 'Period', 0.05, ... % 约20fps 'TimerFcn', @(src,event) processFrame(app), ... 'StopFcn', @(src,event) timerCleanup(app)); app.StatusLabel.Text = '处理中: 检测车辆并跟踪轨迹...'; start(app.Timer); end end function processFrame(app) % 处理当前视频帧 if ~hasFrame(app.VideoReader) stop(app.Timer); return; end % 读取当前帧 frame = readFrame(app.VideoReader); % 转换为灰度并缩放 currentGray = rgb2gray(frame); if app.Scale ~= 1.0 currentGray = imresize(currentGray, app.Scale); end % 初始化前一帧(如果是第一帧) if isempty(app.PrevGray) app.PrevGray = currentGray; return; end % 计算光流(Farneback方法) flow = opticalFlowFarneback(... app.PrevGray, currentGray, ... 'PyramidScale', 0.5, ... 'NumLevels', 3, ... 'WindowSize', 15, ... 'NumIterations', 3, ... 'PolyN', 5, ... 'PolySigma', 1.2); % 检测车辆并更新轨迹 [frameOut, newTrajectories] = detectVehicles(app, frame, flow); % 显示处理后的帧 imshow(frameOut, 'Parent', app.VideoAxes); % 更新前一帧 app.PrevGray = currentGray; app.Trajectories = newTrajectories; end function [frameOut, trajectories] = detectVehicles(app, frame, flow) % 车辆检测和跟踪逻辑 frameOut = frame; trajectories = app.Trajectories; % 计算光流幅度 magnitude = sqrt(flow.Vx.^2 + flow.Vy.^2); % 阈值处理获取运动区域 motionMask = magnitude > 2.0; % 运动阈值 % 形态学操作优化掩膜 motionMask = imopen(motionMask, strel('disk', 3)); motionMask = imclose(motionMask, strel('disk', 10)); % 检测连通区域 stats = regionprops(motionMask, 'BoundingBox', 'Centroid'); % 过滤小区域 minArea = 500 * (app.Scale^2); % 最小面积阈值 validBoxes = []; for k = 1:length(stats) bbox = stats(k).BoundingBox; area = bbox(3) * bbox(4); if area > minArea && bbox(3)/bbox(4) < 3.5 validBoxes = [validBoxes; stats(k)]; end end % 更新或创建轨迹 currentCentroids = reshape([validBoxes.Centroid], 2, [])'; currentCentroids(:,1) = currentCentroids(:,1) / app.Scale; currentCentroids(:,2) = currentCentroids(:,2) / app.Scale; % 初始化匹配状态 matched = false(1, length(validBoxes)); for i = 1:length(trajectories) trajectories(i).matched = false; end % 匹配现有轨迹 for j = 1:size(currentCentroids, 1) minDist = Inf; matchIdx = 0; for i = 1:length(trajectories) if trajectories(i).matched continue; end lastPoint = trajectories(i).points(end,:); dist = norm(currentCentroids(j,:) - lastPoint); % 匹配阈值(欧氏距离) if dist < 30 && dist < minDist minDist = dist; matchIdx = i; end end if matchIdx > 0 % 更新现有轨迹 trajectories(matchIdx).points(end+1,:) = currentCentroids(j,:); trajectories(matchIdx).matched = true; matched(j) = true; else % 创建新轨迹 newId = app.NextId; trajectories(end+1) = struct(... 'id', newId, ... 'points', currentCentroids(j,:), ... 'color', rand(1,3), ... % 随机颜色 'matched', true); app.NextId = newId + 1; end end % 移除未匹配的轨迹 trajectories = trajectories([trajectories.matched]); % 在帧上绘制结果 for k = 1:length(validBoxes) bbox = validBoxes(k).BoundingBox / app.Scale; centroid = validBoxes(k).Centroid / app.Scale; % 绘制边界框 frameOut = insertShape(frameOut, 'Rectangle', bbox, ... 'Color', 'green', 'LineWidth', 2); % 绘制质心 frameOut = insertMarker(frameOut, centroid, ... 'o', 'Color', 'red', 'Size', 8); end % 绘制轨迹 for t = 1:length(trajectories) if size(trajectories(t).points, 1) > 1 % 转换为原始分辨率 points = trajectories(t).points * app.Scale; % 绘制轨迹线 for p = 2:size(points, 1) frameOut = insertShape(frameOut, 'Line', ... [points(p-1,1), points(p-1,2), points(p,1), points(p,2)], ... 'Color', trajectories(t).color, 'LineWidth', 2); end % 绘制轨迹ID frameOut = insertText(frameOut, points(end,:), ... sprintf('ID:%d', trajectories(t).id), ... 'FontSize', 16, 'BoxColor', trajectories(t).color); end end % 显示帧信息 frameOut = insertText(frameOut, [10, 10], ... sprintf('帧: %d | 车辆: %d', app.VideoReader.CurrentTime * app.VideoReader.FrameRate, length(validBoxes)), ... 'FontSize', 18, 'BoxColor', 'black', 'TextColor', 'white'); end function timerCleanup(app) % 定器停止的清理 app.IsProcessing = false; app.StartButton.Text = '开始检测'; app.StatusLabel.Text = '处理完成: 点击"开始检测"重新启动'; end function closeApp(app) % 关闭应用程序的清理 if app.IsProcessing stop(app.Timer); delete(app.Timer); end delete(app.UIFigure); end end methods (Access = public) function app = OpticalFlowVehicleDetection % 构造函数 createComponents(app); % 初始化属性 app.VideoReader = []; app.VideoPath = ''; if nargout == 0 clear app end end function delete(app) % 析构函数 if app.IsProcessing && isvalid(app.Timer) stop(app.Timer); delete(app.Timer); end if ~isempty(app.VideoReader) delete(app.VideoReader); end if isvalid(app.UIFigure) delete(app.UIFigure); end end end end 出此案了问题,加载视频没问题但是处理视频的候命令行写了个:计算计器 'timer-2' 的 TimerFcn 出错 需要字符串标量或字符向量形式的参数名称。 >> 我需要你对这个代码进行修改让它能使用并且能够运行的完整代码
07-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值