Reading animated gif with imread() produces unexpected results

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.

 Accepted Answer

DGM
DGM on 31 Jul 2015
Edited: DGM on 29 Sep 2015
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

I'm using MATLAB R2018a and I'm still getting this bug. The colors in my gif are corrupted.
First things: You aren't doing something like
[inpict map]=imread('mycatgif.gif'); imshow(inpict(:,:,:,4),map)
are you? Using imread this way will only return the the first color table (the GCT), and the color table will typically be wrong for all subsequent frames in a multiframe gif. If this is the case, the above FEX tools for animated gifs should solve the problem or at least demonstrate one way to get the color tables for all frames.
Otherwise, more specific information about your usage would be necessary for help. I don't have access to 2018a, so I can't replicate anything directly.
Assuming none of the above applies, are you sure the behavior is the same? The mentioned bug causes LCT data to be erroneously read with a one-byte offset. This means that a single-frame gif, or the first frame in a multiframe gif will have correct color.
If the file conforms to the appropriate standards and you know the LCT data for the bad frames and can verify that they are being read with an erroneous offset, a bug report might be in order.
Perhaps a good way to test would be to use the first sample gif above. Use IMFINFO and see if the OBOE exists. The first color table begins with the triplet [0.0901960784313726,0.0588235294117647,0.0627450980392157], which equates to [17 0F 10] in hex. The second color table begins with [0.0862745098039216,0.0705882352941177,0.0745098039215686], which equates to [16 12 13] in hex. If indeed, the OBOE exists, the second color table will begin with [0.0705882352941177,0.0745098039215686,0.545098039215686], or [12 13 8B] in hex.
If the bug is the same and you're running linux, you might be able to use GIFREAD from the FEX link above as a workaround. There is an optional parameter that can be used to decompose the file before it is read. (e.g. inpict=gifread('mycatgif.gif','imagemagick');) Keep in mind that GIFREAD produces RGB image arrays instead of indexed image arrays. It can always be edited to produce indexed arrays.
You're right, I'm only pulling in the color table for the first frame and that's what gives the corrupted effect for the rest.
Sorry for being unaware, thank you.
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.

More Answers (0)

Categories

Asked:

DGM
on 30 Jul 2015

Edited:

DGM
on 17 Apr 2018

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!