1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
########################################################################
##
## Copyright (C) 2008-2024 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or <https://octave.org/copyright/>.
##
## This file is part of Octave.
##
## Octave 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.
##
## Octave 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 Octave; see the file COPYING. If not, see
## <https://www.gnu.org/licenses/>.
##
########################################################################
## -*- texinfo -*-
## @deftypefn {} {} imwrite (@var{img}, @var{filename})
## @deftypefnx {} {} imwrite (@var{img}, @var{filename}, @var{ext})
## @deftypefnx {} {} imwrite (@var{img}, @var{map}, @var{filename})
## @deftypefnx {} {} imwrite (@dots{}, @var{param1}, @var{val1}, @dots{})
## Write images in various file formats.
##
## The image @var{img} can be a binary, grayscale, RGB, or multi-dimensional
## image. The size and class of @var{img} should be the same as what should
## be expected when reading it with @code{imread}: the 3rd and 4th dimensions
## reserved for color space, and multiple pages respectively. If it's an
## indexed image, the colormap @var{map} must also be specified.
##
## 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.
##
## Depending on the file format, it is possible to configure the writing of
## images with @var{param}, @var{val} pairs. The following options are
## supported:
##
## @table @samp
## @item Alpha
## Alpha (transparency) channel for the image. This must be a matrix with
## same class, and number of rows and columns of @var{img}. In case of a
## multipage image, the size of the 4th dimension must also match and the third
## dimension must be a singleton. By default, image will be completely opaque.
##
## @item Compression
## Compression to use one the image. Can be one of the following: "none"
## (default), "bzip", "fax3", "fax4", "jpeg", "lzw", "rle", or "deflate".
## Note that not all compression types are available for all image formats
## in which it defaults to your @nospell{Magick} library.
##
## @item DelayTime
## For formats that accept animations (such as GIF), controls for how long a
## frame is displayed until it moves to the next one. The value must be scalar
## (which will applied to all frames in @var{img}), or a vector of length
## equal to the number of frames in @var{im}. The value is in seconds, must
## be between 0 and 655.35, and defaults to 0.5.
##
## @item DisposalMethod
## For formats that accept animations (such as GIF), controls what happens to
## a frame before drawing the next one. Its value can be one of the
## following strings: "doNotSpecify" (default); "leaveInPlace"; "restoreBG";
## and "restorePrevious", or a cell array of those string with length equal
## to the number of frames in @var{img}.
##
## @item LoopCount
## For formats that accept animations (such as GIF), controls how many times
## the sequence is repeated. A value of Inf means an infinite loop (default),
## a value of 0 or 1 that the sequence is played only once (loops zero times),
## while a value of 2 or above loops that number of times (looping twice means
## it plays the complete sequence 3 times). This option is ignored when there
## is only a single image at the end of writing the file.
##
## @item Quality
## Set the quality of the compression. The value should be an integer
## between 0 and 100, with larger values indicating higher visual quality and
## lower compression. Defaults to 75.
##
## @item WriteMode
## Some file formats, such as TIFF and GIF, are able to store multiple images
## in a single file. This option specifies if @var{img} should be appended
## to the file (if it exists) or if a new file should be created for it
## (possibly overwriting an existing file). The value should be the string
## @qcode{"Overwrite"} (default), or @qcode{"Append"}.
##
## Despite this option, the most efficient method of writing a multipage
## image is to pass a 4 dimensional @var{img} to @code{imwrite}, the same
## matrix that could be expected when using @code{imread} with the option
## @qcode{"Index"} set to @qcode{"all"}.
##
## @end table
##
## @seealso{imread, imfinfo, imformats}
## @end deftypefn
function imwrite (varargin)
if (nargin < 2)
print_usage ();
endif
[filename, ext] = imwrite_filename (varargin{2:end});
fmt = imformats (ext);
## When there is no match, fmt will be a 1x1 structure with
## no fields, so we can't just use 'isempty (fmt)'.
if (numfields (fmt) == 0)
if (isempty (ext))
error ("imwrite: no extension found for %s to identify the image format",
filename);
endif
warning ("imwrite: unlisted image format %s (see imformats). Trying to save anyway.",
ext);
__imwrite__ (varargin{:});
else
fmt.write (varargin{:});
endif
endfunction
## Test input validation
%!error <Invalid call> imwrite () # Wrong # of args
%!error <Invalid call> 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
%!function [r, cmap, a] = write_and_read (format, varargin)
%! filename = [tempname() format];
%! unwind_protect
%! imwrite (varargin{1}, filename, varargin{2:end});
%! [r, cmap, a] = imread (filename, "Index", "all");
%! unwind_protect_cleanup
%! unlink (filename);
%! end_unwind_protect
%!endfunction
## typical usage with grayscale uint8 images
%!testif HAVE_MAGICK
%! gray = randi (255, 10, 10, 1, "uint8");
%! r = write_and_read (".tif", gray);
%! assert (r, gray);
## grayscale uint8 images with alpha channel
%!testif HAVE_MAGICK
%! gray = randi (255, 10, 10, 1, "uint8");
%! alpha = randi (255, 10, 10, 1, "uint8");
%! [r, ~, a] = write_and_read (".tif", gray, "Alpha", alpha);
%! assert (r, gray);
%! assert (a, alpha);
## multipage grayscale uint8 images
%!testif HAVE_MAGICK
%! gray = randi (255, 10, 10, 1, 5, "uint8");
%! r = write_and_read (".tif", gray);
%! assert (r, gray);
## multipage RGB uint8 images with alpha channel
%!testif HAVE_MAGICK
%! gray = randi (255, 10, 10, 3, 5, "uint8");
%! alpha = randi (255, 10, 10, 1, 5, "uint8");
%! [r, ~, a] = write_and_read (".tif", gray, "Alpha", alpha);
%! assert (r, gray);
%! assert (a, alpha);
## typical usage with RGB uint8 images
%!testif HAVE_MAGICK
%! rgb = randi (255, 10, 10, 3, "uint8");
%! r = write_and_read (".tif", rgb);
%! assert (r, rgb);
## RGB uint8 images with alpha channel
%!testif HAVE_MAGICK
%! rgb = randi (255, 10, 10, 3, "uint8");
%! alpha = randi (255, 10, 10, 1, "uint8");
%! [r, ~, a] = write_and_read (".tif", rgb, "Alpha", alpha);
%! assert (r, rgb);
%! assert (a, alpha);
## multipage RGB uint8 images
%!testif HAVE_MAGICK
%! rgb = randi (255, 10, 10, 3, 5, "uint8");
%! r = write_and_read (".tif", rgb);
%! assert (r, rgb);
## multipage RGB uint8 images with alpha channel
%!testif HAVE_MAGICK
%! rgb = randi (255, 10, 10, 3, 5, "uint8");
%! alpha = randi (255, 10, 10, 1, 5, "uint8");
%! [r, ~, a] = write_and_read (".tif", rgb, "Alpha", alpha);
%! assert (r, rgb);
%! assert (a, alpha);
%!testif HAVE_MAGICK
%! gray = repmat (uint8 (0:255), 100, 1);
%! [g] = write_and_read (".jpeg", gray);
%! assert (g, gray, 2);
%!testif HAVE_MAGICK
%! gray = repmat (uint8 (0:255), 100, 1);
%! [g] = write_and_read (".jpeg", gray, "quality", 100);
%! assert (g, gray);
%!function compression = get_bmp_compression (ext, cmap = [], varargin)
%! gray = repmat (uint8 (0:255), 100, 1);
%! filename = [tempname() ext];
%! unwind_protect
%! if (isempty (cmap))
%! imwrite (gray, filename, varargin{1:end});
%! else
%! imwrite (gray, cmap, filename, varargin{1:end});
%! endif
%! fid = fopen (filename);
%! unwind_protect
%! compression = fread (fid, 31)(end);
%! unwind_protect_cleanup
%! fclose (fid);
%! end_unwind_protect
%! unwind_protect_cleanup
%! unlink (filename);
%! end_unwind_protect
%!endfunction
## BMP images must be saved uncompressed by default
%!testif HAVE_MAGICK <*45565>
%! assert (get_bmp_compression ("", [], "BMP"), 0);
%! assert (get_bmp_compression ("", [], "bmp"), 0);
%! assert (get_bmp_compression (".BMP"), 0);
%! assert (get_bmp_compression (".bmp"), 0);
%! assert (get_bmp_compression (".bmp", [], "bmp"), 0);
%! assert (get_bmp_compression ("", gray (256), "bmp"), 0);
%! assert (get_bmp_compression (".bmp", gray (256), "Compression", "rle"), 1);
|