├── .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 | --------------------------------------------------------------------------------