snibgo's ImageMagick pages

Alignment of hand-held photographs

Perspective distortion of one photo to match another that is already close, but may have different exposure, or blur.

The following are building-blocks for this page:

Sample images

All work on this page is performed on full-size camera images, about 7500x5000 pixels. The results are shrunk and converted to JPG for this web page.

The camera was hand-held. Auto-exposure was used, so the exposure for overlapping parts of the photos varies slightly.

set WEB_SIZE=-resize 600x400
          
wotpic.png

We assume the camera has rotated but not translated, so a perspective transformation is suitable. Ideally we want four corresponding points from the two images. The four points should be distant from each other: one from each corner, one-third of image width and height.

Parameters: 2 inputs, 1 output, possible debug images.
Window dims of reference. Default 1% of min image dim. At least 5x5.
Window dims search areas. Default 5% of min image dim. At least ref dims * 2.
Threshold to eliminate sky etc.
Threshold for match.
Find useful points in one image:
Calculate SD (or other measure of detail).
Paint margins black.
Paint any values below threshold black.
Consider four corner areas:
- Find three highest points of detail.
- Ignore any that have zero detail.
- If all have zero detail, this four corner area is useless.
If all four corner areas are useless, job fails.
For each useful point in one image,
- crop to reference window
- crop other image to search area, same centre
- normalising gain and bias, and search

See also

Scripts

For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.

alignGausPyr.bat

rem Find translation-only alignment of two Gaussian pyramids %1 and %2 with same structure.
rem Assumes %pyPREFIX% is the prefix of an appropriate blk.lis file.
@rem
@rem Optional:
@rem   %3 Factor for initial search window eg 0.66.

@if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 agp


set SRC1=%1
set SRC2=%2

set SRCH_WIND=%3
if "%SRCH_WIND%"=="." set SRCH_WIND=
if "%SRCH_WIND%"=="" set SRCH_WIND=0.6667


if "%pyPREFIX%"=="" (
  echo %0: pyPREFIX not set
  exit /B 1
)

for /F "tokens=*" %%L in (%pyPREFIX%blk.lis) do set %%L

if "%NUM_OCTAVES%"=="" (
  echo %0: NUM_OCTAVES not set in %pyPREFIX%blk.lis
  exit /B 1
)

set /A iOct=%NUM_OCTAVES%-1


for /F "usebackq" %%L in (`%IM%identify ^
  -format "SrchW=%%[fx:int(!N_BLK_W.%iOct%!*%SRCH_WIND%+0.5)]\nSrchH=%%[fx:int(!N_BLK_H.%iOct%!*%SRCH_WIND%+0.5)]" ^
  xc:`) do set %%L



rem Get dims multiplied by magic overlap factor for first search.

call %PICTBAT%alignBF ^
  %SRC1%[%iOct%] %SRC2%[%iOct%] ^
  !N_BLK_W.%iOct%! !N_BLK_H.%iOct%! -%SrchW% -%SrchH% %SrchW% %SrchH%

set DODGY=%abfDODGY%

call :ReadBlkLis


for /L %%i in (%iOct%,-1,1) do (

  echo %~n0: BESTX=!BESTX! BESTY=!BESTY!
  echo %~n0: N_BLK_W.%%i=!N_BLK_W.%%i!
  echo %~n0: N_BLK_H.%%i=!N_BLK_H.%%i!

  set /A PrevOct=%%i-1

  call :GetPrev %%i !PrevOct!

  echo %~n0: FirstX=!FirstX! FirstY=!FirstY! LastX=!LastX! LastY=!LastY!

  call %PICTBAT%alignBF ^
    %SRC1%[!PrevOct!] %SRC2%[!PrevOct!] ^
    !BW! !BH! !FirstX! !FirstY! !LastX! !LastY!

  if "!abfDODGY!"=="1" set DODGY=1

  call :ReadBlkLis
)

echo %~n0: COMP=%COMP% abfDATAFILE=%abfDATAFILE%

call echoRestore

@endlocal & set agpDATAFILE=%abfDATAFILE%& set agpDODGY=%DODGY%

exit /B 0

::---------------- Subroutines ----------------
:ReadBlkLis

  rem type %abfDATAFILE%

  for /F "tokens=1-9 delims=," %%A in (!abfDATAFILE!) do (
    set COMP=%%A
    set BESTX=%%B
    set BESTY=%%C
    set W=%%D
    set H=%%E
    set X1=%%F
    set Y1=%%G
    set X2=%%H
    set Y2=%%I
  )
  echo %~n0: !COMP! !X! !Y! X1=!X1! Y1=!Y1! X2=!X2! Y2=!Y2!

  exit /B

:GetPrev
  echo %~n0: N_BLK_W.%2=!N_BLK_W.%2!
  echo %~n0: N_BLK_H.%2=!N_BLK_H.%2!

  set /A BW=!N_BLK_W.%2!
  set /A BH=!N_BLK_H.%2!
  set /A FirstX=^(!BESTX!-1^)*!N_BLK_W.%2!/!N_BLK_W.%1!
  set /A FirstY=^(!BESTY!-1^)*!N_BLK_H.%2!/!N_BLK_H.%1!
  set /A LastX=^(!BESTX!+1^)*!N_BLK_W.%2!/!N_BLK_W.%1!
  set /A LastY=^(!BESTY!+1^)*!N_BLK_H.%2!/!N_BLK_H.%1!

  exit /B

All images on this page were created by the commands shown, using:

%IM%identify -version
Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib
%IMDEV%identify -version
Version: ImageMagick 6.9.9-40 Q32 x86_64 2019-12-18 http://www.imagemagick.org
Copyright: © 1999-2018 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC HDRI Modules OpenMP 
Delegates (built-in): bzlib cairo fftw fontconfig fpx freetype jbig jng jpeg lcms ltdl lzma pangocairo png rsvg tiff webp wmf x xml zlib

To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG or TIFF or MIFF to JPG.

Source file for this web page is alignhh.h1. To re-create this web page, run "procH1 alignhh".


This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.

Anyone is permitted to link to this page, including for commercial use.


Page version v1.0 26-November-2020.

Page created 27-Nov-2020 20:26:14.

Copyright © 2020 Alan Gibson.