MATLAB Answers

DGM
1

Reading animated gif with imread() produces unexpected results

Asked by DGM
on 30 Jul 2015
Latest activity Edited by DGM
on 17 Apr 2018
My attempt at using imread() to read an animated gif (itself created with imwrite()) produces incorrect output:
function outpict=gifreadbad(filepath)
% GIFREADBAD(FILEPATH)
% reads all frames of an animated gif into a 4-D RGB image array
[images map]=imread(filepath, 'gif','Frames','all');
s=size(images);
numframes=s(4);
outpict=zeros([s(1:2) 3 numframes],'uint8');
for n=1:1:numframes;
outpict(:,:,:,n)=ind2rgb8(images(:,:,:,n),map);
end
return
This results in the following image:
being read as this:
I don't know if I'm not correctly specifying how the colormap(s) should be handled or what. I have a feeling that the root of the problem may have something to do with the way imread() handles the tables for multiple image blocks. It seems to fetch the map corresponding to frame 1 only. Using imread() to read individual frames produces the same results.
For sake of full disclosure, the function which created the image uses:
for n=1:1:numframes;
[imind,cm]=rgb2ind(inarray(:,:,:,n),256);
if n==1;
imwrite(imind,cm,outname,'gif','DelayTime',delay,'Loopcount',inf);
else
imwrite(imind,cm,outname,'gif','DelayTime',delay,'WriteMode','append');
end
end
I've looked around here and and elsewhere and I haven't come across an explanation or a solution other than my workaround:
function outpict=gifread(filepath)
% GIFREAD(FILEPATH)
% reads all frames of an animated gif into a 4-D RGB image array
% method requires imagemagick for splitting the gif
% imread() cannot correctly read animated gifs
% it seems that imread() won't correctly read animated gifs
% dramatic errors occur in the indexed image
% map data may be truncated or malinterpreted?
% split the gif using imagemagick instead
system(sprintf('convert %s %%03d_gifreadtemp.gif',filepath));
[~,numframes]=system('ls -1 *gifreadtemp.gif | wc -l');
numframes=str2num(numframes);
[image map]=imread('000_gifreadtemp.gif', 'gif');
s=size(image);
outpict=zeros([s(1:2) 3 numframes],'uint8');
for n=1:1:numframes;
[image map]=imread(sprintf('%03d_gifreadtemp.gif',n-1), 'gif');
outpict(:,:,:,n)=ind2rgb8(image,map);
end
system('rm *gifreadtemp.gif');
return
Using external resources to split the file works. Imread() is able to read the individual files without issue. Since the indexed-rgb conversion works fine, this leads me to suspect that the only place my error could lie is in the call to imread() in the case of the animated file.
While the above workaround is okay to demonstrate the concept, I'd like to avoid using imagemagick as it makes the function non-portable and none of the students will be able to use it.
This is 2009b on my lab computer (linux). I have not been able to get a chance to try it on a newer version or a different environment.
UPDATE: I double-checked and verified my assumption that the image data is identical between the two methods. The problem lies in the color tables.
It seems that imread(...'frames','all') returns only the GCT from the target file. After figuring out that imfinfo() exists, I attempted to use it to read in all the color tables from the target file, only to discover that all of the returned tables were incorrect (except for the first one, of course).
Upon closer inspection, the color tables returned by imfinfo() are shifted by one complete byte. This is strange, since as far as I can tell, imfinfo() is reading every other portion of the file correctly.
There does not appear to be any extraneous data in the file, and the structure is as would be expected from both the file format documentation and the image information available from imfinfo() and giftrans.
For example:
*USING HEXEDIT:*
logical screen descriptor and first/last triplets from GCT:
20 01 : width=288
00 02 : height=512
F7 : 1111 0111 : GCT=true, 8-bit, sort=false, size(GCT)=256
00 00 : BG=0, aspect=0
17 0F 10
96 3F 4D
...
2A 28 25
21 FF 0B : application extension, 11 bytes (netscape 2.0)
terminates in a zero byte (correct)
image descriptor and first/last triplets from LCT1:
2C 00 00 00 00 20 01 00 02 : same stuff as before
87 : 1000 0111 : LCT=true, interlace,sort=false,
reserved:00, size(LCT)=256
16 12 13
8B 48 4F
...
A7 A4 9C
08 FE 00 B9 : image data ...
*USING IMFINFO:*
triplets from map(:,:,[1:3 end]):
17 0F 10
96 3F 4D
...
2A 28 25
12 13 8B
48 4F 8E
...
A4 9C 08
Given that imfinfo() can read everything else correctly, even the bytes immediately adjacent to the LCT block, I'm wondering if this is a bug.
I haven't been able to find a gif file which does not produce this behavior, and I'm going to see if I can't try this on a newer version. If there is something obvious I'm missing or a bug report I haven't found, any help would be appreciated.
UPDATE AGAIN: As soon as I had written that, I found this bug report. Seems it was a bug. That pretty much wraps everything up nicely.

  2 Comments

As I mentioned, this was in 2009b, which is subject to bug 813126. I should have thought to double check for bug reports after I created my mathworks account to ask this question. Bug reports aren't viewable without an account, so googling for them won't help.
For what it's worth, the fix attached to the bug report worked.

Sign in to comment.

1 Answer

Answer by DGM
on 31 Jul 2015
Edited by DGM
on 29 Sep 2015
 Accepted Answer

To anyone who googled this, it's an old (fixed) bug . Closing question.
The final implementation of the read/write functions for animated gifs are on the FEX here .

  6 Comments

That FEX tool uses function `ind2rgb8`, which I get is supposed to be ind2rgb8 with a uint8 type casting.
My version of MATLAB doesn't seem to have `ind2rgb8` so I just swapped it to `ind2rgb` since the image and LCT were data type uint8 anyways.
It outputs just garbage RGB values for all 3 channels, absolute 0 or 1 for all 3. So the image array just has all frames of pure black.
Is that because of the ind2rgb instead of ind2rgb8?
Ah, nevermind, got it working...wasn't casting right somehow
Yeah, the empty image array is specified as an integer class, so populating it with FP data with a range of [0 1] will do that. I suppose I should add this to my list of MIMT tools that I need to update. Glad you got it working.
EDIT: I just realized that IND2RGB expects a map of class 'double'. This results in some additional unexpected casting issues.

Sign in to comment.