Combining 2 images by displaying one side by side, separated by a diagonal line

I have 2 images ("before" and "after"). I would like to show the final image where the left half is taken from the previous image and the right half is taken from the image after.

Images should be separated by a white diagonal line of a predefined width (2 or 3 pixels), where the diagonal is specified either at a specific angle or at two start and end coordinates. The diagonal should rewrite part of the final image so that the size is the same as the sources.

Example:

Example of a merged image

I know it can be done by looping over all pixels to recombine and create the final image, but is there an efficient way, or better yet, a built-in function that can do this?

+3


source to share


3 answers


Unfortunately, I don't believe there is a built-in solution to your problem, but I have developed some code to help you do this, but unfortunately it will require the imaging bar to play well with the code. As mentioned in your comments, you already have this, so we should be fine.

The logic behind this is relatively simple. We'll assume that your before and after photos are the same size and also have the same number of channels. The first part is to declare an empty image and we draw a straight line down the middle of a certain thickness. The idea is to declare an image that is slightly larger than the original image size. The reason is that I'm going to draw a line down the middle and then rotate this blank image at a specific angle to achieve the first part of what you want. I will useimrotate

to rotate the image to any angle you want. The first instinct is to declare the image the same size as the originals, draw a line down the middle and rotate it. However, if you do this, you end up with the line disabled, rather than drawing from top to bottom. This makes sense because a line drawn at an angle covers more pixels than if you were drawing it vertically.

Using the Pythagorean theorem, we know that the longest line that can be drawn in your image is a diagonal. Therefore, we declare the image in sqrt(rows*rows + cols*cols)

both rows and columns, where rows

and cols

are the rows and columns of the original image. After that, we'll grab the ceiling to make sure we cover as much as possible and add a little extra room to accommodate the line width. We draw a line on this image, rotate it, then crop the image after it is the same size as the input images. This will ensure that the line drawn at whatever angle you want is completely drawn from top to bottom.

This logic is the hardest part. When you do this, you will declare the two masks logical

you use imfill

to fill the left side of the mask as one mask and we will invert the mask to find another mask. You will also need to use the line image we created earlier imrotate

for indexing in the masks and set the values false

so that we ignore those pixels that are on the line.

Finally, you take each mask, index your image, and copy every part of the image you desire. You finally use the line image for indexing in the output and set the values ​​to white.

Without further ado, here's the code:



% Load some example data
load mandrill;

% im is the image before
% im2 is the image after
% Before image is a colour image
im = im2uint8(ind2rgb(X, map));

% After image is a grayscale image
im2 = rgb2gray(im);
im2 = cat(3, im2, im2, im2);

% Declare line image
rows = size(im, 1); cols = size(im, 2);
width = 5;
m = ceil(sqrt(rows*rows + cols*cols + width*width));
ln = false([m m]);
mhalf = floor(m / 2); % Find halfway point width wise and draw the line
ln(:,mhalf - floor(width/2) : mhalf + floor(width/2)) = true;

% Rotate the line image
ang = 20; % 20 degrees
lnrotate = imrotate(ln, ang, 'crop');

% Crop the image so that it the same dimensions as the originals
mrowstart = mhalf - floor(rows/2);
mcolstart = mhalf - floor(cols/2);
lnfinal = lnrotate(mrowstart : mrowstart + rows - 1, mcolstart : mcolstart + cols - 1);

% Make the masks
mask1 = imfill(lnfinal, [1 1]);
mask2 = ~mask1;
mask1(lnfinal) = false;
mask2(lnfinal) = false;

% Make sure the masks have as many channels as the original
mask1 = repmat(mask1, [1 1 size(im,3)]);
mask2 = repmat(mask2, [1 1 size(im,3)]);

% Do the same for the line
lnfinal = repmat(lnfinal, [1 1 size(im, 3)]);

% Specify output image
out = zeros(size(im), class(im));
out(mask1) = im(mask1);
out(mask2) = im2(mask2);
out(lnfinal) = 255;

% Show the image
figure;
imshow(out);

      

We get:

enter image description here

If you want the line to run in a different direction, just make the angle ang

negative. In the above example script, I made an angle of 20 degrees counterclockwise (i.e. positive). To reproduce the example you provided, specify -20 degrees instead. Now I am getting this image:

enter image description here

+5


source


Here's a solution using polygons:

function q44310306
% Load some image:
I = imread('peppers.png');
B = rgb2gray(I);
lt = I; rt = B;
% Specify the boundaries of the white line:
width = 2; % [px]
offset = 13; % [px]
sz = size(I);
wlb = [floor(sz(2)/2)-offset+[0,width]; ceil(sz(2)/2)+offset-[width,0]];
%      [top-left, top-right;              bottom-left, bottom-right]
% Configure two polygons:
leftPoly  = struct('x',[1 wlb(1,2) wlb(2,2) 1],        'y',[1 1 sz(1) sz(1)]);
rightPoly = struct('x',[sz(2) wlb(1,1) wlb(2,1) sz(2)],'y',[1 1 sz(1) sz(1)]);
% Define a helper grid:
[XX,YY] = meshgrid(1:sz(2),1:sz(1));
rt(inpolygon(XX,YY,leftPoly.x,leftPoly.y)) = intmin('uint8');
lt(repmat(inpolygon(XX,YY,rightPoly.x,rightPoly.y),1,1,3)) = intmin('uint8');
rt(inpolygon(XX,YY,leftPoly.x,leftPoly.y) & ...
   inpolygon(XX,YY,rightPoly.x,rightPoly.y)) = intmax('uint8');
final = bsxfun(@plus,lt,rt);
% Plot:
figure(); imshow(final);

      



Result:

enter image description here

+3


source


One solution:

im1 = imread('peppers.png');
im2 = repmat(rgb2gray(im1),1,1,3);

imgsplitter(im1,im2,80) %imgsplitter(image1,image2,angle [0-100])

function imgsplitter(im1,im2,p)
    s1  = size(im1,1); s2 = size(im1,2);
    pix = floor(p*size(im1,2)/100);
    val = abs(pix -(s2-pix));
    dia = imresize(tril(ones(s1)),[s1 val]);
    len = min(abs([0-pix,s2-pix]));
    if p>50
        ind = [ones(s1,len) fliplr(~dia) zeros(s1,len)];
    else
        ind = [ones(s1,len) dia zeros(s1,len)];
    end
    ind = uint8(ind);
    imshow(ind.*im1+uint8(~ind).*im2)
    hold on 
    plot([pix,s2-pix],[0,s1],'w','LineWidth',1)
end

      

OUTPUT: enter image description here

+2


source







All Articles