gusucode.com > vision工具箱matlab源码程序 > vision/+vision/+internal/+ocr/+tool/BoxEditDisplay.m
classdef BoxEditDisplay < vision.internal.uitools.AppFigure properties KeyPressFcn MouseButtonDownFcn BoundingBoxButtonDownFcn NewPositionCallbackFcn Tag = 'BoxEditDisplay' end %---------------------------------------------------------------------- % UI components %---------------------------------------------------------------------- properties Axes ScrollPanel ScrollAPI ROI end %---------------------------------------------------------------------- % State managed by BoxEditDisplay %---------------------------------------------------------------------- properties % Boxes - Current set of boxes show in image Boxes % BoxIDs - unique ID for each box. BoxIDs % Text - Cell array of characters associated with each box. This is % updated as boxes are modified. Text % LastBoxID - The last box ID used. New box ID is LastBoxID + 1. LastBoxID % IsChanged - set to true if any boxes are modified. IsChanged % Box color and line width settings BoxColor = uint8([66 161 230]) BoxLineWidth = 3; end %---------------------------------------------------------------------- methods % Construct main image display. Requires key press and button down % callbacks to be provided. function this = BoxEditDisplay() this = this@vision.internal.uitools.AppFigure('Box Edit'); end %------------------------------------------------------------------ % Draws image and boxes that make up the image display. %------------------------------------------------------------------ function draw(this, I, bboxes, selectedBox) bbox = bboxes(selectedBox,:); drawImage(this, I, bbox); drawBoxes(this, bboxes, selectedBox); end %------------------------------------------------------------------ function drawImage(this, I, bbox) makeHandleVisible(this); this.Axes = findobj(this.Fig, 'Type','axes'); if isempty(this.Axes) || ~ishandle(this.Axes) % add an axes if needed this.Axes = axes('Parent', this.Fig,... 'Tag', this.Tag,... 'Units','normalized','Position',[0 0 1 0.88],'Visible','off'); hImage = imshow(I,'InitialMagnification', 'fit',... 'Parent', this.Axes, 'Border', 'tight'); this.ScrollPanel = imscrollpanel(this.Fig, hImage); this.Axes = hImage.Parent; this.ScrollAPI = iptgetapi(this.ScrollPanel); else this.ScrollAPI.replaceImage(I); %hImage = imshow(I,'InitialMagnification', 'fit',... %'Parent', this.Axes, 'Border', 'tight'); end mag = this.ScrollAPI.findMagnification(4*bbox(3),4*bbox(4)); cx = bbox(1) + bbox(3)/2; cy = bbox(2) + bbox(4)/2; this.ScrollAPI.setMagnificationAndCenter(mag, cx, cy); % attach key board accelerator callbacks iptaddcallback(this.Fig, 'KeyPressFcn', this.KeyPressFcn); % attach callback for drawing ROIs set(hImage, 'buttondownfcn', this.MouseButtonDownFcn); % Install context menu again. Have to do it twice because the % pointer behavior code prevents the context menus from % showing up immediately after images are loaded. %this.installContextMenu('ROI', hImage); % Disable overwriting to put on the ROIs set(this.Axes, 'NextPlot', 'add'); % resets all axes properties to default values set(this.Axes, 'NextPlot', 'replace'); set(this.Axes, 'Tag', this.Tag); % add tag after reset lockFigure(this); end %------------------------------------------------------------------ function setPointerToCross(this) hImage = this.getImage(); enterFcn = @(figHandle, currentPoint)... set(figHandle, 'Pointer', 'cross'); iptSetPointerBehavior(hImage, enterFcn); iptPointerManager(this.Fig); end %------------------------------------------------------------------ function resetPointerBehavior(this) hImage = this.getImage(); iptSetPointerBehavior(hImage, []); iptPointerManager(this.Fig); end % function attachDrawROICallback(this) hImage = findobj(this.ScrollPanel,'type','image'); set(hImage, 'buttondownfcn', this.MouseButtonDownFcn); end %------------------------------------------------------------------ function drawBoxes(this, bboxes, selectedBox) isValid = cellfun(@(x)ischar(x), this.Text); points = bbox2points(bboxes); points = reshape(points,4,[]); X = points(:,1:2:end); Y = points(:,2:2:end); % set patch alpha values to zero to not display patch at that % box. Instead a imrect will be placed there instead. N = size(X,2); % num patches patchAlphas = zeros(N,1); patchAlphas(selectedBox) = 0; edgeAlphas = ones(N,1); edgeAlphas(selectedBox) = 0; patchOptions = {'EdgeColor',this.BoxColor, ... 'EdgeAlpha',1,... 'FaceColor',this.BoxColor,... 'FaceAlpha','flat',... 'AlphaDataMapping','none',... % don't use figure's alphamap 'FaceVertexAlphaData', patchAlphas, ... 'LineWidth',this.BoxLineWidth}; % Create patch object for each box and attach callback. p(N) = matlab.graphics.GraphicsPlaceholder; for i = 1:N if isValid(i) tag = sprintf('bboxPatch%d',i); % add tag so we can findobj easily. p(i) = patch(X(:,i), Y(:,i), this.BoxColor,... 'Parent', this.Axes, patchOptions{:},... 'FaceVertexAlphaData', patchAlphas(i),... 'EdgeAlpha', edgeAlphas(i), ... 'Tag',tag); iptaddcallback(p(i), 'ButtonDownFcn',... {this.BoundingBoxButtonDownFcn, i}); end end this.LastBoxID = N; bbox = bboxes(selectedBox, :); this.addROI(bbox) % add ROI end %------------------------------------------------------------------ function removeBox(this, whichBox) % delete patch for i = 1:numel(whichBox) p = getBoxPatch(this, whichBox(i)); delete(p) end idxToRemove = this.getBoxIDs(whichBox); this.Boxes(idxToRemove,:) = []; this.BoxIDs(idxToRemove) = []; this.Text(idxToRemove) = []; this.IsChanged = true; end %------------------------------------------------------------------ function idx = getBoxIDs(this, whichBox) if isscalar(whichBox) idx = this.BoxIDs == whichBox; else idx = max(bsxfun(@eq, this.BoxIDs, whichBox(:))); end end %------------------------------------------------------------------ function txt = getText(this, whichBox) assert(isscalar(whichBox)); idx = this.BoxIDs == whichBox; txt = this.Text{idx}; end %------------------------------------------------------------------ function boxes = getBoxes(this, whichBox) idx = this.getBoxIDs(whichBox); boxes = this.Boxes(idx,:); end %------------------------------------------------------------------ function resizeBox(this, whichBox, newBox) % update boxes idx = this.getBoxIDs(whichBox); this.Boxes(idx,:) = newBox; % update patch p = getBoxPatch(this, whichBox); p.Vertices = bbox2points(newBox); this.IsChanged = true; end %------------------------------------------------------------------ % Appends box and returns position in Boxes. %------------------------------------------------------------------ function idx = appendBox(this, bbox, showBoxBorder) if nargin == 2 showBoxBorder = true; end if showBoxBorder edgeAlpha = 1; else edgeAlpha = 0; end patchOptions = {'EdgeColor',this.BoxColor, ... 'EdgeAlpha',edgeAlpha,... 'FaceColor',this.BoxColor,... 'FaceAlpha','flat',... 'AlphaDataMapping','none',... % don't use figure's alphamap 'FaceVertexAlphaData', 0,... 'LineWidth',this.BoxLineWidth}; points = bbox2points(bbox); points = reshape(points,4,[]); X = points(:,1:2:end); Y = points(:,2:2:end); this.LastBoxID = this.LastBoxID + 1; tag = sprintf('bboxPatch%d',this.LastBoxID); % add tag so we can findobj easily. p = patch(X, Y, this.BoxColor,... 'Parent', this.Axes, patchOptions{:},... 'FaceVertexAlphaData', 0,... 'EdgeAlpha', edgeAlpha,... 'Tag', tag); iptaddcallback(p, 'ButtonDownFcn',... {this.BoundingBoxButtonDownFcn, this.LastBoxID}); % return position at which added idx = this.LastBoxID; this.Boxes(end+1,:) = bbox; this.BoxIDs(end+1) = idx; this.Text{end+1} = char(0); % for now this.IsChanged = true; end %------------------------------------------------------------------ % Adds an ROI programmatically given a bounding box. %------------------------------------------------------------------ function addROI(this, bbox) hIm = findobj(this.Axes, 'type','Image'); % bbox is in spatial coordinates for drawing this.ROI = iptui.imcropRect(this.Axes, bbox, hIm); this.ROI.addNewPositionCallback(this.NewPositionCallbackFcn); p = findobj(this.ROI, 'type','patch'); p.FaceColor = this.BoxColor; p.FaceAlpha = 0.5; this.removeROIContextMenu(this.ROI); end %------------------------------------------------------------------ function removeROIContextMenu(~, roi) % remove context menus p = findobj(roi, 'Type','patch'); if ishandle(p) delete(p.UIContextMenu.Children); end end %------------------------------------------------------------------ % Draw an ROI interactively. %------------------------------------------------------------------ function roi = drawROI(this) hImage = findobj(this.ScrollPanel,'type','image'); roi = vision.internal.uitools.imrectButtonDown.drawROI(hImage); if vision.internal.uitools.imrectButtonDown.isValidROI(roi) this.ROI = roi; this.ROI.addNewPositionCallback(this.NewPositionCallbackFcn); this.removeROIContextMenu(this.ROI); end drawnow(); % Finish all the drawing before moving on end %------------------------------------------------------------------ % Highlight a box by setting it face alpha to 0.5. %------------------------------------------------------------------ function highLightBox(this, whichBox) for i = 1:numel(whichBox) p = getBoxPatch(this, whichBox(i)); p.EdgeAlpha = 1; p.FaceVertexAlphaData = 0.5; end end %------------------------------------------------------------------ % Un-highlight a box by making its face alpha 0 %------------------------------------------------------------------ function unhighlightBox(this, whichBox) for i = 1:numel(whichBox) boxIdx = whichBox(i); p = getBoxPatch(this, boxIdx); p.EdgeAlpha = 1; p.FaceVertexAlphaData = 0; end end %------------------------------------------------------------------ % Select a single box by removing the batch border and adding and % ROI on top. This box can now be resized. %------------------------------------------------------------------ function selectBox(this, whichBox) if numel(whichBox) == 1 p = getBoxPatch(this, whichBox); p.EdgeAlpha = 0; p.FaceVertexAlphaData = 0; % convert vertex points to box xy = p.Vertices; xymax = max(xy); xymin = min(xy); bbox = [xymin(1) xymin(2) xymax(1)-xymin(1) xymax(2)-xymin(2)]; this.addROI(bbox); end end %------------------------------------------------------------------ % Unselect a single box by removing the ROI and adding the patch % border. %------------------------------------------------------------------ function unselectBox(this, whichBox) if numel(whichBox) == 1 p = getBoxPatch(this, whichBox); p.EdgeAlpha = 1; p.FaceVertexAlphaData = 0; delete(this.ROI); end end %------------------------------------------------------------------ function p = getBoxPatch(this, whichBox) tag = sprintf('bboxPatch%d',whichBox); p = findobj(this.Axes, 'Type','patch','Tag', tag); end end %---------------------------------------------------------------------- % Zoom/Pan methods specialized for imscrollbar. %---------------------------------------------------------------------- methods %------------------------------------------------------------------ function setZoomInState(this, shouldZoomIn) hIm = getImage(this); if shouldZoomIn warnstate = warning('off','images:imuitoolsgate:undocumentedFunction'); zoomInFcn = imuitoolsgate('FunctionHandle', 'imzoomin'); warning(warnstate); set(hIm,'ButtonDownFcn',zoomInFcn); glassPlus = setptr('glassplus'); iptSetPointerBehavior(hIm,@(hFig,~) set(hFig,glassPlus{:})); else set(hIm,'ButtonDownFcn',''); iptSetPointerBehavior(hIm,[]); end end %------------------------------------------------------------------ function setZoomOutState(this, shouldZoomOut) hIm = getImage(this); if shouldZoomOut warnstate = warning('off','images:imuitoolsgate:undocumentedFunction'); zoomOutFcn = imuitoolsgate('FunctionHandle', 'imzoomout'); warning(warnstate); set(hIm,'ButtonDownFcn',zoomOutFcn); glassMinus = setptr('glassminus'); iptSetPointerBehavior(hIm,@(hFig,~) set(hFig,glassMinus{:})); else set(hIm,'ButtonDownFcn',''); iptSetPointerBehavior(hIm,[]); end end %------------------------------------------------------------------ function setPanState(this, shouldPan) hIm = getImage(this); if shouldPan warnstate = warning('off','images:imuitoolsgate:undocumentedFunction'); panFcn = imuitoolsgate('FunctionHandle', 'impan'); warning(warnstate); set(hIm,'ButtonDownFcn',panFcn); handCursor = setptr('hand'); iptSetPointerBehavior(hIm,@(hFig,~) set(hFig,handCursor{:})); else set(hIm,'ButtonDownFcn',''); iptSetPointerBehavior(hIm,[]); end end %------------------------------------------------------------------ function h = getImage(this) h = findobj(this.ScrollPanel,'type','image'); end end %---------------------------------------------------------------------- methods %------------------------------------------------------------------ function tf = isLeftClick(this) tf = strcmpi(this.Fig.SelectionType,'normal'); end %------------------------------------------------------------------ function tf = isDoubleClick(this) tf = strcmpi(this.Fig.SelectionType,'open'); end %------------------------------------------------------------------ function tf = isCtrlClick(this) % selection type is 'alt' for both right click and ctrl-left % click. check current modifier to check for ctrl press. modifier = get(this.Fig, 'CurrentModifier'); ctrlPressed = ~isempty(modifier) && strcmpi(modifier, 'control'); tf = ctrlPressed && strcmpi(this.Fig.SelectionType,'alt'); end %------------------------------------------------------------------ function tf = isShiftClick(this) tf = strcmpi(this.Fig.SelectionType, 'extend'); end end end