snibgo's ImageMagick pages

Squishing images

When images have transparent areas, we can append such that opaque areas abut each other.

Graphics images may have transparent areas. We might want to squish two images together, appending vertically or horizontally but with an overlap. And we might want to maximise the overlap so that opaque pixels from one image don't quite overlap opaque pixels from the other image.

This is like the "-append" operator but ignoring transparent pixels.

Sample images

%IM%convert ^
  -size 300x200 xc:None ^
  -fill Blue -draw "circle 20,-20 20,150" ^
  sqi_src1.png
sqi_src1.png
%IM%convert ^
  -size 500x300 xc:None ^
  -fill Green -draw "circle 400,400 400,20" ^
  sqi_src2.png
sqi_src2.png

The method

The method adjusts the vertical position so the lower opaque image doesn't quite touch the upper image. We find the solution by trial and error.

Because of anti-aliasing, touching or not touching isn't really a binary condition. There are degrees of touching:

  1. FROM: touching starts to occur when a lower pixel that isn't entirely transparent overlaps an upper pixel that isn't entirely transparent;
  2. TO: touching starts to occur later, only when a fully-opaque lower pixel overlaps a fully-opaque upper pixel.

This script uses a middle definition: touching has occurred when the sum of the opacities of any overlapping pixels exceeds 110%. To avoid a need for HDRI, we divide opacity by 2 and test for a limit of 55%.

The script squishAppend.bat extracts the alpha and levels it, so black represents transparent and 50% gray represents opaque, and then calls whatSquish.bat. This conducts trial overlaps of N rows, where N increases from one. For each trial, it crops the bottom N rows from the first image and the top N rows from the second, then adds the two cropped images together. When the maximum pixel in the addition is more than 55%, we have gone too far, so we back off one.

call %PICTBAT%squishAppend sqi_src1.png sqi_src2.png
sa_outfile.png

If the two objects had no overlap horizontally, then the images could be made to overlap to any degree. The script limits the overlap to the minimum height of the two images.

These scripts will find only a vertical overlap. If a horizontal overlap is wanted instead, the inputs and output should be rotated.

This simple linear search gives adequate performance for my needs. More sophisticated searches could improve performance.

Squishing lines of text

We can apply the method to complex shapes like text. First, we create a simple text file:

(
  echo Hello world.
  echo How is
  echo your
  echo world?
) >sqi_text.txt

The file contains:

Hello world.
How is
your
world?

Here is a non-squished example. The script textAutoWi.bat creates images for the lines, setting the pointsizes to make the lines roughly the same width. Then it trims and appends vertically, using the ordinary "-append" operation.

call %PICTBAT%textAutoWi ^
  sqi_text.txt
sqi_text_taw.png

In the last two lines, the bottom of "your" lines up with the top of "world" but a gap could be closed.

We can close all the gaps between text, so that text on one line doesn't quite ovelap text on the previous line. The script textAutoWiSquish.bat creates one image per line of text, appending it to the image made so far by calling squishAppend.bat.

call %PICTBAT%textAutoWiSquish ^
  sqi_text.txt
sqi_text_taws.png

The result generally has the appearance of a gap of around 1 pixel. Aesthetically, it may be better to either have a deliberate overlap between objects, or a clear gap between them. We do this by reducing or increasing the gap between the lines of text:

Use gap of -2 (overlap).

call %PICTBAT%textAutoWiSquish ^
  sqi_text.txt . -2 sqi_text_taws2.png
sqi_text_taws2.png

Use gap of +2 (separation).

call %PICTBAT%textAutoWiSquish ^
  sqi_text.txt . 2 sqi_text_taws3.png
sqi_text_taws3.png

Squishing words and strings

Perhaps we also want to squish the characters within each word such as "your". First, we find the pointsize that gives the required width.

for /F "usebackq" %%L in (`%IM%convert ^
  -size 300 label:your -format "PNT_SZ=%%[label:pointsize]" ^
  info:`) do set %%L

echo PNT_SZ=%PNT_SZ% 
PNT_SZ=152.25 

(Versions v6.9.2-5 and v7.0.1-4 give slightly different pointsizes.)

Using "label:" at this pointsize, we make an image for each letter, rotate them 90° clockwise, and store them in a single miff file.

%IM%convert ^
  -pointsize %PNT_SZ% ^
  -background None ^
  label:y label:o label:u label:r ^
  -rotate 90 ^
  sqi_your.miff

We squish the four images together, and rotate anticlockwise.

set GAP=0

call %PICTBAT%squishAppend ^
  sqi_your.miff[0] ^
  sqi_your.miff[1] ^
  %GAP% ^
  sqi_your_out.miff

call %PICTBAT%squishAppend ^
  sqi_your_out.miff ^
  sqi_your.miff[2] ^
  %GAP% ^
  sqi_your_out.miff

call %PICTBAT%squishAppend ^
  sqi_your_out.miff ^
  sqi_your.miff[3] ^
  %GAP% ^
  sqi_your_out.miff

%IM%convert sqi_your_out.miff -rotate -90 sqi_your_out.png

sqi_your_out.png

sqi_your_out.png

We can put this together in a script, squishStr.bat.

call %PICTBAT%squishStr ^
  "World's Away." . . ^
  sqi_ss.png
sqi_ss.png

The space character has been squished away. We can set sqsSPACE_GAP, which will be used instead of the gap parameter when the previous character was a space.

set sqsSPACE_GAP=5

call %PICTBAT%squishStr ^
  "World's Away." . . ^
  sqi_ss2.png

set sqsSPACE_GAP=
sqi_ss2.png

Scripts

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

squishAppend.bat

rem Given two images %1 and %2 with binary (more or less) transparency,
rem how much can we overlap them vertically?
rem %3 is amount to increase y-gap (or decrease if negative).
rem %4 is output.

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 sa

set INFILE1=%1
set INFILE2=%2

set deltaY=%3
if "%deltaY%"=="." set deltaY=
if "%deltaY%"=="" set deltaY=0

if not "%4"=="" set OUTFILE=%4

%IM%convert ^
  %INFILE1% ^
  %INFILE2% ^
  -alpha extract ^
  +level 0,50%% ^
  sa_tmp.miff

call %PiCTBAT%whatSquish sa_tmp.miff[0] sa_tmp.miff[1]

echo wsOver=%wsNover% wsGX=%wsGX% wsGY=%wsGY%

set /A wsGY+=%deltaY%

%IM%convert ^
  %INFILE1% ^
  ( %INFILE2% -repage +%wsGX%+%wsGY% ) ^
  -background None ^
  -layers merge ^
  +repage ^
  %OUTFILE%


call echoRestore

endlocal & set saOUTFILE=%OUTFILE%

whatSquish.bat

rem Given images %1 and %2 each with maximum values 0.5,
rem how much can we overlap them vertically?

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 ws

set INFILE1=%1
set INFILE2=%2

set WW1=
for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW1=%%w\nHH1=%%h" ^
  %INFILE1%`) do set %%L
if "%WW1%"=="" exit /B 1

set WW2=
for /F "usebackq" %%L in (`%IM%identify ^
  -format "WW2=%%w\nHH2=%%h" ^
  %INFILE2%`) do set %%L
if "%WW2%"=="" exit /B 1

echo %WW1%x%HH1% %WW2%x%HH2%

set MIN_H=%HH1%
if %MIN_H% GTR %HH2% set MIN_H=%HH2%

set N=0

:loop
set /A N+=1

for /F "usebackq" %%L in (`%IM%convert ^
  ^( %INFILE1% ^
    -gravity South ^
    -crop %WW1%x%N%+0+0 +repage ^) ^
  ^( %INFILE2% ^
    -gravity North ^
    -crop %WW2%x%N%+0+0 +repage ^) ^
  -gravity Center ^
  -compose Add -composite ^
  +write x.png ^
  -format "mx=%%[fx:maxima]\nIsWhite=%%[fx:(maxima>0.55)?1:0]" ^
  info:`) do set %%L

if %N% LSS %MIN_H% if %IsWhite%==0 goto loop

rem FIXME: Do we need a limit?

set /A N-=1

set /A gy=%HH1%-%N%
set /A gx=(%WW1%-%WW2%)/2

echo N=%N%


call echoRestore

@endlocal & set wsNover=%N%& set wsGX=%gx%& set wsGY=%gy%

textAutoWi.bat

rem From text file %1,
rem make image up to %2 pixels wide.
rem Output to %3.

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 taw

set WW=%2
if "%WW%"=="." set WW=
if "%WW%"=="" set WW=300

set SCR_FILE=textaw.scr


echo xc: +delete >%SCR_FILE%

echo off
( for /F "tokens=*" %%L in (%INFILE%) do (
    echo -size %WW%x label:"%%L"
  )
  echo -trim +repage +write info: -verbose -gravity Center -append +write "%OUTFILE%"
) >>%SCR_FILE%
echo on


call %PICTBAT%setIm7Path

%IMG7%magick -script %SCR_FILE%

type %SCR_FILE%

call echoRestore

@endlocal & set tawOUTFILE=%OUTFILE%

textAutoWiSquish.bat

rem From text file %1,
rem make image up to %2 pixels wide.
rem Text will be black, on transparent background.
rem Squishes, with extra gap %3.
rem Output to %4.

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

@setlocal enabledelayedexpansion

@call echoOffSave

call %PICTBAT%setInOut %1 taws

set WW=%2
if "%WW%"=="." set WW=
if "%WW%"=="" set WW=300

set deltaY=%3
if "%deltaY%"=="." set deltaY=
if "%deltaY%"=="" set deltaY=0

if not "%4"=="" set OUTFILE=%4


set TMP0=taws_tmp0.miff
set TMP1=taws_tmp1.miff

set N=0
for /F "tokens=*" %%L in (%INFILE%) do (

  if !N!==0 (
    %IM%convert -background None -size %WW%x label:"%%L" -trim +repage %TMP0%
  ) else (
    %IM%convert -background None -size %WW%x label:"%%L" -trim +repage %TMP1%
    call %PICTBAT%squishAppend %TMP0% %TMP1% %deltaY% %TMP0%
  )

  set /A N+=1
)

%IM%convert %TMP0% %OUTFILE%

call echoRestore

@endlocal & set tawOUTFILE=%OUTFILE%

squishStr.bat

rem Given string %1 (possibly quoted)
rem makes image approx %2 pixels wide
rem then squishes characters with gap %3
rem Output to image %4.
rem
rem Also uses:
rem   sqsSPACE_GAP Gap to be used when previous character is space. [0]

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

@setlocal enabledelayedexpansion

@call echoOffSave

rem call %PICTBAT%setInOut %1 sqs

set STR=%1
set strNq=%~1

set WW=%2
if "%WW%"=="." set WW=
if "%WW%"=="" set WW=300

set GAP=%3
if "%GAP%"=="." set GAP=
if "%GAP%"=="" set GAP=0

set OUTFILE=%4
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=sqs_out.png

if "%sqsSPACE_GAP%"=="" set sqsSPACE_GAP=0

set LETTERS_MIFF=sqs_your.miff
set OUT_MIFF=sqs_out.miff

for /F "usebackq" %%L in (`%IM%convert ^
  -size 300 label:%STR% -format "PNT_SZ=%%[label:pointsize]" ^
  info:`) do set %%L

set N=0

set sLABELS=
:loop
set C=!strNq:~%N%,1!

if defined C (
  set sLABELS=%sLABELS% label:"%C%"
  set /A N+=1
  goto loop
)

echo N=%N% sLABELS=%sLABELS%

%IM%convert ^
  -pointsize %PNT_SZ% ^
  -background None ^
  %sLABELS% ^
  -rotate 90 ^
  %LETTERS_MIFF%

set /A Nm1=%N%-1

set PREV_SP=0
for /L %%I in (1,1,%Nm1%) do (
  if %%I==1 (
    set fZero=%LETTERS_MIFF%[0]
  ) else (
    set fZero=%OUT_MIFF%
  )

  set GP=%GAP%
  if !PREV_SP!==1 (
    set GP=%sqsSPACE_GAP%
    set PREV_SP=0
  )

  set C=!strNq:~%%I,1!
  if "!C!"==" " set PREV_SP=1

  call %PICTBAT%squishAppend ^
    !fZero! ^
    %LETTERS_MIFF%[%%I] ^
    !GP! ^
    %OUT_MIFF%
)

%IM%convert %OUT_MIFF% -rotate -90 %OUTFILE%

call echoRestore

@endlocal & set sqsOUTFILE=%OUTFILE%

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

%IM%identify -version
Version: ImageMagick 6.9.2-5 Q16 x64 2015-10-31 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180031101
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib

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

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


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 31-March-2016.

Page created 20-Jul-2016 20:26:16.

Copyright © 2016 Alan Gibson.