6

I want to merge two pictures but I do not know how to find the cut location in y-axis by any Linux/Unix tools, but here ImageMagick as the first idea. Any approach is welcome: a programmatic solution or a method for solving the problem with manual assistance. Two pictures have joint similarity in y-axis, which I want to minimise and then merge for output. Pseudocode

  1. Find the y-axis location where the data is first time equal in both pictures.
  2. Minimise the y-axis equality of two pictures as long there is no equality between the pictures.
  3. Cut each picture by appropriate location. For top by percentages here, convert -gravity SouthWest -crop 100x70%x+0+0 infile.jpg outfile.jpg. For bottom similarly, convert -gravity NorthWest -crop 100x70%x+0+0 infile.jpg outfile.jpg.
  4. To merge correct parts of two images etc (here) by convert -append A-edit.jpg B-edit.jpg output.png

Fig. 1 Image A, Fig. 2 Image B, Fig. 3 Expected output

enter image description here enter image description here enter image description here

  • 1
    If this were text, I might try taking the last n lines of the first input and comparing against a sliding window across the second. The merge-location would be the window position with the smallest Levenshtein distance, else the beginning. Perhaps if the colors were quantized, a similar approach could work here? – Fox Apr 4 '17 at 17:44
  • 1
    First let me note that I have very little experience in image processing. I do know that JPEGs use lossy compression, so areas that look identical may not be, but if you reduce the color space from, say, 16 million to a smaller number (256? 96?) the rounding would make them more closely identical. Converting to a limited-colorspace XPM (text-based) would allow comparison by Levenshtein distance, which as you note may not be the best metric. It might work better if combined with a Sobel filter for edge-detection – Fox Apr 4 '17 at 17:58
  • 1
    Is Imagemagick required, or other command line image manipulation tools also acceptable? (e.g. libraries for python and other programming languages) – Dario Apr 16 '17 at 21:21
  • 1
    Do you need a programmatic solution? Or would a method for solving the problem with manual assistance be acceptable? – roaima Apr 16 '17 at 23:09
  • 1
    Sounds like a computer vision problem. Try OpenCV, but I'm not familiar enough with to give step-by-step instructions. Look for "image similarity" functions, take into account if the pictures are scaled/skewed etc., and use Python/Java/etc. bindings to write a program. – dirkt Apr 17 '17 at 6:36
3
+500

Hugin will stitch these images for you.

There's a script available within the tutorial for stitching scanned images called run-scan-pto_var.sh that will do exactly what you need.

On my Debian system I need to install two packages (and, of course, their dependencies):

apt-get install hugin hugin-tools

In the interests of question completeness I've included a slightly modified version here (this version accepts image filenames on the command line instead of them being hardcoded):

#! /bin/sh
# hugin command tools script to stitch scanned images, fov unknown
# use of fov >= 10 should be OK, could simply set FOV=10
# Terry Duell 2013, 2014

# usage...run-scan-pto_var.sh outputprefix fov

#get the output file prefix
Prefix=$1

# get the fov
FOV=$2

shift 2

pto_gen --projection=0 --fov=$FOV -o project.pto "$@"
pto_lensstack -o project1.pto --new-lens i1 project.pto
cpfind -o project1.pto --multirow project1.pto
cpclean -o project2.pto project1.pto
linefind -o project3.pto project2.pto
pto_var -o setoptim.pto --opt r,d,e,!r0,!d0,!e0 project3.pto
autooptimiser -n -o autoptim.pto setoptim.pto
pano_modify  --projection=0 --fov=AUTO --center --canvas=AUTO --crop=AUTO -o autoptim2.pto autoptim.pto
pto2mk -o project.mk -p $Prefix autoptim2.pto
make -j 2 -f project.mk all

# Clean up afterwards
rm -f project.pto project1.pto project2.pto project2.pto project.mk
rm -f "$Prefix"[0-9][0-9][0-9][0-9].tif
rm -f autoptim.pto autoptim2.pto autoptim2.pto_rsp.arg
rm -f setoptim.pto

If your images are called wIowW.jpg and orMDp.jpg - as yours are named - and you want the result in rsp.tif you can run the script like this:

./run-scan-pto_var.sh rsp 10 *.jpg

The output is always written to a TIFF file. However, this format is trivially converable to just about any other image format.

The result?

enter image description here

  • There is a lot of temporary files generated by the process. I did not find any parameter which can do the clean-up. Is there any parameter that can do the cleanup? - - Confirmed, output is expected result. It correctly finds the y-axis direction about what is top and bottom. - - What is FOV? I change 10 to 5 and 1 but did not see any difference. I am thinking it is the angle how the stiching is done, but does not seem to be. – Léo Léopold Hertz 준영 Apr 20 '17 at 11:04
  • 1
    @LéoLéopoldHertz준영 cleanup added to the end of the script. – roaima Apr 20 '17 at 11:11
  • 1
    @LéoLéopoldHertz준영 FOV is Field Of View. It's used to control the amount of perspective correction. For these flat files I don't think you'll see any difference regardless of value. – roaima Apr 20 '17 at 11:16
  • I award this answer the bounty because it uses existing code project materials and combines it nicely as an answer. – Léo Léopold Hertz 준영 Apr 22 '17 at 5:12
2

Edited and updated script:

This solution is based on the original images. It will merge two images of any height but the width of the two input images must match.

You would call this script like so:

sh ./name-of-script topImg.jpg bottomImg.jpg

The two input images are checked for a cross over section that matches. If this match is not found, the script will exit.

Only ImageMagick is needed for this to work.

#!/bin/sh
#
# Assume (based on the test images) that the input images are "part of" a
# single image. If not, this will not work :] 
#

# Compare threshold - a value from 0 (100% match) to 100 (nothing alike).
# A low value is a good idea
THRESHOLD=5

# We assume at least 10 pixels overlap
PX=10

# Keep track of the best overlap height
PX_BEST=0

# The overlap diff must be as close to 0 as possible
PX_DIFF=100

# And keep track of the best result so far.
PX_DIFF_BEST=100

# The list of temp files created.
fileList="check1.png check2.png crop2.png"

usage () {
    echo "
Usages:
 ${0##*/} imgTop imgBot

 imgTop: The 'top' image. Height of imgTop does not matter but the 
         width must match the width of the second image.

 imgBot: The 'bottom' image. Same rules as 'imgTop'.

Output will be single image with a file name like:

 imgTop-imgBot-widthxheight.jpg
"
    exit
}

# Check a PX overlap value for a match
checkOverlap () {
    # Cut the overlap amount from the bottom of the top image.
    convert -gravity SouthWest -crop ${W1}x${PX}+0+0 "${1}" +repage check1.png
    # And the top of the bottom image
    convert -gravity NorthWest -crop ${W2}x${PX}+0+0 "${2}" +repage check2.png
    # Compare the two overlap sections
    PX_DIFF=`convert check1.png check2.png -compose Difference -composite -colorspace gray -format '%[fx:mean*100]' info:`
}
# We have a PX_BEST which is the size of the overlap, so crop
# this from top and bottom, then join
cropAndJoin () {
    # Crop the overlap from the bottom image
    H2Crop=$(($H2 - $PX_BEST))
    convert -gravity SouthWest -crop ${W2}x${H2Crop}+0+0 "${2}" +repage crop2.png
    # Join the original top with the cropped bottom
    convert -append "${1}" crop2.png merged.png
    echo ""
}

# clean up the temp images and rename the result.
cleanUp () {
    # Get the name of the orginal images
    tmp1=${1##*/}
    tmp2=${2##*/}
    # remove the file extensions
    tmp1=${tmp1%\.*}
    tmp2=${tmp2%\.*}
    # get the file path for the first image (assume second image is here too)
    tmpPath=${1%/*}
    if [ "$tmpPath" = "$1" ]
    then
        tmpPath="."
    fi
    # the name of the new, merged image
    H=`identify -format '%h' "merged.png"`
    merged="${tmpPath}/${tmp1}-${tmp2}-${W1}x${H}.png"
    # Remove the temp files
    for x in $fileList
    do
        if [ -f "${x}" ]
        then
            rm -f "${x}"
        fi
    done
    if [ -f "merged.png" ]
    then
        mv -f "merged.png" "${merged}"
        echo "
Merged file can be found here:
 ${merged}
"
    else
        echo "
Something went wrong. Maybe the images where not a match?
"
    fi
}

# Basic checks
if [ "$2" = "" ]
then
    usage
fi
if [ ! -f "$1" -o ! -f "$2" ]
then
    usage
fi

# The width and height of the images
W1=`identify -format '%w' "${1}"`
W2=`identify -format '%w' "${2}"`

# The width of the two images must match
if [ $W1 -ne $W2 ]
then
    echo "
Images are not the same width.
"
    exit
fi

# Need to know the height of the images too.
H1=`identify -format '%h' "${1}"`
H2=`identify -format '%h' "${2}"`

# Max height is need to make sure the loop does not go on forever
MAX=$H1

if [ $H1 -gt $H2 ]
then
    MAX=$H2
fi

echo -n "
Looking for a match point "

while [ $PX -lt $MAX ]
do
    echo -n "."
    checkOverlap "${1}" "${2}"
    if [ "$PX_DIFF" != "0" ]
    then
        PX_DIFF=${PX_DIFF%%.*}
    fi
    if [ $PX_DIFF -lt $PX_DIFF_BEST ]
    then
        PX_DIFF_BEST=$PX_DIFF
        PX_BEST=$PX
    fi
    # Check for a perfect match
    if [ $PX_DIFF -eq 0 ]
    then
        PX=$MAX
    else
        PX=$(($PX + 1))
    fi
done

# Check we have a good match, if not tell them.
if [ $PX_DIFF_BEST -gt $THRESHOLD ]
then
    echo "
Unable to find a good match point between the two images.

No merge performed.
"
    exit
fi

# We can assume a good match, try to merge the images.
cropAndJoin "${1}" "${2}"

cleanUp "${1}" "${2}"

echo "Done."
exit
  • 1
    Shortcomings: This answer will only work if the "merged" result of the two "part" images is expected to be 640x480. If that is the case, then this script will always work. If your final result is expected to be any other size, this script will not work. – Tigger Apr 17 '17 at 11:01
  • 1
    If the "final image size" (x by y pixels) is known this script could be changed to suit it. I guess (based on the question) you would only need to know the final "height" of the image. You could get the width from the input images. The height of the input does not matter at all. – Tigger Apr 17 '17 at 11:11
  • 1
    Edited the script to include an "image diff check" of the overlap section. Also worked out a possible way that this script could work if you do not know the final image size, but have not done (or tested) the code for that. I assumed someone else would come up with a better answer. – Tigger Apr 20 '17 at 10:31
  • 1
    OK, Another update. The width and height are no longer required. – Tigger Apr 20 '17 at 12:17
  • 1
    Sorry, just made another minor change to the usage and the final name of the merged file (minor changes only). – Tigger Apr 20 '17 at 12:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.