Code covered by the BSD License  

Highlights from
PNM Toolbox 3.01

from PNM Toolbox 3.01 by Peter J. Acklam
The PNM Toolbox is a collection of MATLAB programs for reading and writing PPM, PGM, PBM, PNM, ...

pnmwritexbm(data, map, filename, varargin)
function pnmwritexbm(data, map, filename, varargin)
%PNMWRITEXBM Write a XBM (X bitmap) file to disk.
%
%   PNMWRITEXBM(BM, [], FILENAME) writes the bitmap image BM to the file
%   specified by the string FILENAME.
%
%   PNMWRITEXBM(I, [], FILENAME) converts the grayscale image I into a bitmap
%   (by thresholding) and writes the resulting image to the file specified by
%   the string FILENAME.
%
%   PNMWRITEXBM(RGB, [], FILENAME) converts the truecolor image represented by
%   the M-by-N-by-3 array RGB into a bitmap (by grayscaling and thresholding)
%   and writes the resulting image to the file specified by the string
%   FILENAME.
%
%   PNMWRITEXBM(X, MAP, FILENAME) converts the indexed image represented by the
%   index matrix X and colormap MAP into a bitmap (by grayscaling and by
%   thresholding) and writes the resulting image to the file specified by the
%   string FILENAME.
%
%   PNMWRITEXBM(...,'XHotSpot',VAL) sets the XHotSpot value to VAL.  VAL must
%   be a non-negative integer.
%
%   PNMWRITEXBM(...,'YHotSpot',VAL) sets the YHotSpot value to VAL.  VAL must
%   be a non-negative integer.
%
%   See also IMREAD, IMWRITE, IMFINFO.

%   Author:      Peter J. Acklam
%   Time-stamp:  2009-07-21 14:21:54 +02:00
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % Some argument checking
   %

   error(nargchk(3, Inf, nargin));

   nd = ndims(data);
   if nd > 3
      error(sprintf('%d-D data not supported for XBM files', nd));
   end

   if ~ischar(filename) | isempty(filename)
      error('Filename must be a non-empty string.');
   end

   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % Process param/value pairs
   %

   xhotspot = [];
   yhotspot = [];

   paramStrings = ['xhotspot'
                   'yhotspot'];

   if rem(length(varargin), 2)
      error('Odd number of Parameter-Value arguments.');
   end

   for k = 1:2:length(varargin)
      param = lower(varargin{k});
      if ~ischar(param)
         error('Parameter name must be a string');
      end
      idx = strmatch(param, paramStrings);
      if isempty(idx)
         error(sprintf('Unrecognized parameter name "%s"', param));
      elseif (length(idx) > 1)
         error(sprintf('Ambiguous parameter name "%s"', param));
      end

      param = deblank(paramStrings(idx,:));

      switch param
         case 'xhotspot'
            xhotspot = varargin{k+1};
            if ~isscalnonnegint(xhotspot)
               error('XHOTSPOT must be a scalar positive integer.');
            end
         case 'yhotspot'
            yhotspot = varargin{k+1};
            if ~isscalnonnegint(yhotspot)
               error('YHOTSPOT must be a scalar positive integer.');
            end
      end
   end

   [height, width, channels] = size(data);
   cls = class(data);

   rgbw = [ 0.298936 ; 0.587043 ; 0.114021 ];   % RGB weights

   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % Convert any image to a bitmap image.  Afterwards, `data' should be a
   % uint8 array of zeros (black) and ones (white).
   %

   if isempty(map)
      % no colormap, so it is not an indexed image

      if channels > 1
         % it is an rgb image

         % adjust rgb weights (to save work we convert from rgb image to
         % grayscale _and_ map pixel values to the set {0,1} all in one go)
         switch class(data)
            case 'uint8',  rgbw = rgbw / 255;
            case 'uint16', rgbw = rgbw / 65535;
            case 'double', % do nothing
         end

         % convert to uint8 array of zeros and ones
         data = uint8(round(   rgbw(1) * double(data(:,:,1)) ...
                             + rgbw(2) * double(data(:,:,2)) ...
                             + rgbw(3) * double(data(:,:,3)) ));

      else
         % it is a grayscale image or a bitmap image

         if islogical(data)
            % it is a bitmap image

            % convert to zeros and ones; let all non-zero values be one
            data(data) = 1;

         else
            % it is a grayscale image

            % convert to zeros and ones by bitshifting or thresholding
            switch class(data)
               case 'uint8',  data = bitshift(data, -7);
               case 'uint16', data = uint8(bitshift(data, -15));
               case 'double', data = uint8(data >= 0.5);
            end

         end
      end
   else
      % it is an indexed image

      % convert colormap to a vector of ones and zeros by thresholding
      bwmap = uint8(map * rgbw >= 0.5);

      % get image data
      switch class(data)
         case 'double',            data = bwmap(data);
         case {'uint8', 'uint16'}, data = bwmap(double(data)+1);
         otherwise, error('Index array is of invalid class.');
      end

   end

   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   % Convert the bitmap image into a vector containing the integer values
   % that will be written to the image file.
   %

   % adjust values since XBM uses zeros for white and ones for black
   data = bitxor(data, 1);

   % add padding if necessary
   byteWidth = ceil(width/8);           % number or bytes for one scanline
   paddedWidth = 8*byteWidth;           % padded width of image
   if paddedWidth > width
      data(:,width+1:paddedWidth) = 0;
   end

   % convert from bits (zeros and ones) to bytes {0,1,...,255} without
   % temporary conversion to double
   bytedata = repmat(uint8(0), [height byteWidth]);
   for i = 1:8
      bytedata = bitor(bytedata, bitshift(uint8(data(:,i:8:end)), i-1));
   end

   % Open output file for writing.
   fid = fopen(filename, 'wt');
   if fid < 0
      error([filename ': can''t open file for writing.']);
   end

   % Get file basename and trim it so it is suitable as an identifier.
   k = find(filename == '.');
   if isempty(k)
      filebase = filename;
   else
      filebase = filename(1:k(end)-1);
   end
   k =   (filebase < 'a' | filebase > 'z') ...
       & (filebase < 'A' | filebase > 'Z') ...
       & (filebase < '0' | filebase > '9');
   filebase(k) = '_';

   % Write file header.
   fprintf(fid, '#define %s_width %d\n', filebase, width);
   fprintf(fid, '#define %s_height %d\n', filebase, height);
   if ~isempty(xhotspot)
      fprintf(fid, '#define %s_x_hot %d\n', filebase, xhotspot);
   end
   if ~isempty(yhotspot)
      fprintf(fid, '#define %s_y_hot %d\n', filebase, yhotspot);
   end

   % Write image data.  Some XBM software writes images with a header saying
   % "static unsigned char ...", but some software failes to read the image
   % if the word "unsigned" is present, so just write "static char ...".
   bytedata = bytedata.';
   fprintf(fid, 'static char %s_bits[] = {\n', filebase);
   format = [repmat(' 0x%02x,', [1 12]) '\n'];
   fprintf(fid, format, double(bytedata(1:end-1)));
   fprintf(fid, '%02x};\n', double(bytedata(end)));

   % Close file.
   fclose(fid);


function tf = isscalnonnegint(x)
%ISSCALNONNEGINT True if input is a scalar non-negative integer.

   tf = all(size(x) == 1) & isnumeric(x);       % scalar and numeric
   if ~tf, return, end

   x = double(x);
   tf = x == round(x) & x >= 0;                 % integer and positive

Contact us at files@mathworks.com