LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-09-2017, 05:14 PM   #1
keirvt
Member
 
Registered: Sep 2006
Location: Sydney Australia
Distribution: fedora/Ubuntu
Posts: 123

Rep: Reputation: 17
ImageMagick Remove differences


I have a series of images of an object taken on a tripod. People kept moving in and out of the pictures. I would like to take this time series set of pictures and remove the differences.

The command "convert -average" almost does this but leaves "ghosts" of the objects that move in an out of the photo.

How can I preserve those parts of the photo that are constant, removing the changed parts
 
Old 08-09-2017, 07:56 PM   #2
jlinkels
Senior Member
 
Registered: Oct 2003
Location: Bonaire
Distribution: Debian Wheezy/Jessie/Stretch/Sid, Linux Mint DE
Posts: 4,604

Rep: Reputation: 686Reputation: 686Reputation: 686Reputation: 686Reputation: 686Reputation: 686
It's a long shot, but take a look at http://www.fmwconcepts.com/imagemagi...ilar/index.php

The SSIM script seems to be able to create an image showing the dissimilarities in two images. Maybe you can take it from there with some boolean functions.

jlinkels
 
Old 08-10-2017, 04:48 PM   #3
keirvt
Member
 
Registered: Sep 2006
Location: Sydney Australia
Distribution: fedora/Ubuntu
Posts: 123

Original Poster
Rep: Reputation: 17
ssim

Fred's scripts often need a bit of work. I gave it a go and the script crashes for a variety of reasons. I may look at it some more if I run out of option. anyway thanks for the suggestion.
 
Old 08-10-2017, 08:40 PM   #4
Sefyir
Member
 
Registered: Mar 2015
Distribution: Linux Mint
Posts: 517

Rep: Reputation: 236Reputation: 236Reputation: 236
Python3 Program

This struck me as a interesting problem, so I wrote a python script to do the following:
  • Take a set of images
  • For each image, get color information for pixel 0,0.
  • Find the most common one.
  • Repeat for every other pixel.
  • Write most common pixels to array and write out as a image.

That's all it does, so it's pretty dumb for "interpreting" a image. I've never written a image manipulation program before so consider this experimental
It works great for controlled images (like screenshots) but left ghosts of images with my camera.. Probably because even though things seem mostly similar.. they do change a little.
The more pictures of what you want will likely get what you want.. probably. I'd also suggest processing it as minimally as possible into something with lossless compression.
You will need PIL and numpy installed and works with tiff, png and will probably work with most image formats (whatever PIL recognizes)

EDIT: See the below post for a better implementation of this code.

Code:
$ ./image_difference.py -o combined_image.tiff my_images/*.tiff

Code:
#!/usr/bin/env python3
# https://www.linuxquestions.org/questions/programming-9/imagemagick-remove-differences-4175611638/#post5746930
# Author: Sefyir

import sys
import argparse

from collections import Counter

import numpy as np
from PIL import Image

def load_image(infilename):
    with Image.open(infilename) as img:
        img.convert('L')
        return np.array(img)

def save_image(image_array, save_location):
    image = Image.fromarray(image_array)
    image.save(save_location)
    print('Saved to {}'.format(save_location))

def file_bit_array_func(array):
    '''Get location and array for each pixel'''
    return ((row_index, column_index, pixel_array)
            for row_index, row in enumerate(array)
            for column_index, pixel_array in enumerate(row)
           )
def create_bit_list(image_arrays):
    '''
    Find most common pixel in image array
    All images must have identical dimensions
    '''
    for image_array in zip(*image_arrays):
        row_index = image_array[0][0]
        column_index = image_array[0][1]
        bit_array_most_common = Counter((tuple(image[2]) for image in image_array))
        # Frequency of most common color values.
        # If most common is 1, all were different.
        # Find median of each color value
        if bit_array_most_common.most_common(1)[0][1] == 1:
            image_info_list = (tuple(pixel_info[2]) for pixel_info in image_array)
            median_array = tuple(np.median(pixel_zipped_array) 
                            for pixel_zipped_array in zip(*image_info_list))
            bit_array_most_common = np.array([int(num) for num in median_array], dtype='uint8')
        else:
            bit_array_most_common = np.array(bit_array_most_common.most_common(1)[0][0], dtype='uint8')
        
        yield (row_index, column_index, bit_array_most_common)

parser = argparse.ArgumentParser('Merge Common Pictures')
parser.add_argument('-o', '--output')
args, other_args = parser.parse_known_args()

image_arrays = (file_bit_array_func(load_image(_file))
                for _file in other_args)

# Grab image array to serve as template for each row / column to change.
with Image.open(other_args[0]) as image_file:
    image_file.convert('L')
    source_image_array = np.array(image_file)

# Write most common values to source image array
for row_index, column_index, bit_array in create_bit_list(image_arrays):
    source_image_array[row_index][column_index] = bit_array

save_image(source_image_array, args.output)

Last edited by Sefyir; 08-18-2017 at 09:20 PM.
 
3 members found this post helpful.
Old 08-11-2017, 09:55 PM   #5
Sefyir
Member
 
Registered: Mar 2015
Distribution: Linux Mint
Posts: 517

Rep: Reputation: 236Reputation: 236Reputation: 236
I updated it to now be able to handle more diversity in images.
It will check each pixel and find the most common, if there is no most common (each one occurs once) then it finds the median of the colors (not the average!)

It worked well in the experiment I ran The camera tilted a bit in one of them which you can notice. Examine the links to see the source 4 images and the resultant image.
Note it is not fast, I ran with 5 RAW 4k images with a neutral profile converted to 8-bit uncompressed tiffs and it took 14min, 50s to complete (about 3min per image).
Smaller / fewer images will of course go faster.

I might optimize it later

https://i.imgur.com/IlrPaVQ.jpg
https://i.imgur.com/ueosUie.jpg
 
1 members found this post helpful.
Old 08-12-2017, 03:54 PM   #6
keirvt
Member
 
Registered: Sep 2006
Location: Sydney Australia
Distribution: fedora/Ubuntu
Posts: 123

Original Poster
Rep: Reputation: 17
Python Remove Differences

This program could be extremely useful.
Imagemagick is great but each command is pretty much a black box.
A Python program is something to which I can do my own mods.

My problem is the program crashes in "save format = EXTENSION[ext]"
My images are jpeg images, fairly large 6M. Can you suggest what is causing this.

Sefyir, you say you have updated to handle a greater diversity in images but where is the update?

The code appears well written but there is not a single line of comments.
This makes it extra work for me not being au fait with Python's PIL.

I can learn but a few clues in the code is very helpful. ( What does zip(*image) do?)

What may be needed to help the program is a means of aligning the pixels across two or more images. I think Imagemagick has something but it would be best to have it in this code.

Anyway its great to see someone this enthusiastic. This could help me a great deal when it works/
 
Old 08-13-2017, 09:47 PM   #7
Sefyir
Member
 
Registered: Mar 2015
Distribution: Linux Mint
Posts: 517

Rep: Reputation: 236Reputation: 236Reputation: 236
Python3 Program

Quote:
My problem is the program crashes in "save format = EXTENSION[ext]"
My images are jpeg images, fairly large 6M. Can you suggest what is causing this.
A traceback or actual error would be good. Trial converting to a uncompressed non lossy format (like tif or png) and try again?

Quote:
The code appears well written but there is not a single line of comments.
This makes it extra work for me not being au fait with Python's PIL.
I was writing in a cycle of coding and testing. Unfortunately, not the best style for good comments. There is a style where the variables and method names should describe what the code is doing as well as comments, but the above code is a example of where I did very poorly at that.
I went over it to make it read better... However a idea occurred to me that rendered much of my code redundant. Before I get to that, I'll address this:

Quote:
What does zip(*image) do?
This made it possible to take a list of images converted to arrays, and iterate over the first pixel in each image simultaneously. Assume Image A was a single pixel of white, it would be (255, 255, 255). If image 2 was black, (0, 0, 0) and Image 3 black, (0, 0, 0)
Then you could do this:

Code:
a = ((255, 255, 255), (0, 0, 0), (0, 0, 0))
list(zip(*a))
[(255, 0, 0), (255, 0, 0), (255, 0, 0)]
As you can see, the first of each was in the first tuple, the second of each in the second tuple.. so forth.

Ok, this new code is now a class (I re-factored) and now leverages the arrays to a greater amount of power.
Specifically, I use numpy to stack the 3 dimensional arrays on top of each other and then..
Calculate the median of values in the 4th dimension (or axis?). Or.. something like that . Anyways, side bonus of instead of a 15-minute wait, it's now about 5s as well as much simpler to understand!



Code

EDIT:
The code is fully classes, you just need to inherit ImageArray and define _process_image and you can have it do whatever you want with the stacked images. Feel free to look at ImageMedian and ImageMean as examples
Any modification would benefit best from vectorized calculations on the array, but it's not required since you can use zip and loops to simulate that (albeit much slower!)

Code:
#!/usr/bin/env python3

import sys
import argparse

import numpy as np
from PIL import Image

class ImageArray():
    '''
    Load each image into a array, stack on top on other image arrays
    and run _process_image
    '''
    def __init__(self, images):
        self.images = (self._load_image(image)
                       for image in images)
        self.stacked_image_arrays = np.stack(self.images)
        self.flattened_image_array = self._process_image(self.stacked_image_arrays)

    def _load_image(self, _file):
        with Image.open(_file) as img:
            return np.array(img)

    def save_image(self, save_location):
        image = Image.fromarray(self.flattened_image_array.astype('uint8'))
        image.save(save_location)
        print(f'Saved to {save_location}')

    def _process_image(self):
        pass


class ImageMedian(ImageArray):
    '''Collapse image array into median of each pixel'''
    def __init__(self, images):
        super().__init__(images)

    def _process_image(self, stacked_image_arrays):
        if len(stacked_image_arrays) < 3:
            raise ValueError('Requires 3 or more images')
        flattened_median_image_array = np.median(stacked_image_arrays, axis=0)
        return flattened_median_image_array


class ImageMean(ImageArray):
    '''Collapse image array into mean of each pixel'''
    def __init__(self, images):
        super().__init__(images)

    def _process_image(self, stacked_image_arrays):
        flattened_mean_image_array = np.mean(stacked_image_arrays, axis=0)
        return flattened_mean_image_array


if __name__ == '__main__':
    parser = argparse.ArgumentParser('Merge Common Pictures')
    parser.add_argument('-o', '--output',
            default='output.jpg')
    args, source_images = parser.parse_known_args()

    DemoImages = ImageMedian(source_images)
    #DemoImages = ImageMean(source_images)
DemoImages.save_image(args.output)

Last edited by Sefyir; 08-23-2017 at 06:10 PM.
 
Old 08-19-2017, 04:48 PM   #8
keirvt
Member
 
Registered: Sep 2006
Location: Sydney Australia
Distribution: fedora/Ubuntu
Posts: 123

Original Poster
Rep: Reputation: 17
Pretty good

Putting a camera on tripod and taking four images of a bowl of fruit with a banana that moves in front of the bowl. Original images had a much large pixel resolution but here are attached, reduced eight times to be a little less of a space hog. Running those images through the last program you supplied renders the jpeg image ofile.jpg The result almost removes the banana entirely.

That's a good result.

For your interest I am a keen scuba diver. I am making a composite photograph taking sections along the length of a shipwreck and stitching the parts together to make a very large high res picture. The trouble is (was) that fish kept getting in the way and spoiling the stitch. With this program I can do a time series on each stitch and remove the fish, then do the stitch.
Attached Thumbnails
Click image for larger version

Name:	pic1.jpg
Views:	10
Size:	173.1 KB
ID:	25738   Click image for larger version

Name:	pic2.jpg
Views:	8
Size:	177.7 KB
ID:	25739   Click image for larger version

Name:	pic3.jpg
Views:	7
Size:	179.5 KB
ID:	25740   Click image for larger version

Name:	pic4.jpg
Views:	8
Size:	179.1 KB
ID:	25741   Click image for larger version

Name:	ofile.jpg
Views:	8
Size:	18.5 KB
ID:	25742  

 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] ImageMagick dmchess Linux - Software 4 03-22-2014 06:43 PM
install ImageMagick-6.5.1-2 & ImageMagick-devel-6.5.1.2 in red hat mokkai Linux - Enterprise 4 04-16-2009 12:04 PM
Imagemagick barn63 Linux - Software 3 11-14-2006 02:44 PM
ImageMagick without X jeru Debian 4 08-21-2004 07:29 PM
ImageMagick! ifm Linux - Software 0 06-20-2002 09:13 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 09:59 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration