# HG changeset patch # User Carnë Draug # Date 1373476701 -3600 # Node ID 1b3b3ee882843fa863d4ab71f896579df0f2671c # Parent 0dab17e69a5556609e5399decbf2b82bfe2dbc77 Add optional extension argument to image IO functions. * imfinfo.m, private/core_imfinfo.m, imread.m, private/core_imread.m, imwrite.m: added support for optional EXT argument. * imwrite.m: in addition to support the extra EXT argument, removed list of supported image formats from help text, and suggested to use imformats() instead. * private/core_imwrite.m: remove usage of __magick_format_list__() which is being replaced by imformats(). Check validity of colormap earlier on. Removed tests that are repeated in imwrite.m (leftovers from copying the file). * private/imageIO.m: deal with separate filename and extension and ignore file existence checking if we are writing. * __magic_read__.cc: remove private function __magick_format_list__() that was only being used by core_imwrite.m and is being replaced by imformats(). diff -r 0dab17e69a55 -r 1b3b3ee88284 libinterp/dldfcn/__magick_read__.cc --- a/libinterp/dldfcn/__magick_read__.cc Tue Jul 09 18:53:06 2013 -0700 +++ b/libinterp/dldfcn/__magick_read__.cc Wed Jul 10 18:18:21 2013 +0100 @@ -1206,66 +1206,3 @@ ## No test needed for internal helper function. %!assert (1) */ - -// Determine the file formats supported by GraphicsMagick. This is -// called once at the beginning of imread or imwrite to determine -// exactly which file formats are supported, so error messages can be -// displayed properly. - -DEFUN_DLD (__magick_format_list__, args, , - "-*- texinfo -*-\n\ -@deftypefn {Loadable Function} {} __magick_format_list__ (@var{formats})\n\ -Undocumented internal function.\n\ -@end deftypefn") -{ - octave_value retval; - -#ifdef HAVE_MAGICK - maybe_initialize_magick (); - - std::list accepted_formats; - - if (args.length () == 1) - { - Cell c = args (0).cell_value (); - - if (! error_state) - { - for (octave_idx_type i = 0; i < c.nelem (); i++) - { - try - { - std::string fmt = c.elem (i).string_value (); - - Magick::CoderInfo info(fmt); - - if (info.isReadable () && info.isWritable ()) - accepted_formats.push_back (fmt); - } - catch (Magick::Exception& e) - { - // Do nothing: exception here are simply missing formats. - } - } - } - else - error ("__magick_format_list__: expecting a cell array of image format names"); - } - else - print_usage (); - - retval = Cell (accepted_formats); - -#else - - error ("__magick_format_list__: not available in this version of Octave"); - -#endif - - return retval; -} - -/* -## No test needed for internal helper function. -%!assert (1) -*/ diff -r 0dab17e69a55 -r 1b3b3ee88284 scripts/image/imfinfo.m --- a/scripts/image/imfinfo.m Tue Jul 09 18:53:06 2013 -0700 +++ b/scripts/image/imfinfo.m Wed Jul 10 18:18:21 2013 +0100 @@ -18,12 +18,16 @@ ## -*- texinfo -*- ## @deftypefn {Function File} {@var{info} =} imfinfo (@var{filename}) +## @deftypefnx {Function File} {@var{info} =} imfinfo (@var{filename}, @var{ext}) ## @deftypefnx {Function File} {@var{info} =} imfinfo (@var{url}) ## Read image information from a file. ## ## @code{imfinfo} returns a structure containing information about the image -## stored in the file @var{filename}. The output structure contains the -## following fields. +## stored in the file @var{filename}. If there is no file @var{filename}, +## and @var{ext} was specified, it will look for a file named @var{filename} +## and extension @var{ext}, i.e., a file named @var{filename}.@var{ext}. +## +## The output structure @var{info} contains the following fields: ## ## @table @samp ## @item Filename @@ -103,16 +107,18 @@ ## FlashPix viewing parameters. ## @end table ## -## @seealso{imread, imwrite, imshow} +## @seealso{imread, imwrite, imshow, imformats} ## @end deftypefn ## Author: Soren Hauberg -function info = imfinfo (filename) - if (nargin < 1) +function info = imfinfo (varargin) + if (nargin < 1 || nargin > 2) print_usage (); - elseif (! ischar (filename)) + elseif (! ischar (varargin{1})) error ("imfinfo: FILENAME must be a string"); + elseif (nargin > 1 && ! ischar (varargin{2})) + error ("imfinfo: EXT must be a string"); endif - info = imageIO (@core_imfinfo, "info", filename, filename); + info = imageIO (@core_imfinfo, "info", varargin, varargin); endfunction diff -r 0dab17e69a55 -r 1b3b3ee88284 scripts/image/imread.m --- a/scripts/image/imread.m Tue Jul 09 18:53:06 2013 -0700 +++ b/scripts/image/imread.m Wed Jul 10 18:18:21 2013 +0100 @@ -20,16 +20,17 @@ ## along with Octave; see the file COPYING. If not, see ## . -## Author: Thomas L. Scofield -## Author: Kristian Rumberg -## Author: Thomas Weber -## Author: Stefan van der Walt -## Author: Andy Adler - ## -*- texinfo -*- -## @deftypefn {Function File} {[@var{img}, @var{map}, @var{alpha}] =} imread (@var{filename}) +## @deftypefn {Function File} {[@var{img}, @var{map}, @var{alpha}] =} imread (@var{filename}) +## @deftypefnx {Function File} {[@dots{}] =} imread (@var{filename}, @var{ext}) +## @deftypefnx {Function File} {[@dots{}] =} imread (@var{url}) ## Read images from various file formats. ## +## Reads an image as a matrix from the file @var{filename}. If there is +## no file @var{filename}, and @var{ext} was specified, it will look for +## a file named @var{filename} and extension @var{ext}, i.e., a file named +## @var{filename}.@var{ext}. +## ## The size and numeric class of the output depends on the ## format of the image. A color image is returned as an ## @nospell{MxNx3} matrix. Gray-level and black-and-white images are @@ -38,17 +39,32 @@ ## class of the output: "uint8" or "uint16" for gray ## and color, and "logical" for black and white. ## -## @seealso{imwrite, imfinfo} +## @seealso{imwrite, imfinfo, imformats} ## @end deftypefn -function varargout = imread (filename, varargin) +## Author: Thomas L. Scofield +## Author: Kristian Rumberg +## Author: Thomas Weber +## Author: Stefan van der Walt +## Author: Andy Adler + +function varargout = imread (varargin) if (nargin < 1) print_usage (); - elseif (! ischar (filename)) + elseif (! ischar (varargin{1})) error ("imread: FILENAME must be a string"); endif - varargout{1:nargout} = imageIO (@core_imread, "read", filename, - filename, varargin{:}); + ## In case the file format was specified as a separate argument we + ## do this. imageIO() will ignore the second part if filename on its + ## own is enough. And if the second argument was a parameter name instead + ## of an extension, it is still going to be passed to the next function + ## since we are passing the whole function input as well. + filename = {varargin{1}}; + if (nargin > 1 && ischar (varargin {2})) + filename{2} = varargin{2}; + endif + + varargout{1:nargout} = imageIO (@core_imread, "read", filename, varargin{:}); endfunction %!testif HAVE_MAGICK diff -r 0dab17e69a55 -r 1b3b3ee88284 scripts/image/imwrite.m --- a/scripts/image/imwrite.m Tue Jul 09 18:53:06 2013 -0700 +++ b/scripts/image/imwrite.m Wed Jul 10 18:18:21 2013 +0100 @@ -18,13 +18,15 @@ ## -*- texinfo -*- ## @deftypefn {Function File} {} imwrite (@var{img}, @var{filename}) -## @deftypefnx {Function File} {} imwrite (@var{img}, @var{filename}, @var{fmt}) -## @deftypefnx {Function File} {} imwrite (@var{img}, @var{filename}, @var{fmt}, @var{p1}, @var{v1}, @dots{}) +## @deftypefnx {Function File} {} imwrite (@var{img}, @var{filename}, @var{ext}) +## @deftypefnx {Function File} {} imwrite (@var{img}, @var{filename}, @var{ext}, @var{p1}, @var{v1}, @dots{}) ## @deftypefnx {Function File} {} imwrite (@var{img}, @var{map}, @var{filename}, @dots{}) ## Write images in various file formats. ## -## If @var{fmt} is not supplied, the file extension of @var{filename} is used -## to determine the format. +## If @var{ext} is not supplied, the file extension of @var{filename} is used +## to determine the format. The actual supported formats are dependent on +## options made during the build of Octave. Use @code{imformats} to check +## the support of the different image formats. ## ## The parameter-value pairs (@var{p1}, @var{v1}, @dots{}) are optional. ## Currently the following options are supported for @t{JPEG} images: @@ -36,31 +38,7 @@ ## quality and lower compression. ## @end table ## -## @strong{Supported Formats} -## @multitable @columnfractions .33 .66 -## @headitem Extension @tab Format -## @item bmp @tab Windows Bitmap -## @item gif @tab Graphics Interchange Format -## @item jpg and jpeg @tab Joint Photographic Experts Group -## @item pbm @tab Portable Bitmap -## @item pcx @tab -## @item pgm @tab Portable Graymap -## @item png @tab Portable Network Graphics -## @item pnm @tab Portable Anymap -## @item ppm @tab Portable Pixmap -## @item ras @tab Sun Raster -## @item tif and tiff @tab Tagged Image File Format -## @item xwd @tab X11 Dump -## @end multitable -## -## @strong{Unsupported Formats} -## @multitable @columnfractions .33 .66 -## @headitem Extension @tab Format -## @item hdf @tab Hierarchical Data Format V4 -## @item @nospell{jp2} and jpx @tab Joint Photographic Experts Group 2000 -## @end multitable -## -## @seealso{imread, imfinfo} +## @seealso{imread, imfinfo, imformats} ## @end deftypefn function imwrite (varargin) @@ -68,15 +46,24 @@ print_usage (); endif + ## This input checking is a bit convoluted to support the multiple + ## ways the function can be called. Basically, after the image we + ## can have the filename or a colormap. If we have a colormap, then + ## the filename becomes the third argument. After that, we may have + ## the optional file extension. if (ischar (varargin{2})) - filename = varargin{2}; + filename_idx = 2; elseif (nargin >= 3 && iscolormap (varargin{2}) && ! ischar (varargin{3})) - filename = varargin{3}' + filename_idx = 3; else error ("imwrite: no FILENAME specified"); endif - varargout{1:nargout} = imageIO (@core_imwrite, "write", filename, varargin{:}); + filename = {varargin{filename_idx}}; + if (nargin > filename_idx + 1 && ischar (varargin {filename_idx + 1})) + filename{2} = varargin{filename_idx + 1}; + endif + imageIO (@core_imwrite, "write", filename, varargin{:}); endfunction %% Test input validation diff -r 0dab17e69a55 -r 1b3b3ee88284 scripts/image/private/core_imfinfo.m --- a/scripts/image/private/core_imfinfo.m Tue Jul 09 18:53:06 2013 -0700 +++ b/scripts/image/private/core_imfinfo.m Wed Jul 10 18:18:21 2013 +0100 @@ -1,4 +1,5 @@ ## Copyright (C) 2008-2012 Soren Hauberg +## Copyright (C) 2013 Carnë Draug ## ## This file is part of Octave. ## @@ -23,14 +24,16 @@ ## Author: Soren Hauberg -function info = core_imfinfo (filename) +function info = core_imfinfo (filename, ext) - if (nargin < 1) + if (nargin < 1 || nargin > 2) print_usage ("imfinfo"); endif if (! ischar (filename)) error ("imfinfo: FILENAME must be a string"); + elseif (nargin >= 2 && ! ischar (ext)) + error ("imfinfo: EXT must be a string"); endif filename = tilde_expand (filename); @@ -38,18 +41,24 @@ unwind_protect fn = file_in_path (IMAGE_PATH, filename); + if (isempty (fn)) + ## We couldn't find the file so... + if (nargin >= 2) + ## try adding a possible file extesion + filename = [filename "." ext]; + fn = file_in_path (IMAGE_PATH, filename); + if (isempty (fn)) + error ("imfinfo: cannot find file %s", filename); + endif + else + ## try filename as an URL + [fn, status, msg] = urlwrite (filename, tmpnam ()); + if (! status) + error ("imfinfo: cannot find or download %s: %s", filename, msg); + endif + delete_file = true; + endif - if (isempty (fn)) - ## Couldn't find file. See if it's an URL. - [fn, status, msg] = urlwrite (filename, tmpnam ()); - if (! status) - error ("imfinfo: cannot find %s", filename); - endif - - if (! isempty (fn)) - delete_file = true; - endif - endif info = __magick_finfo__ (fn); unwind_protect_cleanup diff -r 0dab17e69a55 -r 1b3b3ee88284 scripts/image/private/core_imread.m --- a/scripts/image/private/core_imread.m Tue Jul 09 18:53:06 2013 -0700 +++ b/scripts/image/private/core_imread.m Wed Jul 10 18:18:21 2013 +0100 @@ -1,3 +1,4 @@ +## Copyright (C) 2013 Carnë Draug ## Copyright (C) 2008-2012 Thomas L. Scofield ## Copyright (C) 2008 Kristian Rumberg ## Copyright (C) 2006 Thomas Weber @@ -20,41 +21,33 @@ ## along with Octave; see the file COPYING. If not, see ## . +## This function does all the work of imread. It exists here as private +## function so that imread can use other functions if imformats is +## configured to. It is also needed so that imformats can create a +## function handle for it. + +## Author: Carnë Draug ## Author: Thomas L. Scofield ## Author: Kristian Rumberg ## Author: Thomas Weber ## Author: Stefan van der Walt ## Author: Andy Adler -## -*- texinfo -*- -## @deftypefn {Function File} {[@var{img}, @var{map}, @var{alpha}] =} imread (@var{filename}) -## Read images from various file formats. -## -## The size and numeric class of the output depends on the -## format of the image. A color image is returned as an -## @nospell{MxNx3} matrix. Gray-level and black-and-white images are -## of size @nospell{MxN}. -## The color depth of the image determines the numeric -## class of the output: "uint8" or "uint16" for gray -## and color, and "logical" for black and white. -## -## @seealso{imwrite, imfinfo} -## @end deftypefn - function varargout = core_imread (filename, varargin) if (nargin < 1) print_usage ("imread"); - endif - - if (! ischar (filename)) + elseif (! ischar (filename)) error ("imread: FILENAME must be a string"); endif - filename = tilde_expand (filename); - - fn = file_in_path (IMAGE_PATH, filename); - + filename = tilde_expand (filename); + fn = file_in_path (IMAGE_PATH, filename); + if (isempty (fn) && nargin >= 2 && ischar (varargin{1})) + ## if we can't find the file, check if the next input is the file extension + filename = [filename "." varargin{1}]; + fn = file_in_path (IMAGE_PATH, filename); + endif if (isempty (fn)) error ("imread: cannot find %s", filename); endif diff -r 0dab17e69a55 -r 1b3b3ee88284 scripts/image/private/core_imwrite.m --- a/scripts/image/private/core_imwrite.m Tue Jul 09 18:53:06 2013 -0700 +++ b/scripts/image/private/core_imwrite.m Wed Jul 10 18:18:21 2013 +0100 @@ -23,12 +23,6 @@ function core_imwrite (img, varargin) - persistent imwrite_possible_formats = { - "bmp"; "gif"; "jp2"; "jpg"; "jpx"; "jpeg"; "hdf"; "pbm"; "pcx"; - "pgm"; "png"; "pnm"; "ppm"; "ras"; "tif"; "tiff"; "xwd" }; - - persistent accepted_formats = __magick_format_list__ (imwrite_possible_formats); - if (nargin < 2 || ! (isnumeric (img) || islogical (img))) print_usage ("imwrite"); endif @@ -39,8 +33,8 @@ offset = 1; if (isnumeric (varargin{1})) map = varargin{1}; - if (isempty (map)) - error ("imwrite: colormap must not be empty"); + if (! iscolormap (map)) + error ("imwrite: invalid COLORMAP"); endif offset = 2; endif @@ -55,18 +49,18 @@ print_usage ("imwrite"); endif if (offset < length (varargin)) - has_param_list = 1; + has_param_list = true; for ii = offset:2:(length (varargin) - 1) options.(varargin{ii}) = varargin{ii + 1}; endfor else - has_param_list = 0; + has_param_list = false; endif filename = tilde_expand (filename); if (isempty (fmt)) - [d, n, fmt] = fileparts (filename); + [~, ~, fmt] = fileparts (filename); if (! isempty (fmt)) fmt = fmt(2:end); endif @@ -80,10 +74,6 @@ error ("imwrite: sparse images not supported"); endif - if (! strcmp (fmt, accepted_formats)) - error ("imwrite: %s: unsupported or invalid image format", fmt); - endif - img_class = class (img); map_class = class (map); nd = ndims (img); @@ -97,6 +87,7 @@ if (nd == 3 && size (img, 3) < 3) error ("imwrite: invalid dimensions for truecolor image"); endif + ## FIXME: why nd>5? Shouldn't it be nd>4? What's the 5th dimension for? if (nd > 5) error ("imwrite: invalid %d-dimensional image data", nd); endif @@ -139,16 +130,3 @@ endif endfunction - - -%% Test input validation -%!error imwrite () # Wrong # of args -%!error imwrite (1) # Wrong # of args -%!error imwrite ({"cell"}, "filename.jpg") # Wrong class for img -%!error imwrite (1, [], "filename.jpg") # Empty image map -%!error imwrite (1, 2, 3) # No filename specified -%!error imwrite (1, "filename") # No fmt specified -%!error imwrite (1, "filename", "junk") # Invalid fmt specified -%!error imwrite ([], "filename.jpg") # Empty img matrix -%!error imwrite (spones (2), "filename.jpg") # Invalid sparse img - diff -r 0dab17e69a55 -r 1b3b3ee88284 scripts/image/private/imageIO.m --- a/scripts/image/private/imageIO.m Tue Jul 09 18:53:06 2013 -0700 +++ b/scripts/image/private/imageIO.m Wed Jul 10 18:18:21 2013 +0100 @@ -16,29 +16,54 @@ ## along with Octave; see the file COPYING. If not, see ## . -## This function simply connects the function that call it to all +## This function simply connects the function that calls it to all ## other imageIO functions. It does it by checking the file extension ## of the file and redirecting to the appropriate function after checking ## with imformats. ## ## First argument is a function handle for the default imageIO function (what -## to use if the extensino is not listed by imformats), second argument is -## the fieldname in the struct returned by imformats with a function handle -## for the function to use, and all the others are the input argument mean for -## the destination function. +## to use if the file extension for the image file is not listed by imformats). +## Second argument is the fieldname in the struct returned by imformats with a +## function handle for the function to use. Third argument is a cell array, its +## first element the filename, and the second, an optional file extension to +## add to filename, if filename alone does not exist. All the others are the +## original input arguments passed to the original imageIO function which will +## be passed on to the destination function. ## -## No input checking whatsoever is performed. That is already done by the +## No input checking whatsoever is performed. That should be performed by the ## function calling it. function varargout = imageIO (core_func, fieldname, filename, varargin) - [~, ~, ext] = fileparts (filename); - ## remove dot from extension - if (! isempty (ext) && strcmp (ext(1), ".")); - ext(1) = []; + + ## It should not be this function job to check if the file exists or not. + ## However, we need to know the file extension to use with imformats and + ## that means we need to know the actual filename that will be used which + ## is dependent on whether a file exists. + ## + ## If a file named filename{1} exists, then that's it, we will use that + ## wether it has an extension or not. If it does not exist and we have + ## something in filename{2}, then we will consider it the file extension. + ## Note the detail that if we find a file using filename{1} only, then we + ## should completely ignore filename{2}. It won't even be used by + ## imformats() at all, even if filename{1} has no extension to use with + ## imformats(). + ## + ## To further complicate things, when we are going to be writing a + ## file, whether the file exists or not does not matter. + if (isscalar (filename) || (strcmp (fieldname, "write") && + ! isempty (file_in_path (IMAGE_PATH, filename{1})))) + [~, ~, ext] = fileparts (filename{1}); + if (! isempty (ext)) + ## remove dot from extension + ext(1) = []; + endif + else + ext = filename{2}; endif - fmt = imformats ("ext"); + + fmt = imformats (ext); ## When there is no match, fmt will be a 1x1 structure with no fields, - ## so we can't just use isempty (). + ## so we can't just use `isempty (fmt)'. if (isempty (fieldnames (fmt))) varargout{1:nargout} = core_func (varargin{:}); else