gusucode.com > vision工具箱matlab源码程序 > vision/+vision/+internal/+ocr/+tool/ImageBrowser.m
classdef ImageBrowser < handle events SelectionChange; OpenSelection; RemoveSelection; end properties % Current selected image indices currentSelection = []; EnableContextMenu end properties (Access = private) parent; hParentFigure; imageSource; haxes; hvsb; % index using the same index as the source hImages = matlab.graphics.primitive.Image.empty(); hImageBackGroundColors = {}; % Image indices that need to be redrawn hIndsOfDirtyImages = []; % In pixels thumbNailSize; thumbNailImagePadding = 4; % all around. This space is part of the thumbnail image. thumbNailCellPadding = 2; % all around. This space is part of the canvas. % Border color for unselected images thumbNailImagePaddingColor = uint8([ 255 255 255]); % Border color for selected images thumbNailImageSelectionColor = uint8([78 148 243]); % In char scrollBarWidth = 3; % Visible part viewPortWidth viewPortHeight % Virtual canvas canvasWidth % same as viewPortWidth (no horizontal scrolling) canvasHeight % Visible number of thumbnails visibleColsOfThumbNails visibleRowsOfThumbNails % X and Y indices of visible thumbnails visibleThumbNailsXIndRange visibleThumbNailsYIndRange % Scale oneCharInPixels % Flag isDeleted = false; % Listeners sliderChangeListner % shiftSelectionAnchor = []; end %% API methods function imbrowser = ImageBrowser(parent_, imageSource_, thumbnailSize_, enableContextMenu_) assert(isa(parent_,'matlab.ui.container.Panel')); imbrowser.parent = parent_; imbrowser.hParentFigure = ancestor(parent_,'Figure'); %TODO assert cell of images or image datastore imbrowser.imageSource = imageSource_; % TODO assert int, MxN imbrowser.thumbNailSize = thumbnailSize_; % OCR App: enable context menu when required. This should be % removed when browser has support for custom context menus. imbrowser.EnableContextMenu = enableContextMenu_; imbrowser.haxes = axes('Parent',parent_,... 'Units','normalized',... 'Position', [ 0 0 1 1],... 'Ydir', 'reverse',... 'XLimMode','manual',... 'YLimMode','manual',... 'XTick', [],... 'YTick', [],... 'XColor', 'none',... 'YColor', 'none',... 'Tag', '_imageBrowserAxes',... 'NextPlot','add'); % NOTE - Value = 0 is bottom imbrowser.hvsb = uicontrol('Style','slider',... 'Units','pixels',... 'Parent',parent_,... 'Min', 0,... 'Max', 1,... 'Value', 1,... 'Tag', '_imageBrowserSlider',... 'Visible','off'); % TODO imbrowser.oneCharInPixels = 5; addlistener(parent_,'ObjectBeingDestroyed',@imbrowser.delete); parent_.SizeChangedFcn = @imbrowser.respondToSizeChange; imbrowser.sliderChangeListner = ... addlistener(imbrowser.hvsb,'Value','PostSet',@imbrowser.sliderChanged); % All images are initiall 'dirty' imbrowser.hIndsOfDirtyImages = 1:imbrowser.numberOfImages; imbrowser.hImageBackGroundColors{imbrowser.numberOfImages} = []; imbrowser.respondToSizeChange(); end function setSelection(imbrowser, selectedInds, scrollToSelection) if(nargin==2) scrollToSelection = true; end if(imbrowser.isDeleted) return end selectedInds(selectedInds<1) = 1; selectedInds(selectedInds>imbrowser.numberOfImages) = imbrowser.numberOfImages; % Mark previous selected as needing a redraw imbrowser.hIndsOfDirtyImages = ... [imbrowser.hIndsOfDirtyImages, imbrowser.currentSelection]; % Set selection imbrowser.currentSelection = selectedInds; % Update viewport to bring the last selected image into view if ~isempty(selectedInds) && scrollToSelection imbrowser.scrollVerticallyToSelection(); end % Redraw visible imbrowser.updateViewPortWithImages(); % issue drawnow to flush context menu drawing. drawnow % update shift selection anchor to newly selected image imbrowser.shiftSelectionAnchor = imbrowser.currentSelection; % Broadcast notify(imbrowser,'SelectionChange'); end function setImageBorderColor(imbrowser, iminds, rgbColor) if(imbrowser.isDeleted) return end for k =1:numel(iminds) imind = iminds(k); imbrowser.hImageBackGroundColors{imind} = uint8(rgbColor); imbrowser.hIndsOfDirtyImages(end+1) = imind; end imbrowser.updateViewPortWithImages(); end function keyPressFcn(imbrowser, ~, hEvent) if(imbrowser.isDeleted) return end if(any(strcmp(hEvent.Modifier,'control'))... ||any(strcmp(hEvent.Modifier,'command'))) if(strcmpi(hEvent.Key,'a')) % Only ctrl+a is supported imbrowser.setSelection(1:imbrowser.numberOfImages); end return; end if(any(strcmp(hEvent.Modifier,'shift'))... && ~(strcmp(hEvent.Key,'rightarrow')||strcmp(hEvent.Key,'leftarrow'))) % Shift key is only supported with left and right arrows return; end if(isempty(imbrowser.currentSelection)) lastClickedIndex = 1; else lastClickedIndex = imbrowser.currentSelection(end); end switch(hEvent.Key) case 'downarrow' index = lastClickedIndex+imbrowser.visibleColsOfThumbNails; if(index>imbrowser.numberOfImages) index = lastClickedIndex; end case 'pagedown' index = lastClickedIndex+... floor(imbrowser.visibleRowsOfThumbNails)*imbrowser.visibleColsOfThumbNails; if(index>imbrowser.numberOfImages) index = imbrowser.numberOfImages; end case 'uparrow' index = lastClickedIndex-imbrowser.visibleColsOfThumbNails; if(index<1) index = lastClickedIndex; end case 'pageup' index = lastClickedIndex-... floor(imbrowser.visibleRowsOfThumbNails)*imbrowser.visibleColsOfThumbNails; if(index<1) index = 1; end case 'leftarrow' index = lastClickedIndex-1; case 'rightarrow' index = lastClickedIndex+1; case 'home' index = 1; case 'end' index = imbrowser.numberOfImages; case 'delete' imbrowser.removeCurrentlySelectedImages(); return; case 'backspace' imbrowser.removeCurrentlySelectedImages(); return; case 'return' notify(imbrowser,'OpenSelection'); return; otherwise return; end % Limit index = max(1, index); index = min(index, imbrowser.numberOfImages); if(any(strcmp(hEvent.Modifier,'shift'))) if(any(imbrowser.currentSelection==index)) % Remove newSelection = imbrowser.currentSelection; if(strcmp(hEvent.Key,'leftarrow')) newSelection(newSelection>index) = []; elseif(strcmp(hEvent.Key,'rightarrow')) newSelection(newSelection<index) = []; end else % Extend newSelection = [imbrowser.currentSelection, index]; end else newSelection = index; end imbrowser.setSelection(newSelection); end function mouseWheelFcn(imbrowser, ~, hEvent) if(imbrowser.isDeleted) return end if(strcmp(imbrowser.hvsb.Visible,'off')) % Dont scroll. return; end curSliderLoc = imbrowser.hvsb.Value; scrollAmount = hEvent.VerticalScrollCount; % Slider top is Max bottom is 0; scrollAmount = -scrollAmount; curSliderLoc = curSliderLoc + scrollAmount; curSliderLoc = max(curSliderLoc,0); curSliderLoc = min(curSliderLoc, imbrowser.hvsb.Max); imbrowser.hvsb.Value = curSliderLoc; end function delete(imbrowser, varargin) imbrowser.isDeleted = true; end end %% Selection methods (Access = private) function thisImageWasClicked(imbrowser, hImage, hEvent) %hFig = ancestor(hImage,'Figure'); hFig = imbrowser.hParentFigure; % Last normal selection is the shift anchor (unless shift is % pressed - see shift selection below) lastSelected = hImage.UserData.index; if(hEvent.Button == 1) % Left clicked if(strcmp(hFig.SelectionType,'open')) imbrowser.setSelection(hImage.UserData.index); notify(imbrowser,'OpenSelection'); elseif(strcmp(hFig.SelectionType,'normal')) imbrowser.setSelection(hImage.UserData.index); elseif(strcmp(hFig.SelectionType,'alt')) % ctrl+click if(any(imbrowser.currentSelection==hImage.UserData.index)) % remove cur = imbrowser.currentSelection; cur(cur==hImage.UserData.index) = []; imbrowser.setSelection(cur,false); else % add imbrowser.setSelection(... [imbrowser.currentSelection, hImage.UserData.index]); end elseif(strcmp(hFig.SelectionType,'extend')) % shift+click if imbrowser.shiftSelectionAnchor % dont update anchor point lastSelected = imbrowser.shiftSelectionAnchor; end if(lastSelected<hImage.UserData.index) imbrowser.setSelection(lastSelected:hImage.UserData.index); else imbrowser.setSelection(hImage.UserData.index:lastSelected); end end elseif(hEvent.Button == 3) % Right click, mark selection and let context menu handle % the rest if current image is not selected. if(~any(hImage.UserData.index==imbrowser.currentSelection)) imbrowser.setSelection(hImage.UserData.index); end % issue drawnow to flush context menu drawing. drawnow end imbrowser.shiftSelectionAnchor = lastSelected; end function scrollVerticallyToSelection(imbrowser) % Scroll to view port which shows the last selected image. % If Selection is below current viewport, scroll to the top of % the new one, else scroll to the bottom. indexToScrollTo = imbrowser.currentSelection(end); rowToScrollTo = ceil(indexToScrollTo/imbrowser.visibleColsOfThumbNails); fullRowsInView = [ceil(imbrowser.visibleThumbNailsYIndRange(1)),... floor(imbrowser.visibleThumbNailsYIndRange(2)-1)]; % Note - important observation for the logic below to scroll to % a particular row (with that row being the top, fully visible % row in the current viewport) % % If slider.Value = 0, slider is at top and row 1 is at top of % viewport. % If slider.Value = max, slider is at bottom and the top row is % (totalrows - number of rows visible) if(rowToScrollTo==1) imbrowser.hvsb.Value = imbrowser.hvsb.Max; elseif(rowToScrollTo<fullRowsInView(1)) % Ensure rowToScrollTo is shown in full on the top viewTopRow = rowToScrollTo-1; % This bit is a bit shaky. imbrowser.hvsb.Value = imbrowser.hvsb.Max -(imbrowser.hvsb.Max/(imbrowser.hvsb.Max-imbrowser.visibleRowsOfThumbNails)) *viewTopRow; elseif(rowToScrollTo>fullRowsInView(2)) % Ensure rowToScrollTo is shown in full on the bottom viewTopRow = rowToScrollTo-imbrowser.visibleRowsOfThumbNails; imbrowser.hvsb.Value = imbrowser.hvsb.Max -(imbrowser.hvsb.Max/(imbrowser.hvsb.Max-imbrowser.visibleRowsOfThumbNails)) *viewTopRow; else % already in view end end end %% Image source methods (Access = private) function n = numberOfImages(imbrowser) if(iscell(imbrowser.imageSource)) n = numel(imbrowser.imageSource); else n = numel(imbrowser.imageSource.Files); end end function im = readimage(imbrowser, ind) if(iscell(imbrowser.imageSource)) im = imbrowser.imageSource{ind}; else im = imbrowser.imageSource.readimage(ind); end end function removeFromInternalState(imbrowser, inds) % From source if(iscell(imbrowser.imageSource)) imbrowser.imageSource(inds) = []; else imbrowser.imageSource.Files(inds) = []; end % From view delete(imbrowser.hImages(inds)); imbrowser.hImages(inds) = []; % From background colors imbrowser.hImageBackGroundColors(inds) = []; end function removeCurrentlySelectedImages(imbrowser, varargin) % Note - This bit is specific to the OCR App % ------------------------------------------------------------- toRemoveInds = imbrowser.currentSelection(); if(isempty(toRemoveInds)) % nothing to do return; end toRemoveInds = imbrowser.currentSelection(); imbrowser.removeFromInternalState(toRemoveInds); % Reflow and re-index all imbrowser.hIndsOfDirtyImages = 1:imbrowser.numberOfImages; imbrowser.respondToSizeChange(); notify(imbrowser, 'RemoveSelection'); imbrowser.setSelection([]); drawnow; end end %% Redraw methods (Access = private) function respondToSizeChange(imbrowser, varargin) parentPosition =getpixelposition(imbrowser.haxes); imbrowser.viewPortWidth = parentPosition(3); imbrowser.viewPortHeight = parentPosition(4); sbWidth = imbrowser.scrollBarWidth*imbrowser.oneCharInPixels; % Full canvas size (in units of thumbnails) given the current % width totalThumbnailWidth= imbrowser.thumbNailSize(1)... +2*(imbrowser.thumbNailImagePadding+imbrowser.thumbNailCellPadding); wPixels = imbrowser.viewPortWidth - sbWidth; numColsOfThumbnails = floor(wPixels/totalThumbnailWidth); % min width of 1 thumbnail numColsOfThumbnails = max(numColsOfThumbnails,1); numRowsOfThumbnails = ceil(imbrowser.numberOfImages()/numColsOfThumbnails); % Number of thumbnails rows that can fit in view port % Includes partially visible ones too. totalThumbnailHeight = imbrowser.thumbNailSize(2)... +2*(imbrowser.thumbNailImagePadding+imbrowser.thumbNailCellPadding); numPartialRowsInViewPort = imbrowser.viewPortHeight/totalThumbnailHeight; imbrowser.canvasHeight = numRowsOfThumbnails*totalThumbnailHeight; % no horizontal scrolling, at least one thumbnail wide imbrowser.canvasWidth = imbrowser.viewPortWidth; imbrowser.canvasWidth = max(imbrowser.canvasWidth, totalThumbnailHeight); % Dont let the following manual slider value changes trigger % updates. imbrowser.sliderChangeListner.Enabled = false; if(numRowsOfThumbnails>numPartialRowsInViewPort) % Update slider limits if( (imbrowser.hvsb.Value>numRowsOfThumbnails) ... ||strcmp(imbrowser.hvsb.Visible,'off')) % Slider goes to top imbrowser.hvsb.Value = numRowsOfThumbnails; end imbrowser.hvsb.Max = numRowsOfThumbnails; % (Re)position Scroll bar on right imbrowser.hvsb.Position = ... [imbrowser.viewPortWidth-sbWidth 0 sbWidth imbrowser.viewPortHeight]; imbrowser.hvsb.Visible = 'on'; else imbrowser.hvsb.Visible = 'off'; % Scrollbar to top imbrowser.hvsb.Value = numRowsOfThumbnails; imbrowser.hvsb.Max = numRowsOfThumbnails; end imbrowser.visibleColsOfThumbNails = numColsOfThumbnails; imbrowser.visibleRowsOfThumbNails = numPartialRowsInViewPort; if(imbrowser.canvasHeight==0) % No images. Nothing to do. return; end % Manually do all the slider changes once. (which updates all % visible images too) imbrowser.sliderChanged(); % Restore imbrowser.sliderChangeListner.Enabled = true; end function sliderChanged(imbrowser, varargin) % No horizontal scrolling; imbrowser.haxes.XLim = [0 imbrowser.viewPortWidth]; % Slider top implies Value=Max bottom implies Value=0 sliderPercent = 1 - imbrowser.hvsb.Value/imbrowser.hvsb.Max; % Top Of Ylim can go from 0 to canvasHeight-imbrowser.viewPortHeight; topOfYLim = sliderPercent*(imbrowser.canvasHeight-imbrowser.viewPortHeight); imbrowser.haxes.YLim = [topOfYLim topOfYLim+imbrowser.viewPortHeight]; % Update the indices of thumbnails in view % view port xViewPort = imbrowser.haxes.XLim; yViewPort = imbrowser.haxes.YLim; totalThumbnailSize = imbrowser.thumbNailSize... +2*(imbrowser.thumbNailImagePadding+imbrowser.thumbNailCellPadding); % Update view port in terms of visible thumbnail indices imbrowser.visibleThumbNailsXIndRange = xViewPort/totalThumbnailSize(1); imbrowser.visibleThumbNailsYIndRange = yViewPort/totalThumbnailSize(2)+1; % Redraw content imbrowser.updateViewPortWithImages(); end function updateViewPortWithImages(imbrowser, varargin) xRange = [floor(imbrowser.visibleThumbNailsXIndRange(1)) ceil(imbrowser.visibleThumbNailsXIndRange(2))]; yRange = [floor(imbrowser.visibleThumbNailsYIndRange(1)) ceil(imbrowser.visibleThumbNailsYIndRange(2))]; xRange(1) = max(xRange(1), 1); xRange(2) = min(xRange(2), imbrowser.visibleColsOfThumbNails); yRange(1) = max(yRange(1), 1); yRange(2) = min(yRange(2), imbrowser.hvsb.Max); for xImageInd = xRange(1):xRange(2) for yImageInd = yRange(1):yRange(2) imbrowser.showImage(xImageInd, yImageInd); end end end function showImage(imbrowser, x,y) imageInd = (y-1)*imbrowser.visibleColsOfThumbNails+x; if(imageInd>imbrowser.numberOfImages) return; end thumbNailCellSize = imbrowser.thumbNailSize... +2*(imbrowser.thumbNailImagePadding+imbrowser.thumbNailCellPadding); xLoc = (x-1)*thumbNailCellSize(2); yLoc = (y-1)*thumbNailCellSize(1); % Does this image need updating? isDirty = any(imbrowser.hIndsOfDirtyImages==imageInd); % Already created? if(numel(imbrowser.hImages) <imageInd || isDirty) % create fullImage = imbrowser.readimage(imageInd); paddedThumbNail = repmat(... reshape(imbrowser.thumbNailImagePaddingColor,[ 1 1 3]),... [imbrowser.thumbNailSize+imbrowser.thumbNailImagePadding*2 1]); if(size(fullImage,1)>size(fullImage,2)) thumbNail = imresize(fullImage,[imbrowser.thumbNailSize(1), NaN],'nearest'); else thumbNail = imresize(fullImage,[NaN, imbrowser.thumbNailSize(2)],'nearest'); end if(size(thumbNail,3)==1) thumbNail = cat(3, thumbNail, thumbNail,thumbNail); end % Position the resized thumbnail at the center of the % paddedthumbnail leftOffset = imbrowser.thumbNailImagePadding+1+... ceil( (size(paddedThumbNail,1)-2*imbrowser.thumbNailImagePadding-size(thumbNail,1)) /2); topOffset = imbrowser.thumbNailImagePadding+1+... ceil( (size(paddedThumbNail,2)-2*imbrowser.thumbNailImagePadding-size(thumbNail,2)) /2); rightEnd = leftOffset+size(thumbNail,1)-1; bottomEnd = topOffset+size(thumbNail,2)-1; paddedThumbNail(leftOffset:rightEnd,topOffset:bottomEnd,:) = thumbNail; if(isDirty) % No longer dirty if(imageInd<= numel(imbrowser.hImages)) delete(imbrowser.hImages(imageInd)); end imbrowser.hIndsOfDirtyImages(imbrowser.hIndsOfDirtyImages==imageInd) = []; end imbrowser.hImages(imageInd) = image(xLoc, yLoc, paddedThumbNail,... 'Parent',imbrowser.haxes); imbrowser.hImages(imageInd).UserData.index = imageInd; imbrowser.hImages(imageInd).ButtonDownFcn = @imbrowser.thisImageWasClicked; if imbrowser.EnableContextMenu imbrowser.hImages(imageInd).UIContextMenu = uicontextmenu('Parent',imbrowser.hParentFigure); uimenu(imbrowser.hImages(imageInd).UIContextMenu, ... 'Label',vision.getMessage('vision:ocrTrainer:MoveToUnknown'),... 'Callback',@imbrowser.removeCurrentlySelectedImages); end else % reposition imbrowser.hImages(imageInd).XData = xLoc; imbrowser.hImages(imageInd).YData = yLoc; end if(any(imageInd==imbrowser.currentSelection)) % Show selection color on the border unSelectedPaddedThumbNail = imbrowser.hImages(imageInd).CData; paddedThumbNail = repmat(... reshape(imbrowser.thumbNailImageSelectionColor,[ 1 1 3]),... [imbrowser.thumbNailSize+imbrowser.thumbNailImagePadding*2 1]); % Position the resized thumbnail at the center of the % paddedthumbnail leftOffset = imbrowser.thumbNailImagePadding+1; topOffset = imbrowser.thumbNailImagePadding+1; rightEnd = size(paddedThumbNail,1)-imbrowser.thumbNailImagePadding; bottomEnd = size(paddedThumbNail,2)-imbrowser.thumbNailImagePadding; paddedThumbNail(leftOffset:rightEnd,topOffset:bottomEnd,:) = ... unSelectedPaddedThumbNail(leftOffset:rightEnd,topOffset:bottomEnd,:); imbrowser.hImages(imageInd).CData = paddedThumbNail; else % Check for custom color for the border customBackgroundColor = imbrowser.hImageBackGroundColors{imageInd}; if(~isempty(customBackgroundColor)) unSelectedPaddedThumbNail = imbrowser.hImages(imageInd).CData; paddedThumbNail = repmat(... reshape(customBackgroundColor,[ 1 1 3]),... [imbrowser.thumbNailSize+imbrowser.thumbNailImagePadding*2 1]); % Position the resized thumbnail at the center of the % paddedthumbnail leftOffset = imbrowser.thumbNailImagePadding+1; topOffset = imbrowser.thumbNailImagePadding+1; rightEnd = size(paddedThumbNail,1)-imbrowser.thumbNailImagePadding; bottomEnd = size(paddedThumbNail,2)-imbrowser.thumbNailImagePadding; paddedThumbNail(leftOffset:rightEnd,topOffset:bottomEnd,:) = ... unSelectedPaddedThumbNail(leftOffset:rightEnd,topOffset:bottomEnd,:); imbrowser.hImages(imageInd).CData = paddedThumbNail; end end end end end