function V=bubvol(I,ostr,ps) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Copyright 2014 Washington State University: Tom Asaki (tasaki@wsu.edu) % % Revision: 12 May 2014 % % bubvol is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or %(at your option) any later version. % % bubvol is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with sinthgunt. If not, see http://www.gnu.org/licenses/ % %% Description % function BUBVOL computes the volume of a surface rotation given an image % of the object profile. This routine is *not* general to all shapes. % The assumption is that the axis of symmetry is one of the two principle % directions. % % % GENERAL USAGE: % V=bubvol(I,ostr,ps) % % INPUTS: % I is the object profile image or image file path/name. The image % should be nearly binary in value. The image will be forced % binary by this function by thresholding at the average of the % maximum and minimum image intensities. The object can be % either light or dark (1 or 0). % ostr is the orientation of the object in the image. The user % supplies one of four characters that indicate the rough % orientation of the axis of symmetry: '|' or '/' or '-' or '\'. % There is not default value, orient must be specified. % ps pxiel size in real units. The pixels are assumed to be square. % If ps is not supplied, then the pixel size is assumed to be 1 % and the reported volume is in cubic pixels. % % % EXAMPLE USAGE: % V=bubvol(I,'/',4.35) computes the volume of the solid of revolution % indicated by the profile image I, whose axis of % symmetry in the image is roughly from bottom left % to top right, and whose pixel size is 4.35 units % (where the meaning of "units" is e.g. meter). % % EXAMPLE USAGE FROM FIGURE 2 % V=bubvol('10x.tif','-',0.92e-6) computes the volume of the solid of % revolution indicated by the profile image found in % the file 10x.tif, showing a 10x image of an % uncompressed pico gauge tip. The edge length of a % pixel is 0.92 micrometer. The volume is returned in % units of cubic meter. % The axis of symmetry in the image is roughly % horizontal. % % DeltaV=bubvol('63x.tif','-',0.146e-6) computes the volume of the solid % of revolution indicated by the profile image found % in the file 63x.tif, showing a 63x image of a % compressed pico gauge tip. The edge length of a % pixel is 0.146 micrometer. The volume is returned % in units of cubic meter. % The axis of symmetry in the image is roughly % horizontal. % % To compute the pressure using the images 10x.tif % and 63x.tif, we use Equation 1: % % P = 0.852e9*bubvol('63x.tif','-',0.146e-6)/bubvol('10x.tif','-',0.92e-6) % % where the bulk modulus of the oil is 0.852 GPa. % In this case, we find P = 1.40 MPa. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% input checking % set default volume V=0; % check for image and 'fix' it if ~exist('I','var') || isempty(I) disp('You must supply an input image or file name.'); return end if ischar(I) I=imread(I); if ndims(I)>2 I=rgb2gray(I); end I=double(I); else if ndims(I)>2 disp('Image must be two-dimensional.'); return end end I=(I-min(I(:)))/(max(I(:))-min(I(:))); I=round(I); if I(1), I=~I; end % check for orientation and 'fix' it if ~exist('ostr','var') || isempty(ostr) disp('You must supply an orientation string.'); return end if ischar(ostr) if ~strcmp(ostr,{'|' '/' '-' '\'}) disp('Unrecognized orientation string.') return end else disp('Input orient should be a string.'); return end % check for pixel size input if ~exist('ps','var') || isempty(ps) ps=1; end %% Preliminaries % extract pixels of interest and locations. L=[col,row] in pixel units. % Then subrtract the mean -- the axis must go throught this mean point % anyway. [r,c]=find(I); L=[c r]; M=mean(L); L=L-repmat(M,size(L,1),1); nr=size(I,2); % clean up some stuff clear I c r %% Get symmetry direction % w contains the two principle directions (read as either columns or rows) [U,S,V]=svd(L','econ'); %#ok % clean up some stuff clear S V % Pick the one most closely aligned with the given orientation. % Keep in mind that the visual orientation (e.g. '/') is not the same % as the computed orientation because of the flipped Matlab/octave % indexing on the first variable. th=atan(U(:,2)./U(:,1)); th=[th ; th+pi]; switch ostr case '|' a=pi/2; case '/' a=-pi/4; case '-' a=0; case '\' a=pi/4; end [da idx]=min(abs(th-a)); %#ok lineangle=th(idx); %% Compute the volume % the volume of a rotated pixel, independent of the rotation angle % of the pixel in the profile plane, is V=2*pi*d*w^2, where d is % the distance from the pixel center to the line and w is the pixel % width. If the pixel overlaps the axis of symmetry then this % simple formula does not hold. Thus, we compute the partial volumes % for pixel which do not cross the axis then iteratively subdivide % the pixels into smaller pixels. % start with zero volume V=0; % sa and ca are used to compute distances to the axis sa=sin(lineangle); ca=cos(lineangle); % depth is the iteration depth depth=2; % subsampling factor: each pixel becomes subsamp^2 pixels at each iteration subsamp=2; % the normalized subsampling position vector subvec=linspace(-(subsamp-1)/2,(subsamp-1)/2,subsamp)/subsamp*2; % main iteration % depth=0 corresponds to given pixel size ps % depth=k considers pixels of width ps/subsamp^k for k=0:depth % set current pixel size w=subsamp^(-k); % compute the distance of all pixels to the axis t=L(:,1)*ca+L(:,2)*sa; d2=(L(:,1)-t*ca).^2+(L(:,2)-t*sa).^2; % keep a mask of sufficiently distant pixels gomask=sqrt(d2)>=w/sqrt(2); % add the partial volume to the total Vold=V; V=V+sum(sqrt(d2(gomask)))*w^2; % downselect to the pixels yet to consider L=L(~gomask,:); % compute the new finer-grid pixel locations in K, then save to L. K=[]; dx=repmat(w/2*subvec',1,subsamp); dy=repmat(w/2*subvec,subsamp,1); for j=1:size(L,1) x=repmat(L(j,1)',subsamp,subsamp); y=repmat(L(j,2)',subsamp,subsamp); x=x+dx; y=y+dy; K=[K ; [x(:) y(:)] ]; %#ok end L=K; end % final correction based on all pixels now being very close % to the axis of symmetry. Basically, each iteration is now % only adding 1/subsamp^2 of the previous added volume. So, % sum the series and add the total. V=Vold+(V-Vold)*(subsamp^2/(subsamp^2-1)); %% final tweaks % The volumes computed above double count the volume % and did not include the factor pi, thus: V=pi*V; % now fold in the actual pixel size in real units. V=V*ps^3; %% display results to command prompt disp(' '); disp('------------------------------------------'); disp(' '); if ps==1 disp([' Volume = ',num2str(V),' cubic pixels.']); else disp([' Volume = ',num2str(V),' cubic units.']); end disp(' '); disp([' Symmetry Axis Angle = ',num2str(-lineangle/pi*180),' degrees.']); disp([' Symmetry Center [h,v] = [',num2str(M(1)),',',num2str(nr-M(2)+1),'] pixels.']); disp(' '); disp('------------------------------------------'); disp(' '); %% done return