├── .gitignore
├── App.config
├── DetectChars.vb
├── DetectPlates.vb
├── DocsAndPresentation
├── Steps With Images.docx
├── Steps With Images.pdf
├── form_design.png
├── steps.png
└── steps.txt
├── LicPlateImages
├── 1.png
├── 10.png
├── 11.png
├── 12.png
├── 13.png
├── 14.png
├── 15.png
├── 16.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── 7.png
├── 8.png
└── 9.png
├── License_Plate_Recognition_VB.sln
├── License_Plate_Recognition_VB.vbproj
├── My Project
├── Application.Designer.vb
├── Application.myapp
├── AssemblyInfo.vb
├── Resources.Designer.vb
├── Resources.resx
├── Settings.Designer.vb
└── Settings.settings
├── PossibleChar.vb
├── PossiblePlate.vb
├── Preprocess.vb
├── classifications.xml
├── frmMain.Designer.vb
├── frmMain.resx
├── frmMain.vb
├── images.xml
├── packages.config
└── readme.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
--------------------------------------------------------------------------------
/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/DetectChars.vb:
--------------------------------------------------------------------------------
1 | 'DetectChars.vb
2 | '
3 | 'Emgu CV 2.4.10
4 |
5 | Option Explicit On 'require explicit declaration of variables, this is NOT Python !!
6 | Option Strict On 'restrict implicit data type conversions to only widening conversions
7 |
8 | Imports Emgu.CV '
9 | Imports Emgu.CV.CvEnum 'Emgu Cv imports
10 | Imports Emgu.CV.Structure '
11 | Imports Emgu.CV.UI '
12 | Imports Emgu.CV.ML '
13 | Imports Emgu.CV.Util
14 |
15 | Imports System.Xml
16 | Imports System.Xml.Serialization 'these imports are for reading Matrix objects from file
17 | Imports System.IO
18 |
19 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
20 | Module DetectChars
21 |
22 | ' module level variables ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
23 | 'constants for checkIfPossibleChar, this checks one possible char only (does not compare to another char)
24 | Const MIN_PIXEL_WIDTH As Integer = 2
25 | Const MIN_PIXEL_HEIGHT As Integer = 8
26 |
27 | Const MIN_ASPECT_RATIO As Double = 0.25
28 | Const MAX_ASPECT_RATIO As Double = 1.0
29 |
30 | Const MIN_RECT_AREA As Integer = 80
31 |
32 | 'constants for comparing two chars
33 | Const MIN_DIAG_SIZE_MULTIPLE_AWAY As Double = 0.3
34 | Const MAX_DIAG_SIZE_MULTIPLE_AWAY As Double = 5.0
35 |
36 | Const MAX_CHANGE_IN_AREA As Double = 0.5
37 |
38 | Const MAX_CHANGE_IN_WIDTH As Double = 0.8
39 | Const MAX_CHANGE_IN_HEIGHT As Double = 0.2
40 |
41 | Const MAX_ANGLE_BETWEEN_CHARS As Double = 12.0
42 |
43 | 'other constants
44 | Const MIN_NUMBER_OF_MATCHING_CHARS As Integer = 3
45 |
46 | Const RESIZED_CHAR_IMAGE_WIDTH As Integer = 20
47 | Const RESIZED_CHAR_IMAGE_HEIGHT As Integer = 30
48 |
49 | Dim SCALAR_WHITE As New MCvScalar(255.0, 255.0, 255.0)
50 | Dim SCALAR_GREEN As New MCvScalar(0.0, 255.0, 0.0)
51 |
52 | 'variables
53 | Dim kNearest As New KNearest()
54 |
55 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
56 | Function loadKNNDataAndTrainKNN() As Boolean
57 | 'note: we effectively have to read the first XML file twice
58 | 'first, we read the file to get the number of rows (which is the same as the number of samples)
59 | 'the first time reading the file we can't get the data yet, since we don't know how many rows of data there are
60 | 'next, reinstantiate our classifications Matrix and training images Matrix with the correct number of rows
61 | 'then, read the file again and this time read the data into our resized classifications Matrix and training images Matrix
62 |
63 | Dim mtxClassifications As Matrix(Of Single) = New Matrix(Of Single)(1, 1) 'for the first time through, declare these to be 1 row by 1 column
64 | Dim mtxTrainingImages As Matrix(Of Single) = New Matrix(Of Single)(1, 1) 'we will resize these when we know the number of rows (i.e. number of training samples)
65 |
66 | Dim intValidChars As New List(Of Integer)(New Integer() { Asc("0"), Asc("1"), Asc("2"), Asc("3"), Asc("4"), Asc("5"), Asc("6"), Asc("7"), Asc("8"), Asc("9"), _
67 | Asc("A"), Asc("B"), Asc("C"), Asc("D"), Asc("E"), Asc("F"), Asc("G"), Asc("H"), Asc("I"), Asc("J"), _
68 | Asc("K"), Asc("L"), Asc("M"), Asc("N"), Asc("O"), Asc("P"), Asc("Q"), Asc("R"), Asc("S"), Asc("T"), _
69 | Asc("U"), Asc("V"), Asc("W"), Asc("X"), Asc("Y"), Asc("Z") } )
70 |
71 | Dim xmlSerializer As XmlSerializer = New XmlSerializer(mtxClassifications.GetType) 'these variables are for
72 | Dim streamReader As StreamReader 'reading from the XML files
73 |
74 | Try
75 | streamReader = new StreamReader("classifications.xml") 'attempt to open classifications file
76 | Catch ex As Exception 'if error is encountered, show error and return
77 | frmMain.txtInfo.AppendText(vbCrLf + "unable to open 'classifications.xml', error: ")
78 | frmMain.txtInfo.AppendText(ex.Message + vbCrLf)
79 | Return False
80 | End Try
81 |
82 | 'read from the classifications file the 1st time, this is only to get the number of rows, not the actual data
83 | mtxClassifications = CType(xmlSerializer.Deserialize(streamReader), Matrix(Of Single))
84 |
85 | streamReader.Close() 'close the classifications XML file
86 |
87 | Dim intNumberOfTrainingSamples As Integer = mtxClassifications.Rows 'get the number of rows, i.e. the number of training samples
88 |
89 | 'now that we know the number of rows, reinstantiate classifications Matrix and training images Matrix with the actual number of rows
90 | mtxClassifications = New Matrix(Of Single)(intNumberOfTrainingSamples, 1)
91 | mtxTrainingImages = New Matrix(Of Single)(intNumberOfTrainingSamples, RESIZED_CHAR_IMAGE_WIDTH * RESIZED_CHAR_IMAGE_HEIGHT)
92 |
93 | Try
94 | streamReader = new StreamReader("classifications.xml") 'reinitialize the stream reader
95 | Catch ex As Exception 'if error is encountered, show error and return
96 | frmMain.txtInfo.AppendText(vbCrLf + "unable to open 'classifications.xml', error:" + vbCrLf)
97 | frmMain.txtInfo.AppendText(ex.Message + vbCrLf + vbCrLf)
98 | Return False
99 | End Try
100 | 'read from the classifications file again, this time we can get the actual data
101 | mtxClassifications = CType(xmlSerializer.Deserialize(streamReader), Matrix(Of Single))
102 |
103 | streamReader.Close() 'close the classifications XML file
104 |
105 | xmlSerializer = New XmlSerializer(mtxTrainingImages.GetType) 'reinstantiate file reading variable
106 |
107 | Try
108 | streamReader = New StreamReader("images.xml")
109 | Catch ex As Exception 'if error is encountered, show error and return
110 | frmMain.txtInfo.AppendText("unable to open 'images.xml', error:" + vbCrLf)
111 | frmMain.txtInfo.AppendText(ex.Message + vbCrLf + vbCrLf)
112 | Return False
113 | End Try
114 |
115 | mtxTrainingImages = CType(xmlSerializer.Deserialize(streamReader), Matrix(Of Single)) 'read from training images file
116 | streamReader.Close() 'close the training images XML file
117 |
118 | ' train '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
119 |
120 | kNearest.DefaultK = 1
121 |
122 | kNearest.Train(mtxTrainingImages, MlEnum.DataLayoutType.RowSample, mtxClassifications)
123 |
124 | Return True 'if we got here training was successful so return true
125 | End Function
126 |
127 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
128 | Function detectCharsInPlates(listOfPossiblePlates As List(Of PossiblePlate)) As List(Of PossiblePlate)
129 | Dim intPlateCounter As Integer = 0 'this is only for showing steps
130 | Dim imgContours As Mat
131 | Dim random As New Random() 'this is only for showing steps
132 |
133 | If (listOfPossiblePlates Is Nothing) Then 'if list of possible plates is null,
134 | Return listOfPossiblePlates 'return
135 | ElseIf (listOfPossiblePlates.Count = 0) Then 'if list of possible plates has zero plates
136 | Return listOfPossiblePlates 'return
137 | End If
138 | 'at this point we can be sure list of possible plates has at least one plate
139 |
140 | For Each possiblePlate As PossiblePlate In listOfPossiblePlates ' for each possible plate, this is a big for loop that takes up most of the function
141 | Preprocess.preprocess(possiblePlate.imgPlate, possiblePlate.imgGrayscale, possiblePlate.imgThresh) 'preprocess to get grayscale and threshold images
142 |
143 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
144 | CvInvoke.Imshow("5a", possiblePlate.imgPlate)
145 | CvInvoke.Imshow("5b", possiblePlate.imgGrayscale)
146 | CvInvoke.Imshow("5c", possiblePlate.imgThresh)
147 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
148 |
149 | CvInvoke.Resize(possiblePlate.imgThresh, possiblePlate.imgThresh, New Size(), 1.6, 1.6) 'upscale size by 60% for better viewing and character recognition
150 |
151 | CvInvoke.Threshold(possiblePlate.imgThresh, possiblePlate.imgThresh, 0.0, 255.0, ThresholdType.Binary Or ThresholdType.Otsu) 'threshold again to eliminate any gray areas
152 |
153 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
154 | CvInvoke.Imshow("5d", possiblePlate.imgThresh)
155 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
156 |
157 | 'find all possible chars in the plate,
158 | 'this function first finds all contours, then only includes contours that could be chars (without comparison to other chars yet)
159 | Dim listOfPossibleCharsInPlate As List(Of PossibleChar) = findPossibleCharsInPlate(possiblePlate.imgGrayscale, possiblePlate.imgThresh)
160 |
161 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
162 | imgContours = New Mat(possiblePlate.imgThresh.Size, DepthType.Cv8U, 3)
163 | Dim contours As New VectorOfVectorOfPoint()
164 |
165 | For Each possibleChar As PossibleChar In listOfPossibleCharsInPlate
166 | contours.Push(possibleChar.contour)
167 | Next
168 |
169 | CvInvoke.DrawContours(imgContours, contours, -1, SCALAR_WHITE)
170 |
171 | CvInvoke.Imshow("6", imgContours)
172 |
173 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
174 |
175 | 'given a list of all possible chars, find groups of matching chars within the plate
176 | Dim listOfListsOfMatchingCharsInPlate As List(Of List(Of PossibleChar)) = findListOfListsOfMatchingChars(listOfPossibleCharsInPlate)
177 |
178 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
179 | imgContours = New Mat(possiblePlate.imgThresh.Size, DepthType.Cv8U, 3)
180 |
181 | Dim contours As New VectorOfVectorOfPoint()
182 |
183 | For Each listOfMatchingChars As List(Of PossibleChar) In listOfListsOfMatchingCharsInPlate
184 | Dim intRandomBlue = random.Next(0, 256)
185 | Dim intRandomGreen = random.Next(0, 256)
186 | Dim intRandomRed = random.Next(0, 256)
187 |
188 | For Each matchingChar As PossibleChar In listOfMatchingChars
189 | contours.Push(matchingChar.contour)
190 | Next
191 | CvInvoke.DrawContours(imgContours, contours, -1, New MCvScalar(CDbl(intRandomBlue), CDbl(intRandomGreen), CDbl(intRandomRed)))
192 | Next
193 |
194 | CvInvoke.Imshow("7", imgContours)
195 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
196 |
197 | If (listOfListsOfMatchingCharsInPlate Is Nothing) Then 'if no matching chars were found
198 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''
199 | frmMain.txtInfo.AppendText("chars found in plate number " + intPlateCounter.ToString + " = (none), click on any image and press a key to continue . . ." + vbCrLf)
200 | intPlateCounter = intPlateCounter + 1
201 | CvInvoke.DestroyWindow("8")
202 | CvInvoke.DestroyWindow("9")
203 | CvInvoke.DestroyWindow("10")
204 | CvInvoke.WaitKey(0)
205 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
206 |
207 | possiblePlate.strChars = "" 'set plate string member variable to empty string
208 | Continue For 'and jump back to top of big for loop
209 | ElseIf (listOfListsOfMatchingCharsInPlate.Count = 0) Then
210 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''
211 | frmMain.txtInfo.AppendText("chars found in plate number " + intPlateCounter.ToString + " = (none), click on any image and press a key to continue . . ." + vbCrLf)
212 | intPlateCounter = intPlateCounter + 1
213 | CvInvoke.DestroyWindow("8")
214 | CvInvoke.DestroyWindow("9")
215 | CvInvoke.DestroyWindow("10")
216 | CvInvoke.WaitKey(0)
217 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
218 |
219 | possiblePlate.strChars = "" 'set plate string member variable to empty string
220 | Continue For 'and jump back to top of big for loop
221 | End If
222 |
223 | For i As Integer = 0 To listOfListsOfMatchingCharsInPlate.Count - 1 'for each group of chars within the plate
224 |
225 | 'sort chars from left to right
226 | listOfListsOfMatchingCharsInPlate(i).Sort(Function(oneChar, otherChar) oneChar.boundingRect.X.CompareTo(otherChar.boundingRect.X))
227 |
228 | 'remove inner overlapping chars
229 | listOfListsOfMatchingCharsInPlate(i) = removeInnerOverlappingChars(listOfListsOfMatchingCharsInPlate(i))
230 | Next
231 |
232 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
233 | imgContours = New Mat(possiblePlate.imgThresh.Size, DepthType.Cv8U, 3)
234 |
235 | For Each listOfMatchingChars As List(Of PossibleChar) In listOfListsOfMatchingCharsInPlate
236 | Dim intRandomBlue = random.Next(0, 256)
237 | Dim intRandomGreen = random.Next(0, 256)
238 | Dim intRandomRed = random.Next(0, 256)
239 |
240 | Dim contours As New VectorOfVectorOfPoint()
241 |
242 | For Each matchingChar As PossibleChar In listOfMatchingChars
243 | contours.Push(matchingChar.contour)
244 | Next
245 | CvInvoke.DrawContours(imgContours, contours, -1, New MCvScalar(CDbl(intRandomBlue), CDbl(intRandomGreen), CDbl(intRandomRed)))
246 | Next
247 | CvInvoke.Imshow("8", imgContours)
248 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
249 |
250 | 'within each possible plate, suppose the longest list of potential matching chars is the actual list of chars
251 | Dim intLenOfLongestListOfChars As Integer = 0
252 | Dim intIndexOfLongestListOfChars As Integer = 0
253 | 'loop through all the lists of matching chars, get the index of the one with the most chars
254 | For i As Integer = 0 To listOfListsOfMatchingCharsInPlate.Count - 1
255 | If (listOfListsOfMatchingCharsInPlate(i).Count > intLenOfLongestListOfChars) Then
256 | intLenOfLongestListOfChars = listOfListsOfMatchingCharsInPlate(i).Count
257 | intIndexOfLongestListOfChars = i
258 | End If
259 | Next
260 |
261 | 'suppose that the longest list of matching chars within the plate is the actual list of chars
262 | Dim longestListOfMatchingCharsInPlate As List(Of PossibleChar) = listOfListsOfMatchingCharsInPlate(intIndexOfLongestListOfChars)
263 |
264 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
265 | imgContours = New Mat(possiblePlate.imgThresh.Size, DepthType.Cv8U, 3)
266 |
267 | Dim contours As New VectorOfVectorOfPoint()
268 |
269 | For Each matchingChar As PossibleChar In longestListOfMatchingCharsInPlate
270 | contours.Push(matchingChar.contour)
271 | Next
272 |
273 | CvInvoke.DrawContours(imgContours, contours, -1, SCALAR_WHITE)
274 |
275 | CvInvoke.Imshow("9", imgContours)
276 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
277 |
278 | possiblePlate.strChars = recognizeCharsInPlate(possiblePlate.imgThresh, longestListOfMatchingCharsInPlate) 'perform char recognition on the longest list of matching chars in the plate
279 |
280 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
281 | frmMain.txtInfo.AppendText("chars found in plate number " + intPlateCounter.ToString + " = " + possiblePlate.strChars + ", click on any image and press a key to continue . . ." + vbCrLf)
282 | intPlateCounter = intPlateCounter + 1
283 | CvInvoke.WaitKey(0)
284 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
285 | Next 'end for each possible plate big for loop that takes up most of the function
286 |
287 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
288 | frmMain.txtInfo.AppendText(vbCrLf + "char detection complete, click on any image and press a key to continue . . ." + vbCrLf)
289 | CvInvoke.WaitKey(0)
290 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
291 |
292 | Return listOfPossiblePlates
293 | End Function
294 |
295 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
296 | Function findPossibleCharsInPlate(imgGrayscale As Mat, imgThresh As Mat) As List(Of PossibleChar)
297 | Dim listOfPossibleChars As List(Of PossibleChar) = New List(Of PossibleChar) 'this will be the return value
298 |
299 | Dim imgThreshCopy As New Mat()
300 |
301 | Dim contours As New VectorOfVectorOfPoint()
302 |
303 | imgThreshCopy = imgThresh.Clone()
304 |
305 | CvInvoke.FindContours(imgThreshCopy, contours, Nothing, RetrType.List, ChainApproxMethod.ChainApproxSimple) 'find all contours in plate
306 |
307 | For i As Integer = 0 To contours.Size - 1 'for each contour
308 | Dim possibleChar As New PossibleChar(contours(i))
309 |
310 | If (checkIfPossibleChar(possibleChar)) Then 'if contour is a possible char, note this does not compare to other chars (yet) . . .
311 | listOfPossibleChars.Add(possibleChar) 'add to list of possible chars
312 | End If
313 |
314 | Next
315 |
316 | Return listOfPossibleChars
317 | End Function
318 |
319 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
320 | Function checkIfPossibleChar(possibleChar As PossibleChar) As Boolean
321 | 'this function is a 'first pass' that does a rough check on a contour to see if it could be a char,
322 | 'note that we are not (yet) comparing the char to other chars to look for a group
323 | If (possibleChar.intRectArea > MIN_RECT_AREA And _
324 | possibleChar.boundingRect.Width > MIN_PIXEL_WIDTH And possibleChar.boundingRect.Height > MIN_PIXEL_HEIGHT And _
325 | MIN_ASPECT_RATIO < possibleChar.dblAspectRatio And possibleChar.dblAspectRatio < MAX_ASPECT_RATIO) Then
326 | Return True
327 | Else
328 | Return False
329 | End If
330 | End Function
331 |
332 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
333 | Function findListOfListsOfMatchingChars(listOfPossibleChars As List(Of PossibleChar)) As List(Of List(Of PossibleChar))
334 | 'with this function, we start off with all the possible chars in one big list
335 | 'the purpose of this function is to re-arrange the one big list of chars into a list of lists of matching chars,
336 | 'note that chars that are not found to be in a group of matches do not need to be considered further
337 | Dim listOfListsOfMatchingChars As List(Of List(Of PossibleChar)) = New List(Of List(Of PossibleChar)) 'this will be the return value
338 |
339 | For Each possibleChar As PossibleChar In listOfPossibleChars 'for each possible char in the one big list of chars
340 |
341 | 'find all chars in the big list that match the current char
342 | Dim listOfMatchingChars As List(Of PossibleChar) = findListOfMatchingChars(possibleChar, listOfPossibleChars)
343 |
344 | listOfMatchingChars.Add(possibleChar) 'also add the current char to current possible list of matching chars
345 |
346 | 'if current possible list of matching chars is not long enough to constitute a possible plate
347 | If (listOfMatchingChars.Count < MIN_NUMBER_OF_MATCHING_CHARS) Then
348 | Continue For 'jump back to the top of the for loop and try again with next char, note that it's not necessary
349 | 'to save the list in any way since it did not have enough chars to be a possible plate
350 | End If
351 | 'if we get here, the current list passed test as a "group" or "cluster" of matching chars
352 | listOfListsOfMatchingChars.Add(listOfMatchingChars) 'so add to our list of lists of matching chars
353 |
354 | 'remove the current list of matching chars from the big list so we don't use those same chars twice,
355 | 'make sure to make a new big list for this since we don't want to change the original big list
356 | Dim listOfPossibleCharsWithCurrentMatchesRemoved As List(Of PossibleChar) = listOfPossibleChars.Except(listOfMatchingChars).ToList()
357 |
358 | 'declare new list of lists of chars to get result from recursive call
359 | Dim recursiveListOfListsOfMatchingChars As List(Of List(Of PossibleChar)) = New List(Of List(Of PossibleChar))
360 |
361 | recursiveListOfListsOfMatchingChars = findListOfListsOfMatchingChars(listOfPossibleCharsWithCurrentMatchesRemoved) 'recursive call
362 |
363 | For Each recursiveListOfMatchingChars As List(Of PossibleChar) In recursiveListOfListsOfMatchingChars 'for each list of matching chars found by recursive call
364 | listOfListsOfMatchingChars.Add(recursiveListOfMatchingChars) 'add to our original list of lists of matching chars
365 | Next
366 | Exit For 'jump out of for loop
367 | Next
368 |
369 | Return listOfListsOfMatchingChars 'return result
370 | End Function
371 |
372 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
373 | Function findListOfMatchingChars(possibleChar As PossibleChar, listOfChars As List(Of PossibleChar)) As List(Of PossibleChar)
374 | 'the purpose of this function is, given a possible char and a big list of possible chars,
375 | 'find all chars in the big list that are a match for the single possible char, and return those matching chars as a list
376 | Dim listOfMatchingChars As List(Of PossibleChar) = New List(Of PossibleChar) 'this will be the return value
377 |
378 | For Each possibleMatchingChar As PossibleChar In listOfChars 'for each char in big list
379 |
380 | 'if the char we attempting to find matches for is the exact same char as the char in the big list we are currently checking
381 | If (possibleMatchingChar.Equals(possibleChar)) Then
382 | 'then we should not include it in the list of matches b/c that would end up double including the current char
383 | Continue For 'so do not add to list of matches and jump back to top of for loop
384 | End If
385 | 'compute stuff to see if chars are a match
386 | Dim dblDistanceBetweenChars As Double = distanceBetweenChars(possibleChar, possibleMatchingChar)
387 |
388 | Dim dblAngleBetweenChars As Double = angleBetweenChars(possibleChar, possibleMatchingChar)
389 |
390 | Dim dblChangeInArea As Double = Math.Abs(possibleMatchingChar.intRectArea - possibleChar.intRectArea) / possibleChar.intRectArea
391 |
392 | Dim dblChangeInWidth As Double = Math.Abs(possibleMatchingChar.boundingRect.Width - possibleChar.boundingRect.Width) / possibleChar.boundingRect.Width
393 | Dim dblChangeInHeight As Double = Math.Abs(possibleMatchingChar.boundingRect.Height - possibleChar.boundingRect.Height) / possibleChar.boundingRect.Height
394 |
395 | 'check if chars match
396 | If (dblDistanceBetweenChars < (possibleChar.dblDiagonalSize * MAX_DIAG_SIZE_MULTIPLE_AWAY) And _
397 | dblAngleBetweenChars < MAX_ANGLE_BETWEEN_CHARS And _
398 | dblChangeInArea < MAX_CHANGE_IN_AREA And _
399 | dblChangeInWidth < MAX_CHANGE_IN_WIDTH And _
400 | dblChangeInHeight < MAX_CHANGE_IN_HEIGHT) Then
401 |
402 | listOfMatchingChars.Add(possibleMatchingChar) 'if the chars are a match, add the current char to list of matching chars
403 | End If
404 |
405 | Next
406 |
407 | Return listOfMatchingChars 'return result
408 | End Function
409 |
410 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
411 | 'use Pythagorean theorem to calculate distance between two chars
412 | Function distanceBetweenChars(firstChar As PossibleChar, secondChar As PossibleChar) As Double
413 | Dim intX As Integer = Math.Abs(firstChar.intCenterX - secondChar.intCenterX)
414 | Dim intY As Integer = Math.Abs(firstChar.intCenterY - secondChar.intCenterY)
415 |
416 | Return Math.Sqrt((intX ^ 2) + (intY ^ 2))
417 | End Function
418 |
419 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
420 | 'use basic trigonometry (SOH CAH TOA) to calculate angle between chars
421 | Function angleBetweenChars(firstChar As PossibleChar, secondChar As PossibleChar) As Double
422 | Dim dblAdj As Double = CDbl(Math.Abs(firstChar.intCenterX - secondChar.intCenterX))
423 | Dim dblOpp As Double = CDbl(Math.Abs(firstChar.intCenterY - secondChar.intCenterY))
424 |
425 | Dim dblAngleInRad As Double = Math.Atan(dblOpp / dblAdj)
426 |
427 | Dim dblAngleInDeg As Double = dblAngleInRad * (180.0 / Math.PI)
428 |
429 | Return dblAngleInDeg
430 | End Function
431 |
432 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
433 | 'if we have two chars overlapping or to close to each other to possibly be separate chars, remove the inner (smaller) char,
434 | 'this is to prevent including the same char twice if two contours are found for the same char,
435 | 'for example for the letter 'O' both the inner ring and the outer ring may be found as contours, but we should only include the char once
436 | Function removeInnerOverlappingChars(listOfMatchingChars As List(Of PossibleChar)) As List(Of PossibleChar)
437 | Dim listOfMatchingCharsWithInnerCharRemoved As List(Of PossibleChar) = New List(Of PossibleChar)(listOfMatchingChars)
438 |
439 | For Each currentChar As PossibleChar In listOfMatchingChars
440 | For Each otherChar As PossibleChar In listOfMatchingChars
441 | If (Not currentChar.Equals(otherChar)) Then 'if current char and other char are not the same char . . .
442 | 'if current char and other char have center points at almost the same location . . .
443 | If (distanceBetweenChars(currentChar, otherChar) < (currentChar.dblDiagonalSize * MIN_DIAG_SIZE_MULTIPLE_AWAY)) Then
444 | 'if we get in here we have found overlapping chars
445 | 'next we identify which char is smaller, then if that char was not already removed on a previous pass, remove it
446 | If (currentChar.intRectArea < otherChar.intRectArea) Then 'if current char is smaller than other char
447 | If (listOfMatchingCharsWithInnerCharRemoved.Contains(currentChar)) Then 'if current char was not already removed on a previous pass . . .
448 | listOfMatchingCharsWithInnerCharRemoved.Remove(currentChar) 'then remove current char
449 | End If
450 | Else 'else if other char is smaller than current char
451 | If (listOfMatchingCharsWithInnerCharRemoved.Contains(otherChar)) Then 'if other char was not already removed on a previous pass . . .
452 | listOfMatchingCharsWithInnerCharRemoved.Remove(otherChar) 'then remove other char
453 | End If
454 |
455 | End If
456 | End If
457 | End If
458 | Next
459 | Next
460 |
461 | Return listOfMatchingCharsWithInnerCharRemoved
462 | End Function
463 |
464 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
465 | 'this is where we apply the actual char recognition
466 | Function recognizeCharsInPlate(imgThresh As Mat, listOfMatchingChars As List(Of PossibleChar)) As String
467 | Dim strChars As String = "" 'this will be the return value, the chars in the lic plate
468 |
469 | Dim imgThreshColor As New Mat()
470 |
471 | listOfMatchingChars.Sort(Function(oneChar, otherChar) oneChar.boundingRect.X.CompareTo(otherChar.boundingRect.X)) 'sort chars from left to right
472 |
473 | CvInvoke.CvtColor(imgThresh, imgThreshColor, ColorConversion.Gray2Bgr)
474 |
475 | For Each currentChar As PossibleChar In listOfMatchingChars 'for each char in plate
476 | CvInvoke.Rectangle(imgThreshColor, currentChar.boundingRect, SCALAR_GREEN, 2) 'draw green box around the char
477 |
478 | Dim imgROItoBeCloned As New Mat(imgThresh, currentChar.boundingRect) 'get ROI image of bounding rect
479 |
480 | Dim imgROI As Mat = imgROItoBeCloned.Clone() 'clone ROI image so we don't change original when we resize
481 |
482 | Dim imgROIResized As New Mat()
483 |
484 | 'resize image, this is necessary for char recognition
485 | CvInvoke.Resize(imgROI, imgROIResized, New Size(RESIZED_CHAR_IMAGE_WIDTH, RESIZED_CHAR_IMAGE_HEIGHT))
486 |
487 | 'declare a Matrix of the same dimensions as the Image we are adding to the data structure of training images
488 | Dim mtxTemp As Matrix(Of Single) = New Matrix(Of Single)(imgROIResized.Size())
489 |
490 | 'declare a flattened (only 1 row) matrix of the same total size
491 | Dim mtxTempReshaped As Matrix(Of Single) = New Matrix(Of Single)(1, RESIZED_CHAR_IMAGE_WIDTH * RESIZED_CHAR_IMAGE_HEIGHT)
492 |
493 | imgROIResized.ConvertTo(mtxTemp, DepthType.Cv32F) 'convert Image to a Matrix of Singles with the same dimensions
494 |
495 | For intRow As Integer = 0 To RESIZED_CHAR_IMAGE_HEIGHT - 1 'flatten Matrix into one row by RESIZED_IMAGE_WIDTH * RESIZED_IMAGE_HEIGHT number of columns
496 | For intCol As Integer = 0 To RESIZED_CHAR_IMAGE_WIDTH - 1
497 | mtxTempReshaped(0, (intRow * RESIZED_CHAR_IMAGE_WIDTH) + intCol) = mtxTemp(intRow, intCol)
498 | Next
499 | Next
500 |
501 | Dim sngCurrentChar As Single
502 |
503 | sngCurrentChar = kNearest.Predict(mtxTempReshaped) 'finally we can call Predict !!!
504 |
505 | strChars = strChars + Chr(Convert.ToInt32(sngCurrentChar)) 'append current char to full string of chars
506 | Next
507 |
508 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
509 | CvInvoke.Imshow("10", imgThreshColor)
510 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
511 |
512 | Return strChars 'return result
513 | End Function
514 |
515 | End Module
516 |
517 |
--------------------------------------------------------------------------------
/DetectPlates.vb:
--------------------------------------------------------------------------------
1 | 'DetectPlates.vb
2 | '
3 | 'Emgu CV 3.0.0
4 |
5 | Option Explicit On 'require explicit declaration of variables, this is NOT Python !!
6 | Option Strict On 'restrict implicit data type conversions to only widening conversions
7 |
8 | Imports Emgu.CV '
9 | Imports Emgu.CV.CvEnum 'Emgu Cv imports
10 | Imports Emgu.CV.Structure '
11 | Imports Emgu.CV.UI '
12 | Imports Emgu.CV.Util '
13 |
14 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
15 | Module DetectPlates
16 |
17 | ' module level variables ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
18 | Const PLATE_WIDTH_PADDING_FACTOR As Double = 1.3
19 | Const PLATE_HEIGHT_PADDING_FACTOR As Double = 1.5
20 |
21 | Dim SCALAR_WHITE As New MCvScalar(255.0, 255.0, 255.0)
22 | Dim SCALAR_RED As New MCvScalar(0.0, 0.0, 255.0)
23 |
24 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
25 | Function detectPlatesInScene(imgOriginalScene As Mat) As List(Of PossiblePlate)
26 | Dim listOfPossiblePlates As List(Of PossiblePlate) = New List(Of PossiblePlate) 'this will be the return value
27 |
28 | Dim imgGrayscaleScene As New Mat()
29 | Dim imgThreshScene As New Mat()
30 | Dim imgContours As New Mat(imgOriginalScene.Size, DepthType.Cv8U, 3)
31 |
32 | Dim random As New Random()
33 |
34 | CvInvoke.DestroyAllWindows()
35 |
36 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
37 | CvInvoke.Imshow("0", imgOriginalScene)
38 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
39 |
40 | Preprocess.preprocess(imgOriginalScene, imgGrayscaleScene, imgThreshScene) 'preprocess to get grayscale and threshold images
41 |
42 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
43 | CvInvoke.Imshow("1a", imgGrayscaleScene)
44 | CvInvoke.Imshow("1b", imgThreshScene)
45 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
46 |
47 | 'find all possible chars in the scene,
48 | 'this function first finds all contours, then only includes contours that could be chars (without comparison to other chars yet)
49 | Dim listOfPossibleCharsInScene As List(Of PossibleChar) = findPossibleCharsInScene(imgThreshScene)
50 |
51 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
52 | frmMain.txtInfo.AppendText("step 2 - listOfPossibleCharsInScene.Count = " + listOfPossibleCharsInScene.Count.ToString + vbCrLf) '131 with MCLRNF1 image
53 |
54 | Dim contours As New VectorOfVectorOfPoint()
55 |
56 | For Each possibleChar As PossibleChar In listOfPossibleCharsInScene
57 | contours.Push(possibleChar.contour)
58 | Next
59 |
60 | CvInvoke.DrawContours(imgContours, contours, -1, SCALAR_WHITE)
61 | CvInvoke.Imshow("2b", imgContours)
62 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
63 |
64 | 'given a list of all possible chars, find groups of matching chars
65 | 'in the next steps each group of matching chars will attempt to be recognized as a plate
66 | Dim listOfListsOfMatchingCharsInScene As List(Of List(Of PossibleChar)) = findListOfListsOfMatchingChars(listOfPossibleCharsInScene)
67 |
68 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
69 | frmMain.txtInfo.AppendText("step 3 - listOfListsOfMatchingCharsInScene.Count = " + listOfListsOfMatchingCharsInScene.Count.ToString + vbCrLf) '13 with MCLRNF1 image
70 |
71 | imgContours = New Mat(imgOriginalScene.Size, DepthType.Cv8U, 3)
72 |
73 | For Each listOfMatchingChars As List(Of PossibleChar) In listOfListsOfMatchingCharsInScene
74 | Dim intRandomBlue = random.Next(0, 256)
75 | Dim intRandomGreen = random.Next(0, 256)
76 | Dim intRandomRed = random.Next(0, 256)
77 |
78 | Dim contours As New VectorOfVectorOfPoint()
79 |
80 | For Each matchingChar As PossibleChar In listOfMatchingChars
81 | contours.Push(matchingChar.contour)
82 | Next
83 |
84 | CvInvoke.DrawContours(imgContours, contours, -1, New MCvScalar(CDbl(intRandomBlue), CDbl(intRandomGreen), CDbl(intRandomRed)))
85 |
86 | Next
87 | CvInvoke.Imshow("3", imgContours)
88 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
89 |
90 | For Each listOfMatchingChars As List(Of PossibleChar) In listOfListsOfMatchingCharsInScene 'for each group of matching chars
91 | Dim possiblePlate = extractPlate(imgOriginalScene, listOfMatchingChars) 'attempt to extract plate
92 |
93 | If (Not possiblePlate.imgPlate Is Nothing) Then 'if plate was found
94 | listOfPossiblePlates.Add(possiblePlate) 'add to list of possible plates
95 | End If
96 | Next
97 |
98 | frmMain.txtInfo.AppendText(vbCrLf + listOfPossiblePlates.Count.ToString + " possible plates found" + vbCrLf) 'update text box with # of plates found
99 |
100 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
101 | frmMain.txtInfo.AppendText(vbCrLf)
102 | CvInvoke.Imshow("4a", imgContours)
103 |
104 | For i As Integer = 0 To listOfPossiblePlates.Count - 1
105 | Dim ptfRectPoints(4) As PointF
106 |
107 | ptfRectPoints = listOfPossiblePlates(i).rrLocationOfPlateInScene.GetVertices()
108 |
109 | Dim pt0 As New Point(CInt(ptfRectPoints(0).X), CInt(ptfRectPoints(0).Y))
110 | Dim pt1 As New Point(CInt(ptfRectPoints(1).X), CInt(ptfRectPoints(1).Y))
111 | Dim pt2 As New Point(CInt(ptfRectPoints(2).X), CInt(ptfRectPoints(2).Y))
112 | Dim pt3 As New Point(CInt(ptfRectPoints(3).X), CInt(ptfRectPoints(3).Y))
113 |
114 | CvInvoke.Line(imgContours, pt0, pt1, SCALAR_RED, 2)
115 | CvInvoke.Line(imgContours, pt1, pt2, SCALAR_RED, 2)
116 | CvInvoke.Line(imgContours, pt2, pt3, SCALAR_RED, 2)
117 | CvInvoke.Line(imgContours, pt3, pt0, SCALAR_RED, 2)
118 |
119 | CvInvoke.Imshow("4a", imgContours)
120 | frmMain.txtInfo.AppendText("possible plate " + i.ToString + ", click on any image and press a key to continue . . ." + vbCrLf)
121 | CvInvoke.Imshow("4b", listOfPossiblePlates(i).imgPlate)
122 | CvInvoke.WaitKey(0)
123 | Next
124 | frmMain.txtInfo.AppendText(vbCrLf + "plate detection complete, click on any image and press a key to begin char recognition . . ." + vbCrLf + vbCrLf)
125 | CvInvoke.WaitKey(0)
126 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
127 |
128 | Return listOfPossiblePlates
129 | End Function
130 |
131 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
132 | Function findPossibleCharsInScene(imgThresh As Mat) As List(Of PossibleChar)
133 | Dim listOfPossibleChars As List(Of PossibleChar) = New List(Of PossibleChar) 'this is the return value
134 |
135 | Dim imgContours As New Mat(imgThresh.Size(), DepthType.Cv8U, 3)
136 | Dim intCountOfPossibleChars As Integer = 0
137 |
138 | Dim imgThreshCopy As Mat = imgThresh.Clone()
139 |
140 | Dim contours As New VectorOfVectorOfPoint()
141 |
142 | CvInvoke.FindContours(imgThreshCopy, contours, Nothing, RetrType.List, ChainApproxMethod.ChainApproxSimple) 'find all contours
143 |
144 | For i As Integer = 0 To contours.Size() - 1 'for each contour
145 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''
146 | CvInvoke.DrawContours(imgContours, contours, i, SCALAR_WHITE)
147 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
148 |
149 | Dim possibleChar As New PossibleChar(contours(i))
150 |
151 | If (DetectChars.checkIfPossibleChar(possibleChar)) Then 'if contour is a possible char, note this does not compare to other chars (yet) . . .
152 | intCountOfPossibleChars = intCountOfPossibleChars + 1 'increment count of possible chars
153 | listOfPossibleChars.Add(possibleChar) 'and add to list of possible chars
154 | End If
155 |
156 | Next
157 |
158 | If (frmMain.cbShowSteps.Checked = True) Then ' show steps '''''''''''''''''''''''''''''''''
159 | frmMain.txtInfo.AppendText(vbCrLf + "step 2 - contours.Size() = " + contours.Size().ToString + vbCrLf) '2362 with MCLRNF1 image
160 | frmMain.txtInfo.AppendText("step 2 - intCountOfPossibleChars = " + intCountOfPossibleChars.ToString + vbCrLf) '131 with MCLRNF1 image
161 | CvInvoke.imshow("2a", imgContours)
162 | End If ' show steps '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
163 |
164 | Return listOfPossibleChars
165 | End Function
166 |
167 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
168 | Function extractPlate(imgOriginal As Mat, listOfMatchingChars As List(Of PossibleChar)) As PossiblePlate
169 | Dim possiblePlate As PossiblePlate = New PossiblePlate 'this will be the return value
170 |
171 | 'sort chars from left to right based on x position
172 | listOfMatchingChars.Sort(Function(firstChar, secondChar) firstChar.intCenterX.CompareTo(secondChar.intCenterX))
173 |
174 | 'calculate the center point of the plate
175 | Dim dblPlateCenterX As Double = CDbl(listOfMatchingChars(0).intCenterX + listOfMatchingChars(listOfMatchingChars.Count - 1).intCenterX) / 2.0
176 | Dim dblPlateCenterY As Double = CDbl(listOfMatchingChars(0).intCenterY + listOfMatchingChars(listOfMatchingChars.Count - 1).intCenterY) / 2.0
177 | Dim ptfPlateCenter As New PointF(CSng(dblPlateCenterX), CSng(dblPlateCenterY))
178 |
179 | 'calculate plate width and height
180 | Dim intPlateWidth As Integer = CInt(CDbl(listOfMatchingChars(listOfMatchingChars.Count - 1).boundingRect.X + listOfMatchingChars(listOfMatchingChars.Count - 1).boundingRect.Width - listOfMatchingChars(0).boundingRect.X) * PLATE_WIDTH_PADDING_FACTOR)
181 |
182 | Dim intTotalOfCharHeights As Integer = 0
183 |
184 | For Each matchingChar As PossibleChar In listOfMatchingChars
185 | intTotalOfCharHeights = intTotalOfCharHeights + matchingChar.boundingRect.Height
186 | Next
187 |
188 | Dim dblAverageCharHeight = CDbl(intTotalOfCharHeights) / CDbl(listOfMatchingChars.Count)
189 |
190 | Dim intPlateHeight = CInt(dblAverageCharHeight * PLATE_HEIGHT_PADDING_FACTOR)
191 |
192 | 'calculate correction angle of plate region
193 | Dim dblOpposite As Double = listOfMatchingChars(listOfMatchingChars.Count - 1).intCenterY - listOfMatchingChars(0).intCenterY
194 | Dim dblHypotenuse As Double = DetectChars.distanceBetweenChars(listOfMatchingChars(0), listOfMatchingChars(listOfMatchingChars.Count - 1))
195 | Dim dblCorrectionAngleInRad As Double = Math.Asin(dblOpposite / dblHypotenuse)
196 | Dim dblCorrectionAngleInDeg As Double = dblCorrectionAngleInRad * (180.0 / Math.PI)
197 |
198 | 'assign rotated rect member variable of possible plate
199 | possiblePlate.rrLocationOfPlateInScene = New RotatedRect(ptfPlateCenter, New SizeF(CSng(intPlateWidth), CSng(intPlateHeight)), CSng(dblCorrectionAngleInDeg))
200 |
201 | Dim rotationMatrix As New Mat() 'final steps are to perform the actual rotation
202 | Dim imgRotated As New Mat()
203 | Dim imgCropped As New Mat()
204 |
205 | CvInvoke.GetRotationMatrix2D(ptfPlateCenter, dblCorrectionAngleInDeg, 1.0, rotationMatrix) 'get the rotation matrix for our calculated correction angle
206 |
207 | CvInvoke.WarpAffine(imgOriginal, imgRotated, rotationMatrix, imgOriginal.Size) 'rotate the entire image
208 |
209 | 'crop out the actual plate portion of the rotated image
210 | CvInvoke.GetRectSubPix(imgRotated, possiblePlate.rrLocationOfPlateInScene.MinAreaRect.Size, possiblePlate.rrLocationOfPlateInScene.Center, imgCropped)
211 |
212 | possiblePlate.imgPlate = imgCropped 'copy the cropped plate image into the applicable member variable of the possible plate
213 |
214 | Return possiblePlate
215 | End Function
216 |
217 | End Module
218 |
--------------------------------------------------------------------------------
/DocsAndPresentation/Steps With Images.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/DocsAndPresentation/Steps With Images.docx
--------------------------------------------------------------------------------
/DocsAndPresentation/Steps With Images.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/DocsAndPresentation/Steps With Images.pdf
--------------------------------------------------------------------------------
/DocsAndPresentation/form_design.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/DocsAndPresentation/form_design.png
--------------------------------------------------------------------------------
/DocsAndPresentation/steps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/DocsAndPresentation/steps.png
--------------------------------------------------------------------------------
/DocsAndPresentation/steps.txt:
--------------------------------------------------------------------------------
1 |
2 | 2 classes:
3 |
4 | PossiblePlate
5 |
6 | PossibleChar
7 |
8 |
9 |
10 | find plates
11 |
12 | find chars within plates
13 |
14 |
15 | detectPlatesInScene()
16 |
17 | detectCharsInPlates()
18 |
19 |
20 | imgOriginalScene
21 |
22 | preprocess()
23 |
24 | imgGrayscaleScene, imgThreshScene
25 |
26 | findPossibleCharsInScene()
27 |
28 | listOfPossibleCharsInScene
29 |
30 | findListOfListsOfMatchingChars()
31 |
32 | listOfListsOfMatchingCharsInScene
33 |
34 | extractPlate()
35 |
36 | listOfPossiblePlates
37 |
38 |
39 |
40 |
41 | loadKNNDataAndTrainKNN()
42 |
43 |
44 | listOfPossiblePlates
45 |
46 | preprocess()
47 |
48 | possiblePlate.imgGrayscale, possiblePlate.imgThresh
49 |
50 | findPossibleCharsInPlate()
51 |
52 | listOfPossibleCharsInPlate
53 |
54 | findListOfListsOfMatchingChars()
55 |
56 | listOfListsOfMatchingCharsInPlate
57 |
58 | removeInnerOverlappingChars()
59 |
60 | listOfListsOfMatchingCharsInPlate
61 |
62 | within each possible plate, suppose the longest list of potential matching chars is the actual list of chars
63 |
64 | longestListOfMatchingCharsInPlate
65 |
66 | recognizeCharsInPlate()
67 |
68 | possiblePlate.strChars
69 |
70 |
71 |
72 | listOfPossiblePlates
73 |
74 | suppose the plate with the most recognized chars is the actual plate
75 |
76 | licPlate
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/LicPlateImages/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/1.png
--------------------------------------------------------------------------------
/LicPlateImages/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/10.png
--------------------------------------------------------------------------------
/LicPlateImages/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/11.png
--------------------------------------------------------------------------------
/LicPlateImages/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/12.png
--------------------------------------------------------------------------------
/LicPlateImages/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/13.png
--------------------------------------------------------------------------------
/LicPlateImages/14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/14.png
--------------------------------------------------------------------------------
/LicPlateImages/15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/15.png
--------------------------------------------------------------------------------
/LicPlateImages/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/16.png
--------------------------------------------------------------------------------
/LicPlateImages/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/2.png
--------------------------------------------------------------------------------
/LicPlateImages/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/3.png
--------------------------------------------------------------------------------
/LicPlateImages/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/4.png
--------------------------------------------------------------------------------
/LicPlateImages/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/5.png
--------------------------------------------------------------------------------
/LicPlateImages/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/6.png
--------------------------------------------------------------------------------
/LicPlateImages/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/7.png
--------------------------------------------------------------------------------
/LicPlateImages/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/8.png
--------------------------------------------------------------------------------
/LicPlateImages/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MicrocontrollersAndMore/OpenCV_3_License_Plate_Recognition_Emgu_CV_3_Visual_Basic/0c7df3499593104a8f216339018895c698dc8fc3/LicPlateImages/9.png
--------------------------------------------------------------------------------
/License_Plate_Recognition_VB.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "License_Plate_Recognition_VB", "License_Plate_Recognition_VB.vbproj", "{821D72B3-FB01-40DB-92D6-5B84CA684D83}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {821D72B3-FB01-40DB-92D6-5B84CA684D83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {821D72B3-FB01-40DB-92D6-5B84CA684D83}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {821D72B3-FB01-40DB-92D6-5B84CA684D83}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {821D72B3-FB01-40DB-92D6-5B84CA684D83}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/License_Plate_Recognition_VB.vbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {821D72B3-FB01-40DB-92D6-5B84CA684D83}
8 | WinExe
9 | License_Plate_Recognition_VB.My.MyApplication
10 | License_Plate_Recognition_VB
11 | License_Plate_Recognition_VB
12 | 512
13 | WindowsForms
14 | v4.5.2
15 | true
16 |
17 |
18 |
19 |
20 | AnyCPU
21 | true
22 | full
23 | true
24 | true
25 | bin\Debug\
26 | License_Plate_Recognition_VB.xml
27 | 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
28 | false
29 |
30 |
31 | AnyCPU
32 | pdbonly
33 | false
34 | true
35 | true
36 | bin\Release\
37 | License_Plate_Recognition_VB.xml
38 | 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
39 |
40 |
41 | On
42 |
43 |
44 | Binary
45 |
46 |
47 | Off
48 |
49 |
50 | On
51 |
52 |
53 |
54 | packages\Emgu.CV.3.2.0.2721\lib\net35\Emgu.CV.UI.dll
55 | True
56 |
57 |
58 | packages\Emgu.CV.3.2.0.2721\lib\net35\Emgu.CV.World.dll
59 | True
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | packages\ZedGraph.5.1.5\lib\ZedGraph.dll
73 | True
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | Form
94 |
95 |
96 | frmMain.vb
97 | Form
98 |
99 |
100 |
101 | True
102 | Application.myapp
103 |
104 |
105 | True
106 | True
107 | Resources.resx
108 |
109 |
110 | True
111 | Settings.settings
112 | True
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | frmMain.vb
121 |
122 |
123 | VbMyResourcesResXFileCodeGenerator
124 | Resources.Designer.vb
125 | My.Resources
126 | Designer
127 |
128 |
129 |
130 |
131 | MyApplicationCodeGenerator
132 | Application.Designer.vb
133 |
134 |
135 | SettingsSingleFileGenerator
136 | My
137 | Settings.Designer.vb
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
147 |
148 |
149 |
150 |
157 |
--------------------------------------------------------------------------------
/My Project/Application.Designer.vb:
--------------------------------------------------------------------------------
1 | '------------------------------------------------------------------------------
2 | '
3 | ' This code was generated by a tool.
4 | ' Runtime Version:4.0.30319.42000
5 | '
6 | ' Changes to this file may cause incorrect behavior and will be lost if
7 | ' the code is regenerated.
8 | '
9 | '------------------------------------------------------------------------------
10 |
11 | Option Strict On
12 | Option Explicit On
13 |
14 |
15 | Namespace My
16 |
17 | 'NOTE: This file is auto-generated; do not modify it directly. To make changes,
18 | ' or if you encounter build errors in this file, go to the Project Designer
19 | ' (go to Project Properties or double-click the My Project node in
20 | ' Solution Explorer), and make changes on the Application tab.
21 | '
22 | Partial Friend Class MyApplication
23 |
24 | _
25 | Public Sub New()
26 | MyBase.New(Global.Microsoft.VisualBasic.ApplicationServices.AuthenticationMode.Windows)
27 | Me.IsSingleInstance = false
28 | Me.EnableVisualStyles = true
29 | Me.SaveMySettingsOnExit = true
30 | Me.ShutDownStyle = Global.Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses
31 | End Sub
32 |
33 | _
34 | Protected Overrides Sub OnCreateMainForm()
35 | Me.MainForm = Global.License_Plate_Recognition_VB.frmMain
36 | End Sub
37 | End Class
38 | End Namespace
39 |
--------------------------------------------------------------------------------
/My Project/Application.myapp:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | Form1
5 | false
6 | 0
7 | true
8 | 0
9 | 0
10 | true
11 |
12 |
--------------------------------------------------------------------------------
/My Project/AssemblyInfo.vb:
--------------------------------------------------------------------------------
1 | Imports System
2 | Imports System.Reflection
3 | Imports System.Runtime.InteropServices
4 |
5 | ' General Information about an assembly is controlled through the following
6 | ' set of attributes. Change these attribute values to modify the information
7 | ' associated with an assembly.
8 |
9 | ' Review the values of the assembly attributes
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 'The following GUID is for the ID of the typelib if this project is exposed to COM
21 |
22 |
23 | ' Version information for an assembly consists of the following four values:
24 | '
25 | ' Major Version
26 | ' Minor Version
27 | ' Build Number
28 | ' Revision
29 | '
30 | ' You can specify all the values or you can default the Build and Revision Numbers
31 | ' by using the '*' as shown below:
32 | '
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/My Project/Resources.Designer.vb:
--------------------------------------------------------------------------------
1 | '------------------------------------------------------------------------------
2 | '
3 | ' This code was generated by a tool.
4 | ' Runtime Version:4.0.30319.42000
5 | '
6 | ' Changes to this file may cause incorrect behavior and will be lost if
7 | ' the code is regenerated.
8 | '
9 | '------------------------------------------------------------------------------
10 |
11 | Option Strict On
12 | Option Explicit On
13 |
14 |
15 | Namespace My.Resources
16 |
17 | 'This class was auto-generated by the StronglyTypedResourceBuilder
18 | 'class via a tool like ResGen or Visual Studio.
19 | 'To add or remove a member, edit your .ResX file then rerun ResGen
20 | 'with the /str option, or rebuild your VS project.
21 | '''
22 | ''' A strongly-typed resource class, for looking up localized strings, etc.
23 | '''
24 | _
28 | Friend Module Resources
29 |
30 | Private resourceMan As Global.System.Resources.ResourceManager
31 |
32 | Private resourceCulture As Global.System.Globalization.CultureInfo
33 |
34 | '''
35 | ''' Returns the cached ResourceManager instance used by this class.
36 | '''
37 | _
38 | Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
39 | Get
40 | If Object.ReferenceEquals(resourceMan, Nothing) Then
41 | Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("License_Plate_Recognition_VB.Resources", GetType(Resources).Assembly)
42 | resourceMan = temp
43 | End If
44 | Return resourceMan
45 | End Get
46 | End Property
47 |
48 | '''
49 | ''' Overrides the current thread's CurrentUICulture property for all
50 | ''' resource lookups using this strongly typed resource class.
51 | '''
52 | _
53 | Friend Property Culture() As Global.System.Globalization.CultureInfo
54 | Get
55 | Return resourceCulture
56 | End Get
57 | Set(ByVal value As Global.System.Globalization.CultureInfo)
58 | resourceCulture = value
59 | End Set
60 | End Property
61 | End Module
62 | End Namespace
63 |
--------------------------------------------------------------------------------
/My Project/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/My Project/Settings.Designer.vb:
--------------------------------------------------------------------------------
1 | '------------------------------------------------------------------------------
2 | '
3 | ' This code was generated by a tool.
4 | ' Runtime Version:4.0.30319.42000
5 | '
6 | ' Changes to this file may cause incorrect behavior and will be lost if
7 | ' the code is regenerated.
8 | '
9 | '------------------------------------------------------------------------------
10 |
11 | Option Strict On
12 | Option Explicit On
13 |
14 |
15 | Namespace My
16 |
17 | _
20 | Partial Friend NotInheritable Class MySettings
21 | Inherits Global.System.Configuration.ApplicationSettingsBase
22 |
23 | Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings)
24 |
25 | #Region "My.Settings Auto-Save Functionality"
26 | #If _MyType = "WindowsForms" Then
27 | Private Shared addedHandler As Boolean
28 |
29 | Private Shared addedHandlerLockObject As New Object
30 |
31 | _
32 | Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs)
33 | If My.Application.SaveMySettingsOnExit Then
34 | My.Settings.Save()
35 | End If
36 | End Sub
37 | #End If
38 | #End Region
39 |
40 | Public Shared ReadOnly Property [Default]() As MySettings
41 | Get
42 |
43 | #If _MyType = "WindowsForms" Then
44 | If Not addedHandler Then
45 | SyncLock addedHandlerLockObject
46 | If Not addedHandler Then
47 | AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
48 | addedHandler = True
49 | End If
50 | End SyncLock
51 | End If
52 | #End If
53 | Return defaultInstance
54 | End Get
55 | End Property
56 | End Class
57 | End Namespace
58 |
59 | Namespace My
60 |
61 | _
64 | Friend Module MySettingsProperty
65 |
66 | _
67 | Friend ReadOnly Property Settings() As Global.License_Plate_Recognition_VB.My.MySettings
68 | Get
69 | Return Global.License_Plate_Recognition_VB.My.MySettings.Default
70 | End Get
71 | End Property
72 | End Module
73 | End Namespace
74 |
--------------------------------------------------------------------------------
/My Project/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PossibleChar.vb:
--------------------------------------------------------------------------------
1 | 'PossibleChar.vb
2 | '
3 | 'Emgu CV 3.0.0
4 |
5 | Option Explicit On 'require explicit declaration of variables, this is NOT Python !!
6 | Option Strict On 'restrict implicit data type conversions to only widening conversions
7 |
8 | Imports System.Math
9 |
10 | Imports Emgu.CV '
11 | Imports Emgu.CV.CvEnum 'Emgu Cv imports
12 | Imports Emgu.CV.Structure '
13 | Imports Emgu.CV.UI '
14 | Imports Emgu.CV.Util '
15 |
16 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
17 | Public Class PossibleChar
18 |
19 | ' member variables ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
20 | Public contour As VectorOfPoint
21 |
22 | Public boundingRect As Rectangle
23 |
24 | Public intCenterX As Integer
25 | Public intCenterY As Integer
26 |
27 | Public dblDiagonalSize As Double
28 | Public dblAspectRatio As Double
29 | Public intRectArea As Integer
30 |
31 | ' constructor '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
32 | Sub New(_contour As VectorOfPoint)
33 | contour = _contour
34 |
35 | boundingRect = CvInvoke.BoundingRectangle(contour)
36 |
37 | intCenterX = CInt((boundingRect.Left + boundingRect.Right) / 2)
38 | intCenterY = CInt((boundingRect.Top + boundingRect.Bottom) / 2)
39 |
40 | dblDiagonalSize = Math.Sqrt((boundingRect.Width ^ 2) + (boundingRect.Height ^ 2))
41 |
42 | dblAspectRatio = CDbl(boundingRect.Width) / CDbl(boundingRect.Height)
43 |
44 | intRectArea = boundingRect.Width * boundingRect.Height
45 | End Sub
46 |
47 | End Class
48 |
--------------------------------------------------------------------------------
/PossiblePlate.vb:
--------------------------------------------------------------------------------
1 | 'PossiblePlate.vb
2 | '
3 | 'Emgu CV 3.0.0
4 |
5 | Option Explicit On 'require explicit declaration of variables, this is NOT Python !!
6 | Option Strict On 'restrict implicit data type conversions to only widening conversions
7 |
8 | Imports Emgu.CV '
9 | Imports Emgu.CV.CvEnum 'Emgu Cv imports
10 | Imports Emgu.CV.Structure '
11 | Imports Emgu.CV.UI '
12 |
13 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
14 | Public Class PossiblePlate
15 |
16 | ' member variables ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
17 | Public imgPlate As Mat
18 | Public imgGrayscale As Mat
19 | Public imgThresh As Mat
20 |
21 | Public rrLocationOfPlateInScene As RotatedRect
22 |
23 | Public strChars As String
24 |
25 | ' constructor '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
26 | Sub New
27 | 'initialize values
28 | imgPlate = New Mat()
29 | imgGrayscale = New Mat()
30 | imgThresh = New Mat()
31 |
32 | rrLocationOfPlateInScene = New RotatedRect()
33 |
34 | strChars = ""
35 | End Sub
36 |
37 | End Class
38 |
--------------------------------------------------------------------------------
/Preprocess.vb:
--------------------------------------------------------------------------------
1 | 'Preprocess.vb
2 | '
3 | 'Emgu CV 3.0.0
4 |
5 | Option Explicit On 'require explicit declaration of variables, this is NOT Python !!
6 | Option Strict On 'restrict implicit data type conversions to only widening conversions
7 |
8 | Imports Emgu.CV '
9 | Imports Emgu.CV.CvEnum 'Emgu Cv imports
10 | Imports Emgu.CV.Structure '
11 | Imports Emgu.CV.UI '
12 | Imports Emgu.CV.Util '
13 |
14 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
15 | Module Preprocess
16 |
17 | ' module level variables ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
18 | Const GAUSSIAN_BLUR_FILTER_SIZE As Integer = 5
19 | Const ADAPTIVE_THRESH_BLOCK_SIZE As Integer = 19
20 | Const ADAPTIVE_THRESH_WEIGHT As Integer = 9
21 |
22 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
23 | Sub preprocess(imgOriginal As Mat, ByRef imgGrayscale As Mat, ByRef imgThresh As Mat)
24 | imgGrayscale = extractValue(imgOriginal) 'extract value channel only from original image to get imgGrayscale
25 |
26 | Dim imgMaxContrastGrayscale As Mat = maximizeContrast(imgGrayscale) 'maximize contrast with top hat and black hat
27 |
28 | Dim imgBlurred As New Mat()
29 |
30 | CvInvoke.GaussianBlur(imgMaxContrastGrayscale, imgBlurred, New Size(GAUSSIAN_BLUR_FILTER_SIZE, GAUSSIAN_BLUR_FILTER_SIZE), 0) 'gaussian blur
31 |
32 | 'adaptive threshold to get imgThresh
33 | CvInvoke.AdaptiveThreshold(imgBlurred, imgThresh, 255.0, AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, ADAPTIVE_THRESH_BLOCK_SIZE, ADAPTIVE_THRESH_WEIGHT)
34 | End Sub
35 |
36 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
37 | Function extractValue(imgOriginal As Mat) As Mat
38 | Dim imgHSV As New Mat()
39 | Dim vectorOfHSVImages As New VectorOfMat()
40 | Dim imgValue As New Mat()
41 |
42 | CvInvoke.CvtColor(imgOriginal, imgHSV, ColorConversion.Bgr2Hsv)
43 |
44 | CvInvoke.Split(imgHSV, vectorOfHSVImages)
45 |
46 | imgValue = vectorOfHSVImages(2)
47 |
48 | Return imgValue
49 | End Function
50 |
51 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
52 | Function maximizeContrast(imgGrayscale As Mat) As Mat
53 | Dim imgTopHat As New Mat()
54 | Dim imgBlackHat As New Mat()
55 | Dim imgGrayscalePlusTopHat As New Mat()
56 | Dim imgGrayscalePlusTopHatMinusBlackHat As New Mat()
57 |
58 | Dim structuringElement As Mat = CvInvoke.GetStructuringElement(ElementShape.Rectangle, New Size(3, 3), New Point(-1, -1))
59 |
60 | CvInvoke.MorphologyEx(imgGrayscale, imgTopHat, MorphOp.Tophat, structuringElement, New Point(-1, -1), 1, BorderType.Default, New MCvScalar())
61 | CvInvoke.MorphologyEx(imgGrayscale, imgBlackHat, MorphOp.Blackhat, structuringElement, New Point(-1, -1), 1, BorderType.Default, New MCvScalar())
62 |
63 | CvInvoke.Add(imgGrayscale, imgTopHat, imgGrayscalePlusTopHat)
64 | CvInvoke.Subtract(imgGrayscalePlusTopHat, imgBlackHat, imgGrayscalePlusTopHatMinusBlackHat)
65 |
66 | Return imgGrayscalePlusTopHatMinusBlackHat
67 | End Function
68 |
69 | End Module
70 |
--------------------------------------------------------------------------------
/classifications.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | AAC0QgAAqEIAAKRCAACgQgAAmkIAAIxCAACKQgAAiEIAAIRCAACCQgAAskIAALBCAACuQgAArEIAAKpCAACmQgAAokIAAJ5CAACcQgAAmEIAAJZCAACUQgAAkkIAAJBCAACOQgAAhkIAAGRCAABgQgAAXEIAAFhCAABUQgAATEIAAEhCAABAQgAAUEIAAERCAACkQgAAoEIAAJpCAACIQgAAhEIAAIJCAAC0QgAAskIAALBCAACuQgAArEIAAKpCAACoQgAApkIAAKJCAACeQgAAnEIAAJhCAACWQgAAlEIAAJJCAACQQgAAjkIAAIxCAACKQgAAhkIAAFBCAABEQgAAZEIAAGBCAABcQgAAWEIAAFRCAABMQgAASEIAAEBCAAC0QgAAskIAALBCAACuQgAArEIAAKpCAACoQgAApkIAAKRCAACiQgAAoEIAAJ5CAACcQgAAmkIAAJhCAACWQgAAlEIAAJJCAACQQgAAjkIAAIxCAACKQgAAiEIAAIZCAACEQgAAgkIAAERCAABkQgAAYEIAAFxCAABYQgAAVEIAAFBCAABMQgAASEIAAEBCAAC0QgAAskIAALBCAACuQgAArEIAAKpCAACoQgAApEIAAKBCAACcQgAAmkIAAJhCAACWQgAAlEIAAJJCAACQQgAAjEIAAIpCAACIQgAAhEIAAIJCAACmQgAAokIAAJ5CAACOQgAAhkIAAFxCAABMQgAASEIAAERCAABkQgAAYEIAAFhCAABUQgAAUEIAAEBCAACkQgAAoEIAAJpCAACIQgAAhEIAAIJCAAC0QgAAskIAALBCAACuQgAArEIAAKpCAACoQgAApkIAAKJCAACeQgAAnEIAAJhCAACWQgAAlEIAAJJCAACQQgAAjkIAAIxCAACKQgAAhkIAAFxCAABUQgAAUEIAAGRCAABgQgAAWEIAAExCAABIQgAAREIAAEBC
4 |
--------------------------------------------------------------------------------
/frmMain.Designer.vb:
--------------------------------------------------------------------------------
1 | _
2 | Partial Class frmMain
3 | Inherits System.Windows.Forms.Form
4 |
5 | 'Form overrides dispose to clean up the component list.
6 | _
7 | Protected Overrides Sub Dispose(ByVal disposing As Boolean)
8 | Try
9 | If disposing AndAlso components IsNot Nothing Then
10 | components.Dispose()
11 | End If
12 | Finally
13 | MyBase.Dispose(disposing)
14 | End Try
15 | End Sub
16 |
17 | 'Required by the Windows Form Designer
18 | Private components As System.ComponentModel.IContainer
19 |
20 | 'NOTE: The following procedure is required by the Windows Form Designer
21 | 'It can be modified using the Windows Form Designer.
22 | 'Do not modify it using the code editor.
23 | _
24 | Private Sub InitializeComponent()
25 | Me.components = New System.ComponentModel.Container()
26 | Me.tableLayoutPanel = New System.Windows.Forms.TableLayoutPanel()
27 | Me.openFileDialog = New System.Windows.Forms.OpenFileDialog()
28 | Me.cbShowSteps = New System.Windows.Forms.CheckBox()
29 | Me.lblChosenFile = New System.Windows.Forms.Label()
30 | Me.btnOpenFile = New System.Windows.Forms.Button()
31 | Me.ibOriginal = New Emgu.CV.UI.ImageBox()
32 | Me.txtInfo = New System.Windows.Forms.TextBox()
33 | Me.tableLayoutPanel.SuspendLayout()
34 | CType(Me.ibOriginal, System.ComponentModel.ISupportInitialize).BeginInit()
35 | Me.SuspendLayout()
36 | '
37 | 'tableLayoutPanel
38 | '
39 | Me.tableLayoutPanel.ColumnCount = 3
40 | Me.tableLayoutPanel.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle())
41 | Me.tableLayoutPanel.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
42 | Me.tableLayoutPanel.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle())
43 | Me.tableLayoutPanel.Controls.Add(Me.cbShowSteps, 2, 0)
44 | Me.tableLayoutPanel.Controls.Add(Me.lblChosenFile, 1, 0)
45 | Me.tableLayoutPanel.Controls.Add(Me.btnOpenFile, 0, 0)
46 | Me.tableLayoutPanel.Controls.Add(Me.ibOriginal, 0, 1)
47 | Me.tableLayoutPanel.Controls.Add(Me.txtInfo, 0, 2)
48 | Me.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill
49 | Me.tableLayoutPanel.Location = New System.Drawing.Point(0, 0)
50 | Me.tableLayoutPanel.Name = "tableLayoutPanel"
51 | Me.tableLayoutPanel.RowCount = 3
52 | Me.tableLayoutPanel.RowStyles.Add(New System.Windows.Forms.RowStyle())
53 | Me.tableLayoutPanel.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 70.0!))
54 | Me.tableLayoutPanel.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 30.0!))
55 | Me.tableLayoutPanel.Size = New System.Drawing.Size(1325, 844)
56 | Me.tableLayoutPanel.TabIndex = 0
57 | '
58 | 'openFileDialog
59 | '
60 | Me.openFileDialog.FileName = "OpenFileDialog1"
61 | '
62 | 'cbShowSteps
63 | '
64 | Me.cbShowSteps.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
65 | Me.cbShowSteps.AutoSize = True
66 | Me.cbShowSteps.Font = New System.Drawing.Font("Microsoft Sans Serif", 10.8!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
67 | Me.cbShowSteps.Location = New System.Drawing.Point(1190, 6)
68 | Me.cbShowSteps.Name = "cbShowSteps"
69 | Me.cbShowSteps.Size = New System.Drawing.Size(132, 28)
70 | Me.cbShowSteps.TabIndex = 0
71 | Me.cbShowSteps.Text = "Show Steps"
72 | Me.cbShowSteps.UseVisualStyleBackColor = True
73 | '
74 | 'lblChosenFile
75 | '
76 | Me.lblChosenFile.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
77 | Me.lblChosenFile.AutoSize = True
78 | Me.lblChosenFile.Font = New System.Drawing.Font("Microsoft Sans Serif", 10.8!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
79 | Me.lblChosenFile.Location = New System.Drawing.Point(113, 8)
80 | Me.lblChosenFile.Name = "lblChosenFile"
81 | Me.lblChosenFile.Size = New System.Drawing.Size(1071, 24)
82 | Me.lblChosenFile.TabIndex = 1
83 | Me.lblChosenFile.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
84 | '
85 | 'btnOpenFile
86 | '
87 | Me.btnOpenFile.AutoSize = True
88 | Me.btnOpenFile.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
89 | Me.btnOpenFile.Font = New System.Drawing.Font("Microsoft Sans Serif", 10.8!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
90 | Me.btnOpenFile.Location = New System.Drawing.Point(3, 3)
91 | Me.btnOpenFile.Name = "btnOpenFile"
92 | Me.btnOpenFile.Size = New System.Drawing.Size(104, 34)
93 | Me.btnOpenFile.TabIndex = 2
94 | Me.btnOpenFile.Text = "Open File"
95 | Me.btnOpenFile.UseVisualStyleBackColor = True
96 | '
97 | 'ibOriginal
98 | '
99 | Me.ibOriginal.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
100 | Me.tableLayoutPanel.SetColumnSpan(Me.ibOriginal, 3)
101 | Me.ibOriginal.Dock = System.Windows.Forms.DockStyle.Fill
102 | Me.ibOriginal.Enabled = False
103 | Me.ibOriginal.Location = New System.Drawing.Point(3, 43)
104 | Me.ibOriginal.Name = "ibOriginal"
105 | Me.ibOriginal.Size = New System.Drawing.Size(1319, 556)
106 | Me.ibOriginal.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom
107 | Me.ibOriginal.TabIndex = 2
108 | Me.ibOriginal.TabStop = False
109 | '
110 | 'txtInfo
111 | '
112 | Me.tableLayoutPanel.SetColumnSpan(Me.txtInfo, 3)
113 | Me.txtInfo.Dock = System.Windows.Forms.DockStyle.Fill
114 | Me.txtInfo.Font = New System.Drawing.Font("Courier New", 10.8!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
115 | Me.txtInfo.Location = New System.Drawing.Point(3, 605)
116 | Me.txtInfo.Multiline = True
117 | Me.txtInfo.Name = "txtInfo"
118 | Me.txtInfo.ScrollBars = System.Windows.Forms.ScrollBars.Both
119 | Me.txtInfo.Size = New System.Drawing.Size(1319, 236)
120 | Me.txtInfo.TabIndex = 3
121 | Me.txtInfo.WordWrap = False
122 | '
123 | 'frmMain
124 | '
125 | Me.AutoScaleDimensions = New System.Drawing.SizeF(8.0!, 16.0!)
126 | Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
127 | Me.ClientSize = New System.Drawing.Size(1325, 844)
128 | Me.Controls.Add(Me.tableLayoutPanel)
129 | Me.Name = "frmMain"
130 | Me.Text = "Form1"
131 | Me.tableLayoutPanel.ResumeLayout(False)
132 | Me.tableLayoutPanel.PerformLayout()
133 | CType(Me.ibOriginal, System.ComponentModel.ISupportInitialize).EndInit()
134 | Me.ResumeLayout(False)
135 |
136 | End Sub
137 |
138 | Friend WithEvents tableLayoutPanel As TableLayoutPanel
139 | Friend WithEvents openFileDialog As OpenFileDialog
140 | Friend WithEvents cbShowSteps As CheckBox
141 | Friend WithEvents lblChosenFile As Label
142 | Friend WithEvents btnOpenFile As Button
143 | Friend WithEvents ibOriginal As Emgu.CV.UI.ImageBox
144 | Friend WithEvents txtInfo As TextBox
145 | End Class
146 |
--------------------------------------------------------------------------------
/frmMain.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
--------------------------------------------------------------------------------
/frmMain.vb:
--------------------------------------------------------------------------------
1 | 'frmMain.vb
2 | '
3 | 'add the following components to your form:
4 | 'tableLayoutPanel (TableLayoutPanel)
5 | 'btnOpenFile (Button)
6 | 'lblChosenFile (Label)
7 | 'ibOriginal (ImageBox)
8 | 'txtInfo (TextBox)
9 | 'cbShowSteps (CheckBox)
10 | 'ofdOpenFile (OpenFileDialog)
11 |
12 | Option Explicit On 'require explicit declaration of variables, this is NOT Python !!
13 | Option Strict On 'restrict implicit data type conversions to only widening conversions
14 |
15 | Imports Emgu.CV '
16 | Imports Emgu.CV.CvEnum 'usual Emgu Cv imports
17 | Imports Emgu.CV.Structure '
18 | Imports Emgu.CV.UI '
19 | Imports Emgu.CV.Util
20 |
21 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
22 | Public Class frmMain
23 |
24 | ' module level variables ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
25 | Const IMAGE_BOX_PCT_SHOW_STEPS_NOT_CHECKED As Single = 75 'these are for changing the proportion of image box to text box based on if we are showing steps or not
26 | Const TEXT_BOX_PCT_SHOW_STEPS_NOT_CHECKED As Single = 25
27 |
28 | Const IMAGE_BOX_PCT_SHOW_STEPS_CHECKED As Single = 55 'the idea is to show more of the text box if we are showing steps since there is more text to display
29 | Const TEXT_BOX_PCT_SHOW_STEPS_CHECKED As Single = 45
30 |
31 | Dim SCALAR_RED As New MCvScalar(0.0, 0.0, 255.0)
32 | Dim SCALAR_YELLOW As New MCvScalar(0.0, 255.0, 255.0)
33 |
34 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
35 | Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
36 | cbShowSteps_CheckedChanged(New Object, New EventArgs) 'call check box event to update form based on check box initial state
37 |
38 | Dim blnKNNTrainingSuccessful As Boolean = loadKNNDataAndTrainKNN() 'attempt KNN training
39 |
40 | If (blnKNNTrainingSuccessful = False) Then 'if KNN training was not successful
41 | txtInfo.AppendText(vbCrLf + "error: KNN traning was not successful" + vbCrLf) 'show message on text box
42 | MsgBox("error: KNN traning was not successful") 'also show message box
43 | btnOpenFile.Enabled = False 'disable open file button
44 | Return 'and bail
45 | End If
46 | End Sub
47 |
48 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
49 | Private Sub cbShowSteps_CheckedChanged(sender As Object, e As EventArgs) Handles cbShowSteps.CheckedChanged
50 | If (cbShowSteps.Checked = False) Then
51 | tableLayoutPanel.RowStyles.Item(1).Height = IMAGE_BOX_PCT_SHOW_STEPS_NOT_CHECKED 'if showing steps, show more of the text box
52 | tableLayoutPanel.RowStyles.Item(2).Height = TEXT_BOX_PCT_SHOW_STEPS_NOT_CHECKED
53 | ElseIf (cbShowSteps.Checked = True) Then
54 | tableLayoutPanel.RowStyles.Item(1).Height = IMAGE_BOX_PCT_SHOW_STEPS_CHECKED 'if not showing steps, show less of the text box
55 | tableLayoutPanel.RowStyles.Item(2).Height = TEXT_BOX_PCT_SHOW_STEPS_CHECKED
56 | End If
57 | End Sub
58 |
59 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
60 | Private Sub btnOpenFile_Click(sender As Object, e As EventArgs) Handles btnOpenFile.Click
61 | Dim imgOriginalScene As New Mat() 'this is the original image scene
62 |
63 | Dim blnImageOpenedSuccessfully = openImageWithErrorHandling(imgOriginalScene) 'attempt to open image
64 |
65 | If (Not blnImageOpenedSuccessfully) Then 'if image was not opened successfully
66 | ibOriginal.Image = Nothing 'set the image box on the form to blank
67 | Return 'and bail
68 | End If
69 |
70 | lblChosenFile.Text = openFileDialog.FileName 'update label with file name
71 |
72 | CvInvoke.DestroyAllWindows() 'close any windows that are open from previous button press
73 |
74 | ibOriginal.Image = imgOriginalScene 'show original image on main form
75 |
76 | Dim listOfPossiblePlates As List(Of PossiblePlate) = DetectPlates.detectPlatesInScene(imgOriginalScene) 'detect plates
77 |
78 | listOfPossiblePlates = DetectChars.detectCharsInPlates(listOfPossiblePlates) 'detect chars in plates
79 |
80 | If (listOfPossiblePlates Is Nothing) Then 'check if list of plates is null or zero
81 | txtInfo.AppendText(vbCrLf + "no license plates were detected" + vbCrLf)
82 | ElseIf (listOfPossiblePlates.Count = 0) Then
83 | txtInfo.AppendText(vbCrLf + "no license plates were detected" + vbCrLf)
84 | Else
85 | 'if we get in here list of possible plates has at leat one plate
86 |
87 | 'sort the list of possible plates in DESCENDING order (most number of chars to least number of chars)
88 | listOfPossiblePlates.Sort(Function(onePlate, otherPlate) otherPlate.strChars.Length.CompareTo(onePlate.strChars.Length))
89 |
90 | 'suppose the plate with the most recognized chars
91 | Dim licPlate As PossiblePlate = listOfPossiblePlates(0) '(the first plate in sorted by string length descending order)
92 | 'is the actual plate
93 |
94 | CvInvoke.Imshow("final imgPlate", licPlate.imgPlate) 'show the final color plate image
95 | CvInvoke.Imshow("final imgThresh", licPlate.imgThresh) 'show the final thresh plate image
96 |
97 | If (licPlate.strChars.Length = 0) Then 'if no chars are present in the lic plate,
98 | txtInfo.AppendText(vbCrLf + "no characters were detected" + licPlate.strChars + vbCrLf) 'update info text box
99 | Return 'and return
100 | End If
101 |
102 | drawRedRectangleAroundPlate(imgOriginalScene, licPlate) 'draw red rectangle around plate
103 |
104 | txtInfo.AppendText(vbCrLf + "license plate read from image = " + licPlate.strChars + vbCrLf) 'write license plate text to text box
105 | txtInfo.AppendText(vbCrLf + "----------------------------------------" + vbCrLf)
106 |
107 | writeLicensePlateCharsOnImage(imgOriginalScene, licPlate) 'write license plate text on the image
108 |
109 | ibOriginal.Image = imgOriginalScene 'update image on main form
110 |
111 | CvInvoke.Imwrite("imgOriginalScene.png", imgOriginalScene) 'write image out to file
112 | End If
113 | End Sub
114 |
115 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
116 | Function openImageWithErrorHandling(ByRef imgOriginalScene As Mat) As Boolean
117 | Dim drChosenFile As DialogResult
118 |
119 | drChosenFile = openFileDialog.ShowDialog() 'open file dialog
120 |
121 | If (drChosenFile <> DialogResult.OK Or openFileDialog.FileName = "") Then 'if user did not choose anything
122 | lblChosenFile.Text = "file not chosen" 'update label
123 | Return False 'and bail
124 | End If
125 |
126 | Try
127 | imgOriginalScene = CvInvoke.Imread(openFileDialog.FileName)
128 | Catch ex As Exception 'if error occurred
129 | lblChosenFile.Text = "unable to open image, error: " + ex.Message 'show error message on label
130 | Return False 'and exit function
131 | End Try
132 |
133 | If (imgOriginalScene Is Nothing) Then 'if image could not be opened
134 | lblChosenFile.Text = "unable to open image, image was null" 'show error message on label
135 | Return False 'and exit function
136 | End If
137 |
138 | If (imgOriginalScene.IsEmpty()) Then 'if image opened as empty
139 | lblChosenFile.Text = "unable to open image, image was empty" 'show error message on label
140 | Return False 'and exit function
141 | End If
142 |
143 | Return True
144 | End Function
145 |
146 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
147 | Sub drawRedRectangleAroundPlate(imgOriginalScene As Mat, licPlate As PossiblePlate)
148 | Dim ptfRectPoints(4) As PointF 'declare array of 4 points, floating point type
149 |
150 | ptfRectPoints = licPlate.rrLocationOfPlateInScene.GetVertices() 'get 4 vertices of rotated rect
151 |
152 | Dim pt0 As New Point(CInt(ptfRectPoints(0).X), CInt(ptfRectPoints(0).Y)) 'declare 4 points, integer type
153 | Dim pt1 As New Point(CInt(ptfRectPoints(1).X), CInt(ptfRectPoints(1).Y))
154 | Dim pt2 As New Point(CInt(ptfRectPoints(2).X), CInt(ptfRectPoints(2).Y))
155 | Dim pt3 As New Point(CInt(ptfRectPoints(3).X), CInt(ptfRectPoints(3).Y))
156 |
157 | CvInvoke.Line(imgOriginalScene, pt0, pt1, SCALAR_RED, 2) 'draw 4 red lines
158 | CvInvoke.Line(imgOriginalScene, pt1, pt2, SCALAR_RED, 2)
159 | CvInvoke.Line(imgOriginalScene, pt2, pt3, SCALAR_RED, 2)
160 | CvInvoke.Line(imgOriginalScene, pt3, pt0, SCALAR_RED, 2)
161 | End Sub
162 |
163 | '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
164 | Sub writeLicensePlateCharsOnImage(ByRef imgOriginalScene As Mat, licPlate As PossiblePlate)
165 | Dim ptCenterOfTextArea As New Point() 'this will be the center of the area the text will be written to
166 | Dim ptLowerLeftTextOrigin As New Point() 'this will be the bottom left of the area that the text will be written to
167 |
168 | Dim fontFace As FontFace = FontFace.HersheySimplex 'choose a plain jane font
169 | Dim dblFontScale As Double = licPlate.imgPlate.Height / 30 'base font scale on height of plate area
170 | Dim intFontThickness As Integer = CInt(dblFontScale * 1.5) 'base font thickness on font scale
171 | Dim textSize As New Size()
172 |
173 | 'to get the text size, we should use the OpenCV function getTextSize, but for some reason Emgu CV does not include this
174 | 'we can instead estimate the test size based on the font scale, this will not be especially accurate but is good enough for our purposes here
175 | textSize.Width = CInt(dblFontScale * 18.5 * licPlate.strChars.Length)
176 | textSize.Height = CInt(dblFontScale * 25)
177 |
178 | ptCenterOfTextArea.X = CInt(licPlate.rrLocationOfPlateInScene.Center.X) 'the horizontal location of the text area is the same as the plate
179 |
180 | If (licPlate.rrLocationOfPlateInScene.Center.Y < (imgOriginalScene.Height * 0.75)) Then 'if the license plate is in the upper 3/4 of the image, we will write the chars in below the plate
181 | ptCenterOfTextArea.Y = CInt(licPlate.rrLocationOfPlateInScene.Center.Y + CInt(CDbl(licPlate.rrLocationOfPlateInScene.MinAreaRect.Height) * 1.6))
182 | Else 'else if the license plate is in the lower 1/4 of the image, we will write the chars in above the plate
183 | ptCenterOfTextArea.Y = CInt(licPlate.rrLocationOfPlateInScene.Center.Y - CInt(CDbl(licPlate.rrLocationOfPlateInScene.MinAreaRect.Height) * 1.6))
184 | End If
185 |
186 | ptLowerLeftTextOrigin.X = CInt(ptCenterOfTextArea.X - (textSize.Width / 2)) 'calculate the lower left origin of the text area
187 | ptLowerLeftTextOrigin.Y = CInt(ptCenterOfTextArea.Y + (textSize.Height / 2)) 'based on the text area center, width, and height
188 |
189 | CvInvoke.PutText(imgOriginalScene, licPlate.strChars, ptLowerLeftTextOrigin, fontFace, dblFontScale, SCALAR_YELLOW, intFontThickness) 'write the text on the image
190 | End Sub
191 |
192 | End Class
193 |
194 |
--------------------------------------------------------------------------------
/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | The video pretty much explains it all:
2 | https://www.youtube.com/watch?v=VfpjswS95mo
3 |
--------------------------------------------------------------------------------