I am creating an app to align 2 (or more) images. I have the land registry’s map and old maps with buildings, streets, terrains. Properly aligned maps then go into QGIS to display them.
To align them to the official map I want to display the official map AND the ancient map together in a picturebox and create routines to change the ancient map: resize the old map to the official maps’ size, move the old map to locate it’s position, rotate the map if North is not where is should be, and squeeze or distort the old map by resizing only one of the 4 sides. This will do to align them exactly.
I struggled for weeks in SO and other sites to find information. Hard to find, I only found something about using he picturebox background image. But that works only for 1 map to align.
I will not publish my tentatives. But the final solution is in the answer below using transparency to mix all images together. f1, f2, f3 images represent the input; uc1, uc2 and uc3 the image while under construction.
In Vb.net, but C# follows the path.
Public Class Form1
'form has:
' top left: a button named Button1
' just below: a PictureBox named myPictureBox, sizeMode=Normal
' 3 bitmaps in 3 files to be shown all together. I asume here that an F: directory is available for read/write
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
myPictureBox.Width = Me.Width
myPictureBox.Height = Me.Height - myPictureBox.Top
Dim f1 As String = "f:f1.jpg"
Dim f2 As String = "f:f2.jpg"
Dim f3 As String = "f:f3.jpg"
Dim bitMap1 As New Bitmap(f1)
Dim bitMap2 As New Bitmap(f2)
Dim bitMap3 As New Bitmap(f3)
Dim underConstruction As New Bitmap(myPictureBox.Width, myPictureBox.Height) ' this will fit in the picturebox
' start with image f1. It will occupy all pixels in the picturebox
Using gfx As Graphics = Graphics.FromImage(underConstruction)
' the DrawImage could be simplified, but like here one knows what one is actually doing
' It will be clear, how to draw only a part of the source in (another part of) the destination.
gfx.DrawImage(image:=bitMap1,
destRect:=New Rectangle(0, 0, myPictureBox.Width, myPictureBox.Height),
srcRect:=New Rectangle(0, 0, myPictureBox.Width, myPictureBox.Height),
srcUnit:=GraphicsUnit.Pixel)
underConstruction.Save("f:uc1.jpg") ' for debug only: these saves will show you exactly what happens
' Add f2 to f1. Drawing a bitmap with 50% opacity will fade half of the existing image and add a faded new image on top.
gfx.DrawImage(image:=FadeBitmap(bitMap2, 50),
destRect:=New Rectangle(0, 0, myPictureBox.Width, myPictureBox.Height),
srcRect:=New Rectangle(0, 0, myPictureBox.Width, myPictureBox.Height),
srcUnit:=GraphicsUnit.Pixel)
underConstruction.Save("f:uc2.jpg")
' Add f3 to the now combined f1/f2 image. Drawimage will fade the combined f1/f2 to only 20% opacity,
' and display f3 on top with 80% opacity. The f1/f2 combined image will hardly remain visible.
gfx.DrawImage(image:=FadeBitmap(bitMap3, 80),
destRect:=New Rectangle(0, 0, myPictureBox.Width, myPictureBox.Height),
srcRect:=New Rectangle(0, 0, myPictureBox.Width, myPictureBox.Height),
srcUnit:=GraphicsUnit.Pixel)
underConstruction.Save("f:uc3.jpg")
End Using
myPictureBox.Image = underConstruction
End Sub
'
' apply transparency
Function FadeBitmap(bmp As Bitmap, OpacityPercent As Single) As Bitmap
Dim tempBmp As New Bitmap(bmp.Width, bmp.Height, Imaging.PixelFormat.Format32bppArgb)
OpacityPercent = Math.Min(OpacityPercent, 100)
Using ia As New Imaging.ImageAttributes
Dim cm As New Imaging.ColorMatrix
cm.Matrix33 = OpacityPercent / 100.0F
ia.SetColorMatrix(cm)
Dim destpoints() As PointF = {New Point(0, 0), New Point(bmp.Width, 0), New Point(0, bmp.Height)}
Using g As Graphics = Graphics.FromImage(tempBmp)
g.Clear(Color.Transparent)
g.DrawImage(bmp, destpoints, New RectangleF(Point.Empty, bmp.Size), GraphicsUnit.Pixel, ia)
End Using
End Using
Return tempBmp
End Function
End Class
These are the images f1, f2,f3 and the debug images uc1, uc2 and uc3 are here: