Image with accordion effect

2019-07-19 05:40发布

I have read in an image file to MATLAB and I am trying to stretch it in one direction, but a variable amount (sinusoidal). This would create an accordion affect on the image. I have toyed around with imresize, however that only resizes the image linearly. I would like the amount of "stretch" to vary for each image line. I tried to convey this with the following code:

periods = 10; % Number of "stretch" cycles
sz = size(original_image,2)/periods;
s = 0;
x = 0;
for index = 1:periods
    B = original_image(:,round(s+1:s+sz));
    if mod(index,2) == 0
        amp = 1.5;
    else
        amp = 0.75;
    end
    xi = size(B,2)*amp;
    new_image(:,x+1:x+xi) = imresize(B, [size(B,1) size(B,2)*amp]);
    s = s + sz;
    x = x+xi;
end

You can see that segments of the image are stretched, then compressed, then stretched, etc, like an accordion. However, each segment has a uniform amount of stretch, whereas I'd like it to be increasing then decreasing as you move along the image.

I have also looked at MATLAB's example of Applying a Sinusoidal Transformation to a Checkerboard which seems very applicable to my problem, however I have been trying and I cannot get this to produce the desired result for my image. Any help is much appreciated.


UPDATE:

Thank you for Answer #1. I was unable to get it to work for me, but also realized it would resulted in loss of data, as the code only called for certian lines in the original image, and other lines would have been ignored.

After experimenting further, I developed the code below. I used a checkerboard as an example. While combersome, it does get the job done. However, upon trying the script with an actual high-resolution image, it was extremely slow and ended up failing due to running out of memory. I believe this is because of the excessive number of "imresize" commands that are used in loop.

I = checkerboard(10,50);
I = imrotate(I,90);
[X Y] = size(I);
k = 4; % Number of "cycles"
k = k*2;
x = 1;
y = 2;
z = 2;
F = [];
i = 1;
t = 0;
s = 0;

for j = 1:k/2
    t = t + 1;
    for inc = round(s+1):round(Y/k*t)
        Yi = i + 1;
        F(:,(x:y)) = imresize(I(:,(inc:inc)),[X Yi]);
        x = y + 1;
        y = x + z;
        z = z + 1;
        i = i + 1;
    end
    y = y - 2;
    z = z - 4;
    for inc = round(Y/k*t+1):round(Y/k*(t+1))
        Yi = i - 1;
        F(:,(x:y)) = imresize(I(:,(inc:inc)),[X Yi]);
        x = y + 1;
        y = x + z;
        z = z - 1;
        i = i - 1;
    end
    y = y + 2;
    z = z + 4;

    s = Y/k*(t+1);
    t = t + 1;
end
Fn = imresize(F, [X Y]);
imshow(Fn);

Does anyone know of a simpler way to achieve this? If you run the code above, you can see the effect I am trying to achieve. Unfortunately, my method above does not allow me to adjust the amplitude of the "stretch" either, only the number of "cycles," or frequency. Help on this would also be appreciated. Much thanks!

1条回答
仙女界的扛把子
2楼-- · 2019-07-19 06:47

Here is how I would approach it:

  1. Determine how the coordinate of each point in your Final image F maps into your Initial image I of size (M,N)

    Since you want to stretch horizontally only, given a point (xF,yF) in your final image, that point would be (xI,yI) in your initial image where xI and yI can be obtained as follows:

    yI = yF;
    xI = xF + Lsin(xFK);

    Notes:

    • these equations do not guarantee that xI remains within the range [1:N] so cropping needs to be added
    • K controls the how many wrinkles you want to have in your accordion effect. For example, if you only want one wrinkle, K would be 2*pi/N
    • L controls how much stretching you want to apply
  2. Then simply express your image F from image I with the transforms you have in 1.

Putting it all together, the code below creates a sample image I and generates the image F as follows:

  % Generate a sample input image
  N=500;
  xF=1:N;
  I=(1:4)'*xF/N*50;

  % Set the parameters for your accordion transform
  K=2*pi/N;
  L=100;

  % Apply the transform
  F=I(:, round(min(N*ones(1,N), max(ones(1,N), (xF + L*sin(xF*K))))) );

  % Display the input and output images side by side
  image(I);
  figure;
  image(F);

If you run this exact code you get:

enter image description here

As you can see, the final image on the right stretches the center part of the image on the left, giving you an accordion effect with one wrinkle.

You can fiddle with K and L and adjust the formula to get the exact effect you want, but note how by expressing the transform in a matrix form MATLAB executes the code in a fraction of second. If there is one take away for you is that you should stay away from for loops and complex processing whenever you can.

Have fun!

查看更多
登录 后发表回答