Beier-Neely Warp实施

时间:2014-07-02 05:56:53

标签: matlab image-processing

我一直在尝试在Matlab中实现Beier-Neely变形算法(description)并遇到麻烦。

我大部分时间都在工作,除了最终图像旋转180度,对于非方形图像,目标图像尝试对源图像边界外的像素进行采样。

我对Matlab不是很有经验,所以这可能是一些愚蠢的错误,但是,我已经手工计算了几个像素,并获得了非矩形图像的越界坐标。我不确定我做错了什么,因为我没有在任何其他谈论Beier-Neely的网站上看到过这种行为的提法。

function morphed = bnmorph( im, inputLines, targetLines )
%BNMORPH Takes image and two sets of lines.
% Morphs image from inputLines to targetLines

[xSize,ySize,~] = size(im);
[numPoints,~] = size(targetLines);
numLineSegments = int32(numPoints/2);

% Make sure there are no horizontal or vertical lines
conditionlines(inputLines);
conditionlines(targetLines);

% Preallocate space
morphed = im;

% For each pixel in the destination image, calculate the pixel to 
% sample from the source image.
for y = 1:ySize
    for x = 1:xSize
        X = [x y];
        Xsource = zeros(1,2);
        dsum = zeros(1,2);
        weightsum = 0;
            % For each line segment
            for i = 1:numLineSegments
                % Indices for the endpoints of the segments
                i1 = (i-1)*2+1;
                i2 = i1 + 1;

                % Get Pi, Qi, the end points for the line segment i
                % in the dst image
                Pi = targetLines(i1, :);
                Qi = targetLines(i2, :);

                % Get the vector QPi
                QPi = Qi - Pi;

                % Get Pi', Qi', the end points for the line segment i
                % in the src image
                Pisource = inputLines(i1,:);
                Qisource = inputLines(i2,:);

                % Get the vector QPi'
                QPisource = Qisource - Pisource;

                % Calculate u = (X-Pi).(QPi) / ||QPi||^2
                u = dot((X - Pi), QPi) / (QPi(1).^2 + QPi(2).^2);

                % Calculate v = (X-Pi).perp(QPi) / ||QPi||
                % where perp(QPi) = [-QPi(2), QPi(1)]
                v = dot((X - Pi), perp(QPi)) / sqrt(QPi(1).^2 + QPi(2).^2);

                % Calculate Xi', the pixel to sample in src image
                % Xi' = Pi'+u*QPi' + v*perp(QPi') / ||QPi'||
                Xisource = Pisource+u*QPisource+(v*perp(QPisource) / sqrt(QPisource(1).^2 + QPisource(2).^2));


                % Add this pixel to the weight
                % Does nothing when only one line segment
                Di = Xisource - X;
                if u < 0
                    dist = sqrt((X(1)-Pi(1)).^2+(X(2)-Pi(2)).^2);
                elseif u > 1
                    dist = sqrt((X(1)-Qi(1)).^2+(X(2)-Qi(2)).^2);
                else
                    dist = abs(v);
                end
                length = sqrt(QPi(1).^2 + QPi(2).^2);
                p = 0.5; % Defines strength of lines relative to length. Range: [0,1]. If 0, all lines have same weight, if 1, longer lines carry more weight than shorter ones
                a = 0.1; % Defines strength of line based on distance from point. Lower values = more control, larger value = more smooth warping
                b = 1; % Defines strength fall-off based on distance from point. Good Range: [0.5,2]. If 0, pixel affected by all lines equally, if large, then only affected by nearest lines
                weight = (length.^p / (a + dist)).^b;
                dsum = dsum + (Di * weight);
                weightsum = weightsum + weight;
            end

            % Calculate final source pixel
            % Will equal Xisource when only one line segment       
            Xsource = X + dsum / weightsum;


            % Bounds check to set out of bounds pixels to teal
            % Shouldn't be needed as far as I'm aware
            [xSize, ySize, ~] = size(im);
            nullCol = false;
            if int32(Xsource(1)) <= 0
                Xsource(1) = 1;
                nullCol = true;
            elseif int32(Xsource(1)) > xSize
                Xsource(1) = xSize;
                nullCol = true;
            end
            if int32(Xsource(2)) <= 0
                Xsource(2) = 1;
                nullCol = true;
            elseif int32(Xsource(2)) > ySize
                Xsource(2) = ySize;
                nullCol = true;
            end

            if nullCol == true
                morphed(X(1), X(2), :) = [0, 255, 255];
            else
                % Set pixel X in dst image
                morphed(X(1), X(2), :) = im(int32(Xsource(1)), int32(Xsource(2)), :);
            end
     end
end

subplot(1, 2, 1);
imshow(im);
line([inputLines(1,1) inputLines(2,1)], [inputLines(1,2) inputLines(2,2)], 'Color',[.0 1.0 .0]);
subplot(1, 2, 2);
imshow(morphed);
line([targetLines(1,1) targetLines(2,1)], [targetLines(1,2) targetLines(2,2)], 'Color',[.0 1.0 .0]);

end

以上是上述代码的一些输出。该功能应将源图像中的点P'变形为目标图像中的P点。同样,将Q'指向Q.

Square Image, with single line. Is correct, except should be rotated 180 degrees.

这是正确的,除了应旋转180度

Rectangle Image. Incorrect rotation and scaling

这个中间图像(带有蓝绿色)也应旋转180度,并沿三角形的长边伸展,而不是宽边。

我在matlab网站上看到提到像素坐标是在(y,x)而不是(x,y)中完成的。使用morphed(X(2), X(1), :) = im(int32(Xsource(2)), int32(Xsource(1)), :);修复了方形图像的旋转问题,然而,它将矩形图像转换为更大的正方形(即如果输入图像为192 * 128,则会尝试输出为192 * 192而不是192 * 128)),但不再有任何越界访问。

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:1)

原来翻转问题是索引问题。在循环开始时,我将其更改为X = [y x],然后在Xsource = X + dsum / weightsum;后的所有行中将其更改为(1)&{39}和(2)&#39;第

通过添加第二条控制线来解决拉伸问题(横跨图像的水平线与垂直线一样)。