Mercurial > octave-antonio
annotate scripts/image/imformats.m @ 16906:bfad37d33435
Connect imfinfo with imformats.
* private/core_imfinfo.m: new function. Old code from imfinfo()
moved here so that imformats() can create function handle to
this part of the code only otherwise imfinfo() would get stuck
into an endless loop, calling itself while respecting imformats()
configuration.
* private/imageIO.m: new function. Responsible for connecting the
image IO functions with imformats(), and calling the correct
function. Will later also be used by imread and imwrite.
* imfinfo.m: reduced to minimum input check, until finding
filename. Passes all arguments to new function imageIO().
* imformats.m: change calls to imfinfo() to the new core_imfinfo().
author | Carnë Draug <carandraug@octave.org> |
---|---|
date | Sat, 06 Jul 2013 04:28:04 +0100 |
parents | 861516dcad19 |
children | 04f4f067eb33 |
rev | line source |
---|---|
16901 | 1 ## Copyright (C) 2013 Carnë Draug |
2 ## | |
3 ## This file is part of Octave. | |
4 ## | |
5 ## Octave is free software; you can redistribute it and/or modify it | |
6 ## under the terms of the GNU General Public License as published by | |
7 ## the Free Software Foundation; either version 3 of the License, or (at | |
8 ## your option) any later version. | |
9 ## | |
10 ## Octave is distributed in the hope that it will be useful, but | |
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 ## General Public License for more details. | |
14 ## | |
15 ## You should have received a copy of the GNU General Public License | |
16 ## along with Octave; see the file COPYING. If not, see | |
17 ## <http://www.gnu.org/licenses/>. | |
18 | |
19 ## -*- texinfo -*- | |
20 ## @deftypefn {Function File} {} imformats () | |
21 ## @deftypefnx {Function File} {@var{formats} =} imformats (@var{ext}) | |
22 ## @deftypefnx {Function File} {@var{formats} =} imformats (@var{format}) | |
23 ## @deftypefnx {Function File} {@var{formats} =} imformats ("add", @var{format}) | |
24 ## @deftypefnx {Function File} {@var{formats} =} imformats ("remove", @var{ext}) | |
25 ## @deftypefnx {Function File} {@var{formats} =} imformats ("update", @var{ext}, @var{format}) | |
26 ## @deftypefnx {Function File} {@var{formats} =} imformats ("factory") | |
27 ## Manage supported image formats. | |
28 ## | |
29 ## @var{formats} is a structure with information about each supported file | |
30 ## format, or from a specific format @var{ext}, the value displayed on the | |
31 ## field @code{ext}. It contains the following fields: | |
32 ## | |
33 ## @table @asis | |
34 ## @item ext | |
35 ## The name of the file format. This may match the file extension but Octave | |
36 ## will automatically detect the file format. | |
37 ## @item description | |
38 ## A long description of the file format. | |
39 ## @item isa | |
40 ## A function handle to confirm if a file is of the specified format. | |
41 ## @item write | |
42 ## A function handle to write if a file is of the specified format. | |
43 ## @item read | |
44 ## A function handle to open files the specified format. | |
45 ## @item info | |
46 ## A function handle to obtain image information of the specified format. | |
47 ## @item alpha | |
48 ## Logical value if format supports alpha channel (transparency or matte). | |
49 ## @item multipage | |
50 ## Logical value if format supports multipage (multiple images per file). | |
51 ## @end table | |
52 ## | |
53 ## It is possible to change the way Octave manages file formats with the options | |
54 ## @code{"add"}, @code{"remove"}, and @code{"update"}, and supplying a | |
55 ## structure @var{format} with the required fields. The option | |
56 ## @code{"factory"} resets the configuration to the default. | |
57 ## | |
58 ## This can be used by Octave packages to extend the image reading capabilities | |
59 ## Octave, through use of the PKG_ADD and PKG_DEL commands. | |
60 ## | |
61 ## @seealso{imfinfo, imread, imwrite} | |
62 ## @end deftypefn | |
63 | |
64 ## Author: Carnë Draug <carandraug@octave.org> | |
65 | |
66 function varargout = imformats (arg1, arg2, arg3) | |
67 if (nargin > 3) | |
68 print_usage (); | |
69 endif | |
70 | |
71 persistent formats = default_formats (); | |
72 | |
73 if (nargin == 0 && nargout == 0) | |
74 error ("imformats: pretty print not yet implemented."); | |
75 elseif (nargin >= 1) | |
76 if (isstruct (arg1)) | |
77 arrayfun (@is_valid_format, arg1); | |
78 ## FIXME: what is the return value in this situation? | |
79 formats = arg1; | |
80 | |
81 elseif (ischar (arg1)) | |
82 switch (tolower (arg1)) | |
83 case "add", | |
84 if (! isstruct (arg2)) | |
85 error ("imformats: FORMAT to %s must be a structure.", arg1); | |
86 endif | |
87 arrayfun (@is_valid_format, arg2); | |
88 formats(end + numel (b)) = arg2; | |
89 varargin{1} = formats; | |
90 | |
91 case {"remove", "update"}, | |
92 if (! ischar (arg2)) | |
93 error ("imformats: EXT to %s must be a string.", arg1); | |
94 endif | |
95 ## FIXME: suppose a format with multiple extensions. If one of | |
96 ## them is requested to be removed, should we remove the | |
97 ## whole format, or just that extension from the format? | |
98 match = find_ext_idx (formats, arg2) | |
99 if (! any (match)) | |
100 error ("imformats: no EXT `%s' found.", arg2); | |
101 endif | |
102 if (strcmpi (arg1, "remove")) | |
103 formats(match) = []; | |
104 else | |
105 ## then it's update | |
106 if (! isstruct (arg3)) | |
107 error ("imformats: FORMAT to update must be a structure."); | |
108 endif | |
109 is_valid_format (arg3); | |
110 formats(match) = arg3; | |
111 endif | |
112 varargin{1} = formats; | |
113 | |
114 case "factory", | |
115 formats = default_formats (); | |
116 otherwise | |
117 ## then we look for a format with that extension. | |
118 match = find_ext_idx (formats, arg1) | |
119 ## For matlab compatibility, if we don't find any format we must | |
120 ## return an empty struct with NO fields. We can't use match as mask | |
121 if (any (match)) | |
122 varargin{1} = formats(match); | |
123 else | |
124 varargin{1} = struct (); | |
125 endif | |
126 endswitch | |
127 else | |
128 error ("imformats: first argument must be either a structure or string."); | |
129 endif | |
130 else | |
131 varargout{1} = formats; | |
132 endif | |
133 endfunction | |
134 | |
135 function formats = default_formats () | |
136 | |
137 ## The available formats are dependent on what the user has installed at | |
138 ## a given time, and how GraphicsMagick was built. Checking for | |
139 ## GraphicsMagick features when building Octave is not enough since it | |
140 ## delegates some of them to external programs which can be removed or | |
141 ## installed at any time. | |
142 ## The recommended method would be to use CoderInfoList() to get a list of | |
143 ## all available coders and try to write and read back a small test image. | |
144 ## But this will not work since some coders are readable or writable only. | |
145 ## It will still fail if we test only the ones marked as readable and | |
146 ## writable because some RW coders are not of image formats (NULL, 8BIM, | |
147 ## or EXIF for example). | |
148 ## So we'd need a blacklist (unacceptable because a `bad' coder may be | |
149 ## added later) or a whitelist. A whitelist means that even with a | |
150 ## super-fancy recent build of GraphicsMagick, some formats won't be listed | |
151 ## by imformats but in truth, we will still be able to read and write them | |
152 ## since imread() and imwrite() will give it a try anyway. | |
153 ## | |
154 ## For more info and comments from the GraphicsMagick main developer, see | |
155 ## http://sourceforge.net/mailarchive/forum.php?thread_name=alpine.GSO.2.01.1304301916050.2267%40freddy.simplesystems.org&forum_name=graphicsmagick-help | |
156 | |
157 persistent formats = struct ( "coder", {}, | |
158 "ext", {}, | |
159 "isa", {}, | |
160 "info", {}, | |
161 "read", {}, | |
162 "write", {}, | |
163 "alpha", {}, | |
164 "description", {}, | |
165 "multipage", {}); | |
166 | |
167 ## Image IO abilities won't change during the same Octave session, | |
168 ## there's no need to go and calculate it all over again if we are | |
169 ## requested to reset back to factory. | |
170 if (! isempty (formats)) | |
171 return | |
172 endif | |
173 | |
174 ## Building the formats info | |
175 ## | |
176 ## As mentioned above we start with a whitelist of coders. Since the | |
177 ## GraphicsMagick build may be missing some coders, we will remove those | |
178 ## from the list. Some info can be obtained directly from GraphicsMagick | |
179 ## through the CoderInfo object. However, some will need to be hardcoded. | |
180 ## | |
181 ## The association between file extensions and coders needs to be done | |
182 ## with a manually coded list (file extensions do not define the image | |
183 ## format and GraphicsMagick will not be fooled by changing the extension). | |
184 ## | |
185 ## We can get the read, write, description and multipage fields from | |
186 ## CoderInfo in C++. We should do the same for alpha (GraphicsMagick | |
187 ## calls it matte) but it's not available from CoderInfo. The only way to | |
188 ## check it is to create a sample image with each coder, then try to read | |
189 ## it back with GraphicsMagick and use the matte method on the Image class. | |
190 ## But making such test for each Octave session... meh! While technically | |
191 ## it may be possible that the same coder has different support for alpha | |
192 ## channel in different versions and builds, this doesn't seem to happen. | |
193 ## So we also hardcode those. In the future, maybe the CoderInfo class will | |
194 ## have a matte method like it does for multipage. | |
195 ## | |
196 ## Other notes: some formats have more than one coder that do the same. For | |
197 ## example, for jpeg images there is both the JPG and JPEG coders. However, | |
198 ## it seems that when reading images, GraphicsMagick only uses one of them | |
199 ## and that's the one we list (it's the one reported by imfinfo and that we | |
200 ## can use for isa). However, in some cases GraphicsMagick seems to rely | |
201 ## uniquely on the file extension ((JBIG and JBG at least. Create an image | |
202 ## with each of those coders, swap their extension and it will report the | |
203 ## other coder). We don't have such cases on the whitelist but if we did, we | |
204 ## would need two entries for such cases. | |
205 | |
206 ## each row: 1st => Coder, 2nd=> file extensions, 3rd=> alpha | |
207 coders = {"BMP", {"bmp"}, true; | |
208 "CUR", {"cur"}, false; | |
209 "GIF", {"gif"}, true; | |
210 "ICO", {"ico"}, true; | |
211 "JBG", {"jbg"}, false; | |
212 "JBIG", {"jbig"}, false; | |
213 "JP2", {"jp2", "jpx"}, true; | |
214 "JPEG", {"jpg", "jpeg"}, false; # there is also a JPG coder | |
215 "PBM", {"pbm"}, false; | |
216 "PCX", {"pcx"}, true; | |
217 "PGM", {"pgm"}, false; | |
218 "PGM", {"pgm"}, false; | |
219 "PNG", {"png"}, true; | |
220 ## PNM is a family of formats supporting portable bitmaps (PBM), | |
221 ## graymaps (PGM), and pixmaps (PPM). There is no file format | |
222 ## associated with pnm itself. If PNM is used as the output format | |
223 ## specifier, then GraphicsMagick automatically selects the most | |
224 ## appropriate format to represent the image. | |
225 "PNM", {"pnm"}, true; | |
226 "PPM", {"ppm"}, false; | |
227 "SUN", {"ras"}, true; # SUN Rasterfile | |
228 "TGA", {"tga", "tpic"}, true; | |
229 "TIFF", {"tif", "tiff"}, true; | |
230 "XBM", {"xbm"}, false; | |
231 "XPM", {"xpm"}, true; | |
232 "XWD", {"xwd"}, false; | |
233 }; | |
234 | |
235 for fidx = 1: rows(coders) | |
236 formats(fidx).coder = coders{fidx, 1}; | |
237 formats(fidx).ext = coders{fidx, 2}; | |
238 formats(fidx).alpha = coders{fidx, 3}; | |
239 ## default isa is to check if the format returned by imfinfo is the coder | |
240 formats(fidx).isa = @(x) isa_magick (coders{fidx,1}, x); | |
241 endfor | |
242 | |
243 ## the default info, read, and write functions | |
16906
bfad37d33435
Connect imfinfo with imformats.
Carnë Draug <carandraug@octave.org>
parents:
16901
diff
changeset
|
244 [formats.info ] = deal (@core_imfinfo); |
16901 | 245 [formats.read ] = deal (@imread); |
246 [formats.write] = deal (@imread); | |
247 | |
248 ## fills rest of format information by checking with GraphicsMagick | |
249 formats = __magick_formats__ (formats); | |
250 endfunction | |
251 | |
252 function is_valid_format (format) | |
253 ## the minimal list of fields required in the structure. We don't | |
254 ## require multipage because it doesn't exist in matlab | |
255 min_fields = {"ext", "read", "isa", "write", "info", "alpha", "description"}; | |
256 fields_mask = cellfun (@(x) isfield (format, x), min_fields); | |
257 if (! all (fields_mask)) | |
258 error ("imformats: structure has missing field `%s'.", min_fields(! fields_mask){1}); | |
259 endif | |
260 endfunction | |
261 | |
262 function match = find_ext_idx (formats, ext) | |
263 ## FIXME: is matlab sensitive to file extensions? | |
264 ## XXX: what should we do if there's more than one hit? | |
265 ## Should this function prevent the addition of | |
266 ## duplicated extensions? | |
267 match = cellfun (@(x) any (strcmp (x, ext)), {formats.ext}); | |
268 endfunction | |
269 | |
270 function bool = isa_magick (coder, filename) | |
271 bool = false; | |
272 try | |
16906
bfad37d33435
Connect imfinfo with imformats.
Carnë Draug <carandraug@octave.org>
parents:
16901
diff
changeset
|
273 info = core_imfinfo (filename); |
16901 | 274 bool = strcmp (coder, info.Format); |
275 end_try_catch | |
276 endfunction |