Images can be warped using vector fields. The warp vectors are indicating the location of the source pixel. The code warps an equirectangular projection on an azimuthal projection.

#!/usr/bin/env ruby
require 'hornetseye'
include Hornetseye
raise "Syntax: warp.rb <input file> <output file>" if ARGV.size != 2
class MultiArray
class << self
def ramp( w, h )
idx = int( w, h ).indgen!
retval = MultiArray.int 2, w, h
retval.roll[ 0 ] = idx % w
retval.roll[ 1 ] = idx / w
retval
end
end
end
img = MultiArray.load_ubytergb( ARGV[0] ).downsample [ 2, 2 ], [ 1, 1 ]
w, h = img.shape[0], img.shape[1]
c = 0.5 * h
r = MultiArray.ramp h, h
x, y = r.roll[ 0 ], r.roll[ 1 ]
warp = MultiArray.int 2, h, h
warp.roll[ 0 ] = ( Math.atan2( x - c, y - c ) / Math::PI + 1 ) * w / 2 - 0.5
warp.roll[ 1 ] = Math.hypot( x - c, y - c )
result = MultiArray.ubytergb( w + h + 1, h )
result.roll[ 0 ... w ] = img
result.roll[ w ... (w+1) ] = RGB 0xFF, 0x00, 0x00
result.roll[ (w+1)...(w+h+1) ] = img.warp_clipped warp
result.save_ubytergb ARGV[1]