0

I'm working on a program which applies a median blur onto a multiframe tiff file. My goal is to filter through every frame in the tiff sequence and then save the result as the same sequence, just filtered. However, anytime I run it, it only saves the last frame, as I don't know how to properly save the data into a separate sequence as it runs.

#takes .tiff, loads it into PIL, converts to greyscale, sets attributes in PIL form
im = Image.open('example_recording.tif').convert('L')
im.save('greyscale_example.tif')

width,height = im.size
image_lookup = 0

#creates class used to de-sequence the animation


class ImageSequence:
    def __init__(self, im):
        self.im = im
    def __getitem__(self, ix):
        try:
            if ix:
                self.im.seek(ix)
            return self.im
        except EOFError:
            raise IndexError # end of sequence; needed to avoid process from unecessary breaking once it runs to the end of the tiff file animation


for frame in ImageSequence(im):
            imarray = np.array(frame)
            Blur = cv2.medianBlur(imarray,5)
            im = Image.fromarray(Blur)

im.save('corrected.tif')
#(saves actually only the last frame of the movie, med-corrected)

On Andrews advice, the code was modified to look like this:

im = Image.open('example_recording.tif')
width,height = im.size
image_lookup = 0
n = 1
while True:
    try:
        im.seek(n)
        n = n+1
    except EOFError:
        print "length is",  n
        break;
    #this solves the length issue as ImageSequence doesnt have _len_ attribute
class ImageSequence:
    def __init__(self, im):
        self.im = im
    def __getitem__(self, ix):
        try:
            if ix:
                self.im.seek(ix)
            return self.im
        except EOFError:
            raise IndexError
depth = n
target_array = np.zeros((width, height, depth))
for index, frame in enumerate(ImageSequence(im)):
    imarray = np.array(frame)
    Blur = cv2.medianBlur(imarray,5)
    print type(Blur)
    im = Image.fromarray(Blur)
    im.save('corrected_{}.tif'.format(index))
    print n

So now it works perfectly fine!

Jericho Jones
  • 157
  • 1
  • 2
  • 11
  • 1
    Could you not create a 3rd dimension for the array from `len(im)`, and then put the results of the blur into that, then iterate through the 3rd dimension of the array and save each image that way? – Andrew Aug 30 '16 at 21:23
  • Thank you, that sounds very reasonable, but how would I go about that though? Still being very green to python and while I can understand the theory behind the solution, don't know how to implement it. – Jericho Jones Aug 30 '16 at 21:28
  • Are you trying to extract the blurred image as an animation, or as a series of stills? – Andrew Aug 30 '16 at 21:33
  • As an animation, in order to get rid of noise to further process the image – Jericho Jones Aug 30 '16 at 21:35
  • How about sharing your TIF file? – Mark Setchell Aug 30 '16 at 21:48
  • The thing is pretty hefty, but why not; this one is already greyscale, but should do as far as the code goes http://www.filedropper.com/examplerecording – Jericho Jones Aug 30 '16 at 22:22
  • I'm actually surprised PIL even knows how to deal with multi-frame files. I believe your problem is when you do `im = ...` since this replaces the original multi-frame `im` with a single frame. – Mark Ransom Aug 30 '16 at 22:34
  • yep, i'm well aware of that, however I lack the python syntax knowledge to make a result of every frame conversion save into a different class / override existing one (as it goes one after one and ends up with last one which it considers its final output as opposed to ideal case, when it would save them in an order it ran through them) – Jericho Jones Aug 30 '16 at 22:37
  • Take a look at this: [link](http://stackoverflow.com/questions/18602525/python-pil-for-loop-to-work-with-multi-image-tiff) – Andrew Aug 31 '16 at 16:30

1 Answers1

1
depth = len(ImageSequence(im))
target_array = np.zeros((width, height, depth))
for index, frame in enumerate(ImageSequence(im)):
    imarray = np.array(frame)
    Blur = cv2.medianBlur(imarray,5)
    target_array[:, :, index] = Blur

That gives you a nice array of matrices, I would think you'll have to reverse everything you've done to pull your images out, but I'm not a PIL expert.

EDIT:

for index, frame in enumerate(ImageSequence(im)):
    imarray = np.array(frame)
    Blur = cv2.medianBlur(imarray,5)
    im = Image.fromarray(Blur)
    im.save('corrected_{}.tif'.format(index))

This should give you an image for each loop, at least.

Andrew
  • 1,072
  • 1
  • 7
  • 15
  • Thank you very much. How would I go about setting up length in the ImageSequence though? what is it i should be returning if i am setting it up in the class itself? – Jericho Jones Aug 30 '16 at 22:07
  • You shouldn't need to, `len` is a python builtin that will give you the size of any iterable, I've just checked and it should work for a generator, depending on what `ImageSequence` is – Andrew Aug 30 '16 at 22:38
  • I've been playing with it a little bit, and it looks like your Image is only being imported as a single 464 x 264 image. Is there a reason you're not using the ImageSequence.Iterator, rather than creating a custom class? If I'm doing anything with 2D images like you are, I usually have `import matplotlib.pyplot as plt` and `plt.imshow(im)` in order to test what's going on with anything I'm working with. – Andrew Aug 31 '16 at 00:15
  • I was trying before with ImageSequence.Iterator, but was running into pretty much the same problem (because eventually, as I do not know better I had to convert from array into the image in a way like im=Image.fromarray), again allowing for playing around with only one frame – Jericho Jones Aug 31 '16 at 14:05
  • The problem I am seeing is that the result of `im.size` is a single 2-tuple, which would normally be interpreted as a single image. It seems as though in the importing process, the multiple layers are not being preserved. If you put `count = 0` before your last for loop, and then `count += 1` inside it, what is the value of `count` after the loop is done? I suspect it's only running once, and that's why you're only saving one image. If it's running more than once, then you could put `im.save("etc")` into the loop and use some string formatting to vary the filename – Andrew Aug 31 '16 at 14:26
  • Just checked your for loop, and it does indeed only iterate once, so your code up to that point has only extracted a single image. – Andrew Aug 31 '16 at 16:26
  • I tried implementing some of these changes and ran into few problems (apparently, image sequences do not have length attribute, so I used a wee workaround for that ), but upon trying to make the loop iterate over and over the sequence, it iterates over one image only, albeit length amount of times. Furthermore, attempt to go from array to image is met with a problem: 'im = Image.fromarray(Blur) File "C:\Anaconda2\lib\site-packages\PIL\Image.py", line 2167, in fromarray raise TypeError("Cannot handle this data type") TypeError: Cannot handle this data type' see edit for code – Jericho Jones Aug 31 '16 at 20:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122344/discussion-between-andrew-and-jericho-jones). – Andrew Aug 31 '16 at 20:40