Reputation: 1628
I've spent a few hours researching this and I've made some progress but still need some help.
I'm aware there are libraries to do this, but since my scenario is terribly simple, I'd rather have my own code. I'm scanning checks in a check scanner, but some images get in crooked and need to be de-skewed;
Here is my code:
Function DeskewImage(Original As Bitmap) As Bitmap
Dim w = Original.Width
Dim h = Original.Height
'i go over the first 200 rows of pixels and for each row i get the first black pixel. this should be enough to get me the angle
Dim FirstBlacks As New List(Of Point)
For row = 0 To 200
For col = 0 To w - 1
Dim p = Original.GetPixel(col, row)
Dim rl = CInt(p.R) + p.G + p.B
If rl < 760 Then
FirstBlacks.Add(New Point(col, row))
Exit For
End If
Next
Next
'here i try to get the angle, im not sure its correct though
Dim xyAvg = FirstBlacks.Select(Function(pnt) pnt.X * pnt.Y).Average
Dim xAvg = FirstBlacks.Select(Function(pnt) pnt.X).Average
Dim yAvg = FirstBlacks.Select(Function(pnt) pnt.Y).Average
Dim xVar = FirstBlacks.Select(Function(pnt) pnt.X).Variance
Dim coefficient = (xyAvg - xAvg * yAvg) / xVar
Dim LeftTop = -20
'now id like utilize the angle to skew
Dim destinationPoints = {New Point(0, LeftTop), New Point(w, 0), New Point(0, h)}
Dim ret = New Bitmap(w, h)
Dim g = Graphics.FromImage(ret)
g.DrawImage(Original, destinationPoints)
ret.Save("D:\aa.jpg")
Return Original
End Function
I have 2 issues:
I also find it odd that I need to specify the height & width of the return image before drawing. How am I supposed to know the size before the skew?
EDIT
thanks to Pieter Geerkens i changed my code to rotating instead of skewing
the problem now remains with the algorithm
first off heres is a sample image
my algorithm above returns a result of approx -0.33 forthis sample pic. where in reality i need approx a +4 degree rotation. so is the algorithm wrong? or does the result need to be converted to degrees? any ideas?
with appreciation
Upvotes: 0
Views: 655
Reputation: 1628
OK. thanks to http://www.alcula.com/calculators/statistics/linear-regression/ i was able to debug my algo results. this tool helped me find out that i was actually mixing up the xs and ys. the correct code follows.
also i had to ignore low x values which are actually the left border, not top border.
Dim ret As Double?
Dim w = Original.Width
Dim h = Original.Height
Dim FirstBlacks = New List(Of Point)
Dim MaxHeight = h - 1
Dim done = False
For y = 0 To MaxHeight
If done Then Exit For
For x = 0 To w - 1
Dim p = Original.GetPixel(x, y)
Dim rl = p.GetBrightness
If rl < 1 Then
If x < 20 Then
done = True
Else
FirstBlacks.Add(New Point(x, y))
End If
Exit For
End If
Next
Next
If FirstBlacks.Count > 2 Then
Dim xyAvg = FirstBlacks.Select(Function(pnt) pnt.X * pnt.Y).Average
Dim xAvg = FirstBlacks.Select(Function(pnt) pnt.X).Average
Dim yAvg = FirstBlacks.Select(Function(pnt) pnt.Y).Average
Dim xVar = FirstBlacks.Select(Function(pnt) pnt.X).Variance
ret = (xyAvg - xAvg * yAvg) / xVar
End If
Return ret
thanks to Pieter-Geerkens for all his help along the way
Upvotes: 0
Reputation: 11893
Try changing the assignment of destinationPoints as follows:
Dim dh = coefficient * h
Dim dw = coefficient * w
Dim destinationPoints = {
New Point(0, LeftTop),
New Point(w,-dw),
New Point(dh, h)
}
Update as per OP note below:
For small angles theta (measured in radians), theta ~ sin(theta) ~ tan(theta). Therefore tha angle of rotation (in degrees) is approximatelyl (180/PI) * coefficient * w / w = (180/PI) * coefficient.
Upvotes: 1