2020年4月3日金曜日

Excelで画像処理 その3

 Excelで画像処理をしてみました。長かった伏線を経て、ようやく本題です。

 以前に公開しているBitmapと一次方程式のソースコードをコピペしておけば動作するはずです。

 画像の回転とか拡大縮小とか傾けたりとかです。実は当初の目標は射影変換で、画像を傾けることでした。変換のパラメータの計算で一次方程式を解く必要があり、Gaussの消去法を使っています。

 ソースコードは、ご自由にご利用ください。ただし、趣味のプログラムなので、保証はありません。



Option Explicit

Public Const PI = 3.1415926535

Public Sub Transformation_Init()
    '------------------------------
    Dim shp As Shape
    For Each shp In Sheet1.Shapes
        shp.Delete
    Next
 
    Sheet1.Cells.Delete
 
    '------------------------------
    Dim filename As String
 
    filename = ThisWorkbook.Path + "\test.bmp"
    'filename = Application.GetOpenFilename("Bitmap, *.bmp")
    If filename = "False" Then Exit Sub
 
    Sheet1.Cells(4, 6) = "Input Image"
    Sheet1.Cells(5, 6).Select
    ShowImage filename
 
    '------------------------------
    Dim data() As Byte
    Dim width As Long
    Dim height As Long
 
    readBitmap filename, data, width, height
 
    '------------------------------
    Dim row As Long
 
    row = 1
    Sheet1.Cells(row, 1) = "Input File"
    Sheet1.Cells(row, 2) = filename
    Sheet1.Cells(row + 1, 1) = "Output File"
    Sheet1.Cells(row + 1, 2) = ThisWorkbook.Path + "\output.bmp"
 
    row = row + 3
    Sheet1.Cells(row, 1) = "Type"
    Sheet1.Cells(row, 2) = 0
 
    '------------------------------
    row = row + 2
    Sheet1.Cells(row, 1) = "width_i"
    Sheet1.Cells(row, 2) = width
    Sheet1.Cells(row + 1, 1) = "height_i"
    Sheet1.Cells(row + 1, 2) = height
 
    Sheet1.Cells(row + 2, 1) = "width_o"
    Sheet1.Cells(row + 2, 2) = width
    Sheet1.Cells(row + 3, 1) = "height_o"
    Sheet1.Cells(row + 3, 2) = height
 
    '------------------------------
    row = row + 5
    Sheet1.Cells(row, 1) = 0
    Sheet1.Cells(row, 2) = "Resize Image"
    '------------------------------
    row = row + 1
    Sheet1.Cells(row, 1) = 1
    Sheet1.Cells(row, 2) = "Rotate Image"
    Sheet1.Cells(row, 3) = "angle"
    Sheet1.Cells(row, 4) = 30
    '------------------------------
    row = row + 1
    Sheet1.Cells(row, 1) = 2
    Sheet1.Cells(row, 2) = "Affine Transformation"
    Sheet1.Cells(row, 3) = "a0"
    Sheet1.Cells(row + 1, 3) = "a1"
    Sheet1.Cells(row + 2, 3) = "a2"
    Sheet1.Cells(row + 3, 3) = "a3"
    Sheet1.Cells(row + 4, 3) = "a4"
    Sheet1.Cells(row + 5, 3) = "a5"
    Sheet1.Cells(row, 4) = 1
    Sheet1.Cells(row + 1, 4) = 0.8
    Sheet1.Cells(row + 2, 4) = 0
    Sheet1.Cells(row + 3, 4) = 0.5
    Sheet1.Cells(row + 4, 4) = 1
    Sheet1.Cells(row + 5, 4) = 0
    '------------------------------
    row = row + 6
    Sheet1.Cells(row, 1) = 3
    Sheet1.Cells(row, 2) = "Projective Transformation"
    Sheet1.Cells(row, 3) = "xlb"
    Sheet1.Cells(row + 1, 3) = "y_lb"
    Sheet1.Cells(row + 2, 3) = "x_rb"
    Sheet1.Cells(row + 3, 3) = "y_rb"
    Sheet1.Cells(row + 4, 3) = "x_rt"
    Sheet1.Cells(row + 5, 3) = "y_rt"
    Sheet1.Cells(row + 6, 3) = "x_lt"
    Sheet1.Cells(row + 7, 3) = "y_lt"
    Sheet1.Cells(row, 4) = CLng(width / 16)
    Sheet1.Cells(row + 1, 4) = CLng(height / 16)
    Sheet1.Cells(row + 2, 4) = CLng(width * 15 / 16)
    Sheet1.Cells(row + 3, 4) = CLng(height * 2 / 16)
    Sheet1.Cells(row + 4, 4) = CLng(width * 11 / 16)
    Sheet1.Cells(row + 5, 4) = CLng(height * 14 / 16)
    Sheet1.Cells(row + 6, 4) = CLng(width * 3 / 16)
    Sheet1.Cells(row + 7, 4) = CLng(height * 13 / 16)
    '------------------------------
    row = row + 8
    Sheet1.Cells(row, 1) = 4
    Sheet1.Cells(row, 2) = "Distort Image"
    Sheet1.Cells(row, 3) = "range"
    Sheet1.Cells(row + 1, 3) = "power"
    Sheet1.Cells(row, 4) = 64
    Sheet1.Cells(row + 1, 4) = 2
    '------------------------------
End Sub

Public Sub Transformation()
    '------------------------------
    Dim filename As String
    filename = Sheet1.Cells(1, 2)
    If filename = "" Or Dir(filename) = "" Then Exit Sub
 
    '------------------------------
    Dim row As Long
    Dim width_o As Long
    Dim height_o As Long
    Dim angle As Double
    Dim a(5) As Double
    Dim xo(3) As Long
    Dim yo(3) As Long
    Dim range As Double
    Dim power As Double
 
    row = 8
    width_o = Sheet1.Cells(row, 2)
    height_o = Sheet1.Cells(row + 1, 2)
 
    row = 12
    angle = Sheet1.Cells(row, 4)
 
    row = row + 1
    a(0) = Sheet1.Cells(row, 4)
    a(1) = Sheet1.Cells(row + 1, 4)
    a(2) = Sheet1.Cells(row + 2, 4)
    a(3) = Sheet1.Cells(row + 3, 4)
    a(4) = Sheet1.Cells(row + 4, 4)
    a(5) = Sheet1.Cells(row + 5, 4)
 
    row = row + 6
    xo(0) = Sheet1.Cells(row, 4)
    yo(0) = Sheet1.Cells(row + 1, 4)
    xo(1) = Sheet1.Cells(row + 2, 4)
    yo(1) = Sheet1.Cells(row + 3, 4)
    xo(2) = Sheet1.Cells(row + 4, 4)
    yo(2) = Sheet1.Cells(row + 5, 4)
    xo(3) = Sheet1.Cells(row + 6, 4)
    yo(3) = Sheet1.Cells(row + 7, 4)
 
    row = row + 8
    range = Sheet1.Cells(row, 4)
    power = Sheet1.Cells(row + 1, 4)
 
    '------------------------------
    Dim data() As Byte
    Dim width As Long
    Dim height As Long

    readBitmap filename, data, width, height
 
    If Sheet1.Cells(4, 2) = 0 Then
        ResizeImage data, width, height, width_o, height_o
    ElseIf Sheet1.Cells(4, 2) = 1 Then
        RotateImage data, width, height, width_o, height_o, 2 * PI * angle / 360
    ElseIf Sheet1.Cells(4, 2) = 2 Then
        AffineTransformation data, width, height, width_o, height_o, a
    ElseIf Sheet1.Cells(4, 2) = 3 Then
        ProjectiveTransformation data, width, height, width_o, height_o, xo, yo
    ElseIf Sheet1.Cells(4, 2) = 4 Then
        DistortImage data, width, height, width_o, height_o, range, power
    End If
 
    '------------------------------
    filename = Sheet1.Cells(2, 2)
    writeBitmap filename, data, width, height
 
    Sheet1.Cells(4, 10) = "Output Image"
    Sheet1.Cells(5, 10).Select
    ShowImage filename
    '------------------------------
End Sub

Private Sub ShowImage(filename As String)
    Dim shp As Shape
 
    Set shp = Sheet1.Shapes.AddPicture(filename, msoFalse, msoTrue, Selection.Left, Selection.Top, 0, 0)
    shp.ScaleWidth 1, msoTrue
    shp.ScaleHeight 1, msoTrue
    shp.width = shp.width * 200 / shp.height
    shp.height = 200
End Sub



Private Sub GetPixelValue(ByRef data() As Byte, ByRef width As Long, ByRef height As Long, x As Double, y As Double, ByRef r As Byte, ByRef g As Byte, ByRef b As Byte)
    Dim i As Long
    Dim j As Long
    Dim k As Long
    Dim lineSize As Long
    Dim pos As Long
    Dim temp(2) As Double
 
    lineSize = 3 * width + width Mod 4
 
    i = Int(y)
    j = Int(x)
 
    For k = 0 To 2
        '------------------------------
        'No Interpolation
        If 0 <= i And i < height And 0 <= j And j < width Then
            pos = lineSize * i + j * 3
            temp(k) = data(pos + k)
        End If
     
        '------------------------------
        'Bilinear Interpolation
        If 1 <= i And i < height - 1 And 1 <= j And j < width - 1 Then
            pos = lineSize * i + j * 3
     
            temp(k) = (j + 1 - x) * (i + 1 - y) * data(pos + k) _
                + (j + 1 - x) * (y - i) * data(pos + width * 3 + k) _
                + (x - j) * (i + 1 - y) * data(pos + 3 + k) _
                + (x - j) * (y - i) * data(pos + width * 3 + 3 + k)
        End If
 
        If temp(k) < 0 Then temp(k) = 0
        If 255 < temp(k) Then temp(k) = 255
    Next k
 
    r = CByte(temp(2))
    g = CByte(temp(1))
    b = CByte(temp(0))
End Sub

' x_o = x_i * width_o / width_i
' y_o = y_i * height_o / height_i
Private Sub ResizeImage(ByRef data() As Byte, ByRef width_i As Long, ByRef height_i As Long, width_o As Long, height_o As Long)
    Dim i As Long
    Dim j As Long
    Dim lineSize As Long
    Dim pos As Long
    Dim x As Double
    Dim y As Double
 
    lineSize = 3 * width_o + width_o Mod 4
 
    Dim buf() As Byte
    ReDim buf(lineSize * height_o - 1) As Byte
 
    For i = 0 To lineSize * height_o - 1
        buf(i) = 0
    Next i
 
    '------------------------------
    For i = 0 To height_o - 1
        For j = 0 To width_o - 1
            pos = lineSize * i + j * 3
            x = CDbl(j) * width_i / width_o
            y = CDbl(i) * height_i / height_o
         
            GetPixelValue data, width_i, height_i, x, y, buf(pos + 2), buf(pos + 1), buf(pos + 0)
        Next j
    Next i
 
    Erase data
 
    width_i = width_o
    height_i = height_o
    data = buf
End Sub

' x_o = cos * x_i - sin * y_i
' y_o = sin * x_i + cos * y_i
Private Sub RotateImage(ByRef data() As Byte, ByRef width_i As Long, ByRef height_i As Long, width_o As Long, height_o As Long, theta As Double)
    Dim i As Long
    Dim j As Long
    Dim lineSize As Long
    Dim pos As Long
    Dim x As Double
    Dim y As Double
 
    lineSize = 3 * width_o + width_o Mod 4
 
    Dim buf() As Byte
    ReDim buf(lineSize * height_o - 1) As Byte
 
    For i = 0 To lineSize * height_o - 1
        buf(i) = 0
    Next i
 
    '------------------------------
    Dim cos_theta As Double
    Dim sin_theta As Double
    cos_theta = Math.Cos(theta)
    sin_theta = Math.Sin(theta)
 
    '------------------------------
    For i = 0 To height_o - 1
        For j = 0 To width_o - 1
            pos = lineSize * i + j * 3
         
            x = width_i / 2 + (j - width_o / 2) * cos_theta + (i - height_o / 2) * sin_theta
            y = height_i / 2 - (j - width_o / 2) * sin_theta + (i - height_o / 2) * cos_theta
     
            GetPixelValue data, width_i, height_i, x, y, buf(pos + 2), buf(pos + 1), buf(pos + 0)
        Next j
    Next i
 
    Erase data
 
    width_i = width_o
    height_i = height_o
    data = buf
End Sub

' x_o = a(0) * x_i + a(1) * y_i + a(2)
' y_o = a(3) * x_i + a(4) * y_i + a(5)
Private Sub AffineTransformation(ByRef data() As Byte, ByRef width_i As Long, ByRef height_i As Long, width_o As Long, height_o As Long, a() As Double)
    Dim i As Long
    Dim j As Long
    Dim lineSize As Long
    Dim pos As Long
    Dim x As Double
    Dim y As Double
 
    lineSize = 3 * width_o + width_o Mod 4
 
    Dim buf() As Byte
    ReDim buf(lineSize * height_o - 1) As Byte
 
    For i = 0 To lineSize * height_o - 1
        buf(i) = 0
    Next i
 
    '------------------------------
    Dim det As Double
    det = a(0) * a(4) - a(1) * a(3)
 
    '------------------------------
    For i = 0 To height_o - 1
        For j = 0 To width_o - 1
            pos = lineSize * i + j * 3
         
            x = width_i / 2 + ((j - width_o / 2 - a(2)) * a(4) - (i - height_o / 2 - a(5)) * a(3)) / det
            y = height_i / 2 + (-(j - width_o / 2 - a(2)) * a(1) + (i - height_o / 2 - a(5)) * a(0)) / det
         
            GetPixelValue data, width_i, height_i, x, y, buf(pos + 2), buf(pos + 1), buf(pos + 0)
        Next j
    Next i
 
    Erase data
 
    width_i = width_o
    height_i = height_o
    data = buf
End Sub

' x_o = (a0 * x_i + a1 * y_i + a2) / (c0 * x_i + c1 * y_i + 1)
' y_o = (b0 * x_i + b1 * y_i + b2) / (c0 * x_i + c1 * y_i + 1)
Private Sub ProjectiveTransformation(ByRef data() As Byte, ByRef width_i As Long, ByRef height_i As Long, width_o As Long, height_o As Long, xo() As Long, yo() As Long)
    Dim i As Long
    Dim j As Long
    Dim lineSize As Long
    Dim pos As Long
    Dim x As Double
    Dim y As Double
 
    lineSize = 3 * width_o + width_o Mod 4
 
    Dim buf() As Byte
    ReDim buf(lineSize * height_o - 1) As Byte
 
    For i = 0 To lineSize * height_o - 1
        buf(i) = 0
    Next i
 
    '------------------------------
    Dim MatA() As Double
    Dim VecX() As Double
    Dim VecB() As Double
 
    ReDim MatA(8 * 8 - 1) As Double
    ReDim VecX(8 - 1) As Double
    ReDim VecB(8 - 1) As Double
 
    VecB(0) = 0
    VecB(1) = 0
    VecB(2) = width_i
    VecB(3) = 0
    VecB(4) = width_i
    VecB(5) = height_i
    VecB(6) = 0
    VecB(7) = height_i
 
    For i = 0 To 3
        j = 2 * i
        MatA(j * 8 + 0) = xo(i)
        MatA(j * 8 + 1) = yo(i)
        MatA(j * 8 + 2) = 1
        MatA(j * 8 + 3) = 0
        MatA(j * 8 + 4) = 0
        MatA(j * 8 + 5) = 0
        MatA(j * 8 + 6) = -xo(i) * VecB(j)
        MatA(j * 8 + 7) = -yo(i) * VecB(j)
     
        j = j + 1
        MatA(j * 8 + 0) = 0
        MatA(j * 8 + 1) = 0
        MatA(j * 8 + 2) = 0
        MatA(j * 8 + 3) = xo(i)
        MatA(j * 8 + 4) = yo(i)
        MatA(j * 8 + 5) = 1
        MatA(j * 8 + 6) = -xo(i) * VecB(j)
        MatA(j * 8 + 7) = -yo(i) * VecB(j)
    Next i
 
    MatrixSolveGauss MatA, VecX, VecB, 8
 
    '------------------------------
    For i = 0 To height_o - 1
        For j = 0 To width_o - 1
            pos = lineSize * i + j * 3
         
            x = (VecX(0) * j + VecX(1) * i + VecX(2)) / (VecX(6) * j + VecX(7) * i + 1)
            y = (VecX(3) * j + VecX(4) * i + VecX(5)) / (VecX(6) * j + VecX(7) * i + 1)
         
            GetPixelValue data, width_i, height_i, x, y, buf(pos + 2), buf(pos + 1), buf(pos + 0)
        Next j
    Next i
 
    Erase data
 
    width_i = width_o
    height_i = height_o
    data = buf
End Sub

Private Sub DistortImage(ByRef data() As Byte, ByRef width_i As Long, ByRef height_i As Long, width_o As Long, height_o As Long, range As Double, power As Double)
    Dim i As Long
    Dim j As Long
    Dim lineSize As Long
    Dim pos As Long
    Dim x As Double
    Dim y As Double
 
    lineSize = 3 * width_o + width_o Mod 4
 
    Dim buf() As Byte
    ReDim buf(lineSize * height_o - 1) As Byte
 
    For i = 0 To lineSize * height_o - 1
        buf(i) = 0
    Next i
 
    Dim temp As Double
 
    '------------------------------
    For i = 0 To height_o - 1
        For j = 0 To width_o - 1
            pos = lineSize * i + j * 3
         
            temp = Math.Sqr((j - width_o / 2) * (j - width_o / 2) + (i - height_o / 2) * (i - height_o / 2))
         
            If temp < range Then
                temp = (temp / range) ^ (power - 1)
             
                x = width_i / 2 + temp * (j - width_o / 2)
                y = height_i / 2 + temp * (i - height_o / 2)
            Else
                x = width_i / 2 + (j - width_o / 2)
                y = height_i / 2 + (i - height_o / 2)
            End If
         
            GetPixelValue data, width_i, height_i, x, y, buf(pos + 2), buf(pos + 1), buf(pos + 0)
        Next j
    Next i
 
    Erase data
 
    width_i = width_o
    height_i = height_o
    data = buf
End Sub

0 件のコメント:

コメントを投稿