Making a long image without resizing - image

I need to put many images together side by side but without changing the height or width of any of them. That is to say, it will just be one image of a constant height but very long width as the image are sitting horizontally.
I've been using Python and the PIL library but what I've tried so far is producing an image that makes all the images smaller to concatenate into one long image.
Image.MAX_IMAGE_PIXELS = 100000000 # For PIL Image error when handling very large images
imgs = [ Image.open(i) for i in list_of_images ]
widths, heights = zip(*(i.size for i in imgs))
total_width = sum(widths)
max_height = max(heights)
new_im = Image.new('RGB', (total_width, max_height))
# Place first image
new_im.paste(imgs[0],(0,0))
# Iteratively append images in list horizontally
hoffset=0
for i in range(1,len(imgs),1):
hoffset=imgs[i-1].size[0]+hoffset # update offset**
new_im.paste(imgs[i],(hoffset,0))
new_im.save('row.jpg')
The result I'm getting now is one image made up of concatenated images in a horizontal row. This is what I want, except the images are being made smaller and smaller in the concatenation process. I want the end result to not make the images smaller and instead produce an image made of the input images with their original size. So the output image will just have to have a very long width.

It seems you have a bug while updating the offsets.
You should replace your iteration block with:
imgs = [Image.open(i) for i in list_of_images]
widths, heights = zip(*(i.size for i in imgs))
new_img = Image.new('RGB', (sum(widths), max(heights)))
h_offset = 0
for i, img in enumerate(imgs):
new_img.paste(img, (h_offset, 0))
h_offset += img.size[0]

Related

MiniMagick Resize Image

I'm trying to use MiniMagick to resize 2 images and overlay one on top of the other. Heres the code I am using
require "mini_magick"
first_image = MiniMagick::Image.new("spider.jpg")
first_image = first_image.resize("250x250")
second_image = MiniMagick::Image.new("q.png")
second_image = second_image.resize("250x250")
result = first_image.composite(second_image) do |c|
c.compose "Over" # OverCompositeOp
c.gravity "center"
# c.resize("250x250")
end
result.write "output.jpg"
This overlays the images but neither is resized and the overlay image ends up awkwardly cropped. Ive tried making both the same size, making the bigger overlay image smaller and the smaller image bigger, but none seem to work. Any advice would be highly appreciated.

Add padding to object in 4-channel image

I have a 4-channel image (.png, .tif) like this one:
I am using OpenCV, and I would like to add padding of type BORDER_REFLECT around the flower. copyMakeBorder is not useful, since it adds padding to the edges of the image.
I can add certain padding if I split the image in bgr + alpha and apply dilate with BORDER_REFLECT option on the bgr image, but that solution spoils all the pixels of the flower.
Is there any way to perform a selective BORDER_REFLECT padding addition on a ROI defined by a binary mask?
EDIT:
The result I expect is something like (sorry I painted it very quickly with GIMP) :
I painted two black lines to delimit the old & new contour of the flower after the padding, but of course those lines should not appear in the final result. The padding region (inside the two black lines) must be composed by mirrored pixels from the flower (I painted it yellow to make it understandable).
A simple python script to resize the image and copy the original over the enlarged one will do the trick.
import cv2
img = cv2.imread('border_reflect.png', cv2.IMREAD_UNCHANGED)
pad = 20
sh = img.shape
sh_pad = (sh[0]+pad, sh[1]+pad)
imgpad = cv2.resize(img, sh_pad)
imgpad[20:20+sh[0], 20:20+sh[1], :][img[:,:,3]==255] = img[img[:,:,3]==255]
cv2.imwrite("padded_image.png", imgpad)
Here is the result
But that doesn't look very 'centered'. So I modified the code to detect and account for the offsets while copying.
import cv2
img = cv2.imread('border_reflect.png', cv2.IMREAD_UNCHANGED)
pad = 20
sh = img.shape
sh_pad = (sh[0]+pad, sh[1]+pad)
imgpad = cv2.resize(img, sh_pad)
def get_roi(img):
cimg = img[:,:,3].copy()
contours,hierarchy = cv2.findContours(cimg,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#Remove the tiny pixel noises that get detected as contours
contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 10]
x,y,w,h = cv2.boundingRect(cnt)
roi=img[y:y+h,x:x+w]
return roi
roi = get_roi(img)
roi2 = get_roi(imgpad)
sh = roi.shape
sh2 = roi2.shape
o = ((sh2[0]-sh[0])/2, (sh2[1]-sh[1])/2)
roi2[o[0]:o[0]+sh[0], o[1]:o[1]+sh[1], :][roi[:,:,3]==255] = roi[roi[:,:,3]==255]
cv2.imwrite("padded_image.png", imgpad)
Looks much better now
The issue has been already addressed and solved here:
http://answers.opencv.org/question/90229/add-padding-to-object-in-4-channel-image/

Image blending with mask

I'm trying to combine the two images based on the information from the mask. I'm using the color information from the background image if the mask is 0 and color information from foreground image if the mask is 1. Because the mask and both
Images are of the same size, I would like to use logical indexing of matrices to achieve this.
My attempt:
mask = imread('mask.png');
foreground = imread('fg.jpg');
background = imread('bg.jpg');
[r,c,~]=size(mask);
A = zeros(size(mask));
for i=1:r
for j=1:c
if mask(i,j) == 0
A(i,j,:) = background(i,j,:);
end
if mask(i,j) > 0
A(i,j,:) = foreground(i,j,:);
end
end
end
imshow(A);
The result looks like a flickering blue image, but I don't want that. Please help.
You can do this a bit more concisely:
f = double(foreground).*double(mask);
b = double(background).*double(~mask);
blend = f+b;
imshow(blend, []);
Using logical indexing you could also do
foreground(logical(mask)) = 0;
background(logical(~mask)) = 0;
blend = foreground+background;
The ISNOT operator '~' inverts your matrix in the second line, so you cut out the area you would like for background.
NOTE: This works for black and white (one channel). For coloured images see rayryeng's solution.
There are two problems with your code. The first problem is that you are trying to assign colour pixels to the output image A, yet this image is only two-dimensional. You want an image with three channels, not two. In addition, the output image type you are specifying is wrong. By default, the output image A is of type double, yet you are copying values into it that aren't double... most likely unsigned 8-bit integer.
As such, cast the image to the same type as the input images. Assuming both input images are the same type, initialize your A so that:
A = zeros(size(foreground), class(foreground));
This correctly makes a colour image with the same type as any of the inputs, assuming that they're both the same type.
Now, your for loop is fine, but it's better if you do this in one shot with logical indexing. If you want to use logical indexing, create a new image that's initially blank like what you've done, but then make sure your mask has three channels to match the number of channels the other images have. After, you simply need to index into each image and set the right locations accordingly:
mask = imread('mask.png');
foreground = imread('fg.jpg');
background = imread('bg.jpg');
[r,c,d]=size(mask); %// Change
%// If your mask isn't three channels, make it so
%// Change
if d ~= 3
mask = cat(3, mask, mask, mask);
end
A = zeros(size(foreground), class(foreground)); %// Change
A(mask) = foreground(mask); %// Assign pixels to foreground
A(~mask) = background(~mask); %// Assign pixels to background
imshow(A);

Compare two images and highlight differences along on the second image

Below is the current working code in python using PIL for highlighting the difference between the two images. But rest of the images is blacken.
Currently i want to show the background as well along with the highlighted image.
Is there anyway i can keep the show the background lighter and just highlight the differences.
from PIL import Image, ImageChops
point_table = ([0] + ([255] * 255))
def black_or_b(a, b):
diff = ImageChops.difference(a, b)
diff = diff.convert('L')
# diff = diff.point(point_table)
h,w=diff.size
new = diff.convert('RGB')
new.paste(b, mask=diff)
return new
a = Image.open('i1.png')
b = Image.open('i2.png')
c = black_or_b(a, b)
c.save('diff.png')
!https://drive.google.com/file/d/0BylgVQ7RN4ZhTUtUU1hmc1FUVlE/view?usp=sharing
PIL does have some handy image manipulation methods,
but also a lot of shortcomings when one wants
to start doing serious image processing -
Most Python lterature will recomend you to switch
to use NumPy over your pixel data, wich will give
you full control -
Other imaging libraries such as leptonica, gegl and vips
all have Python bindings and a range of nice function
for image composition/segmentation.
In this case, the thing is to imagine how one would
get to the desired output in an image manipulation program:
You'd have a black (or other color) shade to place over
the original image, and over this, paste the second image,
but using a threshold (i.e. a pixel either is equal or
is different - all intermediate values should be rounded
to "different) of the differences as a mask to the second image.
I modified your function to create such a composition -
from PIL import Image, ImageChops, ImageDraw
point_table = ([0] + ([255] * 255))
def new_gray(size, color):
img = Image.new('L',size)
dr = ImageDraw.Draw(img)
dr.rectangle((0,0) + size, color)
return img
def black_or_b(a, b, opacity=0.85):
diff = ImageChops.difference(a, b)
diff = diff.convert('L')
# Hack: there is no threshold in PILL,
# so we add the difference with itself to do
# a poor man's thresholding of the mask:
#(the values for equal pixels- 0 - don't add up)
thresholded_diff = diff
for repeat in range(3):
thresholded_diff = ImageChops.add(thresholded_diff, thresholded_diff)
h,w = size = diff.size
mask = new_gray(size, int(255 * (opacity)))
shade = new_gray(size, 0)
new = a.copy()
new.paste(shade, mask=mask)
# To have the original image show partially
# on the final result, simply put "diff" instead of thresholded_diff bellow
new.paste(b, mask=thresholded_diff)
return new
a = Image.open('a.png')
b = Image.open('b.png')
c = black_or_b(a, b)
c.save('c.png')
Here's a solution using libvips:
import sys
from gi.repository import Vips
a = Vips.Image.new_from_file(sys.argv[1], access = Vips.Access.SEQUENTIAL)
b = Vips.Image.new_from_file(sys.argv[2], access = Vips.Access.SEQUENTIAL)
# a != b makes an N-band image with 0/255 for false/true ... we have to OR the
# bands together to get a 1-band mask image which is true for pixels which
# differ in any band
mask = (a != b).bandbool("or")
# now pick pixels from a or b with the mask ... dim false pixels down
diff = mask.ifthenelse(a, b * 0.2)
diff.write_to_file(sys.argv[3])
With PNG images, most CPU time is spent in PNG read and write, so vips is only a bit faster than the PIL solution.
libvips does use a lot less memory, especially for large images. libvips is a streaming library: it can load, process and save the result all at the same time, it does not need to have the whole image loaded into memory before it can start work.
For a 10,000 x 10,000 RGB tif, libvips is about twice as fast and needs about 1/10th the memory.
If you're not wedded to the idea of using Python, there are a few really simple solutions using ImageMagick:
“Diff” an image using ImageMagick

How to make gif images from a set of images in matlab?

How to make '.gif' image from a set of '.jpg' images (say: I1.jpg, I2.jpg,..., I10.jpg) in matlab?
Ok here is a simple example. I got an image with a unicorn on it and remove 2 part to create 3 different images, just for the sake of creating an animated gif. Here is what it looks like:
clear
clc
%// Image source: http:\\giantbomb.com
A = rgb2gray(imread('Unicorn1.jpg'));
B = rgb2gray(imread('Unicorn2.jpg'));
C = rgb2gray(imread('Unicorn3.jpg'));
ImageCell = {A;B;C};
figure;
subplot(131)
imshow(A)
subplot(132)
imshow(B)
subplot(133)
imshow(C)
%// Just to show what the images look like (I removed spots to make sure there was an animation created):
%// Create file name.
FileName = 'UnicornAnimation.gif';
for k = 1:numel(ImageCell)
if k ==1
%// For 1st image, start the 'LoopCount'.
imwrite(ImageCell{k},FileName,'gif','LoopCount',Inf,'DelayTime',1);
else
imwrite(ImageCell{k},FileName,'gif','WriteMode','append','DelayTime',1);
end
end
As you see, its not that different from the example on the Mathworks website. Here my images are in a cell array but yours might be in a regular array or something else.That should work fine; when I open 'UnicornAnimation.gif' it is indeed a nice animation!
Hope that helps!

Resources