{"id":269,"date":"2013-07-17T23:35:53","date_gmt":"2013-07-18T04:35:53","guid":{"rendered":"http:\/\/homepages.uc.edu\/~yaozo\/wordpress\/?p=269"},"modified":"2013-07-17T23:35:53","modified_gmt":"2013-07-18T04:35:53","slug":"matlab-working-with-videos-image-warping-and-mosaicing","status":"publish","type":"post","link":"https:\/\/zhuoyao.net\/index.php\/2013\/07\/17\/matlab-working-with-videos-image-warping-and-mosaicing\/","title":{"rendered":"Matlab &#8211; Working with Videos&#8212;&#8212;&#8212;-IMAGE WARPING and MOSAICING"},"content":{"rendered":"<p>the process of combining multiple photographic images with overlapping fields of view to produce a segmented panorama or high-resolution image. Commonly performed through the use of computer software, most approaches to image stitching require nearly exact overlaps between images and identical exposures to produce seamless results.<\/p>\n<p>My Steps<\/p>\n<ol start=\"1\">\n<li>Convert the video into Frames and then into Images<\/li>\n<li>using SIFT\u00a0get pairs of corresponding points taken from the two images<\/li>\n<li>Recover homographies of each Image<\/li>\n<li>Warp the images using the homography<\/li>\n<li>Blend images into a mosaic using weighted average<\/li>\n<li>View the Results<\/li>\n<\/ol>\n<p>a One minute video is 60*30 = 1800 images. Will reduce for speed.<\/p>\n<pre>function [mos_img]=mosaic(filename)\n% creates a mosaic image from a movie\n% assuming translation only between frames\n\nwarning off MATLAB:mir_warning_variable_used_as_function;\n\n% using global names to cache frames in memory\n% performance boosting\nglobal Nframes;\nglobal AviName;\n\nAviName = filename;\n[h, w, Nframes] = avi_size(AviName);\n\n% relative displacements\nd = zeros(2,Nframes);\n\nRefFrame = GetImage(1);\n\n% align every two consequent frames\nfor ii=2:Nframes\n\n    % read a frame\n    Frame = GetImage(ii);\n\n    d(:,ii) = align(RefFrame, Frame, d(:,ii-1));\n\n    % current frame is next one's reference\n    RefFrame = Frame;\nend\n\n% cumulative displacement relative to first frame\nD = cumsum(d,2);\n\n% minimal\nMinimum = min(D,[],2);\n%maximal\nMaximum = max(D,[],2);\n\n% minimal displacement is one\nD = D - Minimum*ones(1,Nframes)+1;\n\n% create an empty mosaic (compute the bounding box)\nmos_img = zeros([w, h]+ceil(Maximum'-Minimum'))';\n[bm bn] = size(mos_img);\n% creates weights\nweights = zeros(bm,bn);\n\n% shfoch everything into the mosaic\nfor ii=1:Nframes\n    % read a frame\n    Frame = GetImage(ii);\n\n    tmpF(1:(h+1),1:(w+1)) = 0;\n    tmpF(2:h+1,2:w+1) = Frame;\n    intD = floor(D(:,ii));\n    wF = warp(tmpF, (D(:,ii)-intD));\n    wF(find(isnan(wF))) = 0;\n\n    % mask\n    m = zeros(size(tmpF));\n    m(2:h+1,2:w+1) = 1;\n    wm = warp(m, (D(:,ii)-intD));\n    wm(find(isnan(wm))) = 0;\n\n    [A B] = GetPosArray(bm, h, bn, w, intD);\n    mos_img(A, B) = mos_img(A, B) + wF;\n    weights(A, B) = weights(A, B) + wm;\n\nend\n\nweights = weights + (weights == 0); % in order to avoid zero division\nmos_img = mos_img.\/weights;\n\nfunction [A, B] = GetPosArray(bm, h, bn, w, intD)\n% Gets an array, where to put the image.\n% bm, bn - the total image's size\n% h, w - single frame's size\n% intD - the integer displacement.\nA = bm - h + 1 - intD(2): bm - intD(2) + 1;\nB = bn - w + 1- intD(1): bn - intD(1) + 1;\n\nfunction image = GetImage(index)\n% Gets the image data in the correct form (Double, greyscale).\n% using chached frames to boost performance\n\n% caching BUFFER_SIZE images at each access to the avi file\nBUFFER_SIZE = 40;\npersistent CurrentBlock M;\nglobal AviName Nframes;\n\nif (isempty(CurrentBlock))\n    CurrentBlock = -1;\nend;\n\n% zindex to start from zero rather than one.\nzindex = index - 1;\n\nif (floor(zindex \/ BUFFER_SIZE) ~= CurrentBlock)\n    CurrentBlock = zindex \/ BUFFER_SIZE;\n    ReadTo = min((CurrentBlock+1)*BUFFER_SIZE, Nframes);\n    M = aviread(AviName, (CurrentBlock*BUFFER_SIZE + 1):ReadTo);\nend\n\nlocalIndex = mod(zindex, BUFFER_SIZE) + 1;\n\n%returning the right image in the correct format.\nimage = im2double(rgb2gray(M(localIndex).cdata ));\n\nfunction [shift] = align(im1, im2, initial_shift)\n% This function aligns im2 to im1 assuming an homography consist of shift only\n% Input parameters\n%   im1, im2 - images in double representation grey level range [0,1]\n%\n% The output parameters:\n%   shift - a vector [dx, dy]' of displacement\n\n% initial guess for displacement\nshift = [0, 0]';\nif (nargin == 3)\n    shift = initial_shift;\nend;\n\n% global parameters\nITER_EPS = 0.001;\nPYRAMID_DEPTH = 5;\nITERATIONS_PER_LEVEL = 5;\nFILTER_PARAM = 0.4;         % parameter for gaussian pyramid\n\n% Creating pyramids for both images.\nF = gaussian_pyramid(im1, PYRAMID_DEPTH, FILTER_PARAM);\nG = gaussian_pyramid(im2, PYRAMID_DEPTH, FILTER_PARAM);\n\n% Allowing initial shift - scale it to fit pyramid level\nshift = shift .\/ (2^(PYRAMID_DEPTH+1));\n\n% work coarse to fine in pyramids\nfor ii = PYRAMID_DEPTH : -1 : 1 %PYRAMID_DEPTH\n    clear warped_im2;    % we are about to change its dimensionality\n\n    shift = 2 * shift;   % compensate the scale factor between pyramid levels.\n\n    % compute the gradient once per pyramid level\n    [Fx Fy] = gradient(F{ii});\n\n    % although we can re-calculate it according to overlapping zone\n    % it is negligible thus we save computation by doing it once per level\n    tmp = sum(Fx(:).*Fy(:));\n    A = inv( [ sum(Fx(:).^2) tmp ; tmp sum(Fy(:).^2) ] );\n\n    % iterate per pyramid level\n    for jj = 1 : ITERATIONS_PER_LEVEL\n        % always warp original image by accumulated shift, avoid over\n        % blurring\n        warped_im2 = warp(G{ii}, shift);\n\n        % mask to rule out boundary (NaN values)\n        mask = isnan(warped_im2);\n        warped_im2(find(mask)) = 0;\n        mask = 1-mask;\n\n        % difference image - ignore parts that are not overlapping\n        diff = ( F{ii}(:)-warped_im2(:) ).*mask(:);\n\n        % Solve and update shift;\n        tmpshift =  A*[sum(diff.*Fx(:)) ; sum(diff.*Fy(:))] ;\n        shift = shift + tmpshift;\n        if (abs(tmpshift) &lt; ITER_EPS)\n            break;\n        end;\n    end;\nend;\n\n% wrapping an image with shift using bilinear interpolation\nfunction [warped] = warp(image, shift)\n[x y] = size(image);\n[X Y] = meshgrid(1:y,1:x); % Behold!!!\nwarped = interp2(image, X+shift(1), Y+shift(2), 'linear');\n\nfunction [G] = gaussian_pyramid(image, n, a)\n% compute n gaussian pyramid of an image\n% a - filter parameter\n\nG = cell(n,1);\nG{1} = image;\nfor ii=1:n-1\n    % use tmp variable to comply with varying size of image through down sampling\n    G{ii+1}=gaussian_pyramid_one_stage(G{ii}, a);\nend\n\nfunction [G]=gaussian_pyramid_one_stage(im, a)\n% compute one stage of a gaussian pyramid of an image\n% a is the filter parameter\n\n[m n]=size(im);\n\n% construct the filter\nfilterKernel = [0.25 - (a\/2), 0.25, a, 0.25, 0.25-(a\/2)];\nh = filterKernel'*filterKernel;\n\n% filter the image, i.e. blur it with the gaussian, replicate to overcome\n% boundary conditions\nfilt = imfilter(im, h, 'replicate');\n\n% down sample\nG = filt(1:2:m,1:2:n);<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>the process of combining multiple photographic images with overlapping fields of view to produce a segmented panorama or high-resolution image. Commonly performed through the use&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-269","post","type-post","status-publish","format-standard","hentry","category-matlab"],"_links":{"self":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/posts\/269","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/comments?post=269"}],"version-history":[{"count":0,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/posts\/269\/revisions"}],"wp:attachment":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/media?parent=269"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/categories?post=269"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/tags?post=269"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}