upptäcka maskinläsbara zoner i passbilder
dagens blogginlägg skulle inte vara möjligt utan PyImageSearch Gurus medlem, Hans Boone. Hans arbetar med ett datorvisionsprojekt för att automatiskt upptäcka maskinläsbara zoner (MRZ) i passbilder-ungefär som regionen som upptäckts i bilden ovan.
MRZ-regionen i pass eller resekort faller i två klasser: typ 1 och typ 3. Typ 1 MRZ är tre rader, där varje rad innehåller 30 tecken. Typ 3 MRZ har bara två rader, men varje rad innehåller 44 tecken. I båda fallen kodar MRZ identifierande information för en viss medborgare, inklusive typ av pass, pass-ID, utfärdande land, namn, nationalitet, utgångsdatum etc.
inne i PyImageSearch Gurus-kursen visade Hans mig sina framsteg på projektet och jag blev omedelbart intresserad. Jag har alltid velat tillämpa datorvisionsalgoritmer på passbilder (främst bara för skojs skull), men saknade datasetet för att göra det. Med tanke på den personliga identifieringsinformation som ett pass innehåller kunde jag självklart inte skriva ett blogginlägg om ämnet och dela de bilder jag använde för att utveckla algoritmen.
lyckligtvis gick Hans med på att dela några av de prov/provpassbilder han har tillgång till — och jag hoppade på chansen att leka med dessa bilder.
nu, innan vi kommer långt, är det viktigt att notera att dessa pass inte är ”riktiga” i den meningen att de kan kopplas till en verklig människa. Men de är äkta pass som genererades med falska namn,adresser etc. för utvecklare att arbeta med.
du kanske tror att för att upptäcka MRZ — regionen i ett pass behöver du lite maskininlärning, kanske med hjälp av Linear SVM + HOG-ramverket för att konstruera en ”MRZ-detektor” – men det skulle vara överkill.
istället kan vi utföra MRZ-detektering med endast grundläggande bildbehandlingstekniker som tröskelvärde, morfologiska operationer och konturegenskaper. I resten av detta blogginlägg kommer jag att beskriva min egen uppfattning om hur man tillämpar dessa metoder för att upptäcka MRZ-regionen i ett pass.
letar du efter källkoden till det här inlägget?
hoppa direkt till avsnittet Nedladdningar
upptäcka maskinläsbara zoner i passbilder
Låt oss gå vidare och få det här projektet igång. Öppna en ny fil, namnge den detect_mrz.py
och sätt in följande kod:
# import the necessary packagesfrom imutils import pathsimport numpy as npimport argparseimport imutilsimport cv2# construct the argument parse and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-i", "--images", required=True, help="path to images directory")args = vars(ap.parse_args())# initialize a rectangular and square structuring kernelrectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (13, 5))sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 21))
linjer 2-6 importerar våra nödvändiga paket. Jag antar att du redan har OpenCV installerat. Du behöver också imutils, min samling av bekvämlighetsfunktioner för att göra grundläggande bildbehandlingsoperationer med OpenCV enklare. Du kan installera imutils
med pip
:
$ pip install --upgrade imutils
därifrån hanterar rader 9-11 tolkning av vårt kommandoradsargument. Vi behöver bara en enda omkopplare här, --images
, vilket är sökvägen till katalogen som innehåller passbilderna vi ska bearbeta.
slutligen initierar linjerna 14 och 15 två kärnor som vi senare kommer att använda vid tillämpning av morfologiska operationer, särskilt stängningsoperationen. För tillfället noterar du bara att den första kärnan är rektangulär med en bredd ungefär 3x större än höjden. Den andra kärnan är kvadratisk. Dessa kärnor gör att vi kan stänga luckor mellan MRZ-tecken och öppningar mellan MRZ-linjer.
nu när våra kommandoradsargument analyseras kan vi börja looping över var och en av bilderna i vår dataset och bearbeta dem:
# loop over the input image pathsfor imagePath in paths.list_images(args):# load the image, resize it, and convert it to grayscaleimage = cv2.imread(imagePath)image = imutils.resize(image, height=600)gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# smooth the image using a 3x3 Gaussian, then apply the blackhat# morphological operator to find dark regions on a light backgroundgray = cv2.GaussianBlur(gray, (3, 3), 0)blackhat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, rectKernel)
linjerna 20 och 21 laddar vår originalbild från disken och ändrar storlek på den för att ha en maximal höjd på 600 pixlar. Du kan se ett exempel på en originalbild nedan:
Gaussisk oskärpa appliceras på rad 26 för att minska högfrekvent brus. Vi tillämpar sedan en blackhat morfologisk operation på den suddiga gråskalebilden på rad 27.
en blackhat-operatör används för att avslöja mörka områden (dvs. MRZ-text) mot ljusa bakgrunder (dvs. bakgrunden till själva passet). Eftersom passtexten alltid är svart på en ljus bakgrund (åtminstone när det gäller denna dataset) är en blackhat-operation lämplig. Nedan kan du se resultatet av att använda blackhat-operatören:
nästa steg i MRZ-detektering är att beräkna gradientstorleksrepresentationen av blackhat-bilden med hjälp av Scharr-operatören:
# compute the Scharr gradient of the blackhat image and scale the# result into the range gradX = cv2.Sobel(blackhat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)gradX = np.absolute(gradX)(minVal, maxVal) = (np.min(gradX), np.max(gradX))gradX = (255 * ((gradX - minVal) / (maxVal - minVal))).astype("uint8")
här beräknar vi Scharr-gradienten längs x-axeln för blackhat-bilden, vilket avslöjar regioner i bilden som inte bara är mörka mot en ljus bakgrund utan också innehåller vertikala förändringar i lutningen, såsom MRZ-textregionen. Vi tar sedan denna gradientbild och skalar den tillbaka till intervallet med min / max skalning:
även om det inte är helt uppenbart varför vi tillämpar detta steg, kommer jag att säga att det är extremt användbart för att minska falskt positiva MRZ-upptäckter. Utan det kan vi av misstag markera utsmyckade eller utformade regioner i passet som MRZ. Jag kommer att lämna detta som en övning till dig för att verifiera att beräkning av lutningen av blackhat-bilden kan förbättra MRZ-detekteringsnoggrannheten.
nästa steg är att försöka upptäcka de faktiska linjerna i MRZ:
# apply a closing operation using the rectangular kernel to close# gaps in between letters -- then apply Otsu's thresholding methodgradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
först tillämpar vi en stängningsoperation med vår rektangulära kärna. Denna stängningsoperation är avsedd att stänga luckor mellan MRZ-tecken. Vi tillämpar sedan tröskelvärde med Otsu: s metod för att automatiskt tröskla bilden:
som vi kan se från figuren ovan är var och en av MRZ-linjerna närvarande i vår tröskelkarta.
nästa steg är att stänga luckorna mellan de faktiska linjerna, vilket ger oss en stor rektangulär region som motsvarar MRZ:
# perform another closing operation, this time using the square# kernel to close gaps between lines of the MRZ, then perform a# series of erosions to break apart connected componentsthresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)thresh = cv2.erode(thresh, None, iterations=4)
här utför vi en annan stängningsoperation, den här gången med vår fyrkantiga kärna. Denna kärna används för att stänga luckor mellan de enskilda linjerna i MRZ, vilket ger oss en stor region som motsvarar MRZ. En serie erosioner utförs sedan för att bryta isär anslutna komponenter som kan ha förenats under stängningsoperationen. Dessa erosioner är också användbara för att ta bort små blobbar som är irrelevanta för MRZ.
för vissa passskanningar kan passets gräns ha blivit knuten till MRZ-regionen under stängningsoperationerna. För att avhjälpa detta ställer vi in 5% av bildens vänstra och högra gränser till noll (dvs svart):
# during thresholding, it's possible that border pixels were# included in the thresholding, so let's set 5% of the left and# right borders to zerop = int(image.shape * 0.05)thresh = 0thresh - p:] = 0
du kan se resultatet av vår gränsavlägsnande nedan.
jämfört med Figur 5 ovan kan du nu se att gränsen har tagits bort.
det sista steget är att hitta konturerna i vår tröskelbild och använda konturegenskaper för att identifiera MRZ:
# find contours in the thresholded image and sort them by their# sizecnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)cnts = sorted(cnts, key=cv2.contourArea, reverse=True)# loop over the contoursfor c in cnts:# compute the bounding box of the contour and use the contour to# compute the aspect ratio and coverage ratio of the bounding box# width to the width of the image(x, y, w, h) = cv2.boundingRect(c)ar = w / float(h)crWidth = w / float(gray.shape)# check to see if the aspect ratio and coverage width are within# acceptable criteriaif ar > 5 and crWidth > 0.75:# pad the bounding box since we applied erosions and now need# to re-grow itpX = int((x + w) * 0.03)pY = int((y + h) * 0.03)(x, y) = (x - pX, y - pY)(w, h) = (w + (pX * 2), h + (pY * 2))# extract the ROI from the image and draw a bounding box# surrounding the MRZroi = image.copy()cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)break# show the output imagescv2.imshow("Image", image)cv2.imshow("ROI", roi)cv2.waitKey(0)
på linje 56-58 beräknar vi konturerna (dvs konturerna) av vår tröskelbild. Vi tar sedan dessa konturer och sorterar dem baserat på deras storlek i fallande ordning på rad 59 (vilket innebär att de största konturerna är först i listan).
på linje 62 börjar vi looping över vår sorterade lista över konturer. För var och en av dessa konturer beräknar vi avgränsningsrutan (linje 66) och använder den för att beräkna två egenskaper: bildförhållandet och täckningsförhållandet. Bildförhållandet är helt enkelt bredden på avgränsningsboxen dividerad med höjden. Täckningsförhållandet är bredden på avgränsningsrutan dividerad med bredden på den faktiska bilden.
med hjälp av dessa två egenskaper kan vi göra en kontroll på rad 72 för att se om vi undersöker MRZ-regionen. MRZ är rektangulär, med en bredd som är mycket större än höjden. MRZ bör också spänna över minst 75% av inmatningsbilden.
förutsatt att dessa två fall håller, använder linjerna 75-84 (x, y)-koordinaterna för avgränsningsrutan för att extrahera MRZ och rita avgränsningsrutan på vår inmatningsbild.
slutligen visar raderna 87-89 våra resultat.
resultat
för att se vår MRZ-detektor i aktion, kör bara följande kommando:
$ python detect_mrz.py --images examples
nedan kan du se ett exempel på en framgångsrik MRZ-upptäckt, med MRZ skisserat i grönt:
här är ett annat exempel på att upptäcka den maskinläsbara zonen i en passbild med Python och OpenCV:
det spelar ingen roll om MRZ-regionen är högst upp eller längst ner på bilden. Genom att tillämpa morfologiska operationer, extrahera konturer och beräkna konture egenskaper kan vi extrahera MRZ utan problem.
detsamma gäller för följande bild:
Låt oss ge en annan bild ett försök:
hittills har vi bara sett typ 1 MRZ som innehåller tre rader. Men vår metod fungerar lika bra med MRZ-typ 3 som bara innehåller två rader:
här är ett annat exempel på att upptäcka en typ 3 MRZ:
vad händer nu? Jag rekommenderar PyImageSearch University.
30 + totalt klasser • 39h 44M video * Senast uppdaterad: 12/2021
★★★★★ 4.84 (128 betyg) * 3,000+ studenter inskrivna
jag tror starkt att om du hade rätt lärare kan du behärska datorseende och djupt lärande.
tycker du att det måste vara tidskrävande, överväldigande och komplicerat att lära sig datorseende och djupt lärande? Eller måste involvera komplex matematik och ekvationer? Eller kräver en examen i datavetenskap?
det är inte fallet.
allt du behöver för att behärska datorseende och djup inlärning är för någon att förklara saker för dig i enkla, intuitiva termer. Och det är precis vad jag gör. Mitt uppdrag är att förändra utbildning och hur komplexa ämnen inom artificiell intelligens lärs ut.
om du menar allvar med att lära sig datorseende, bör ditt nästa stopp vara PyImageSearch University, den mest omfattande datorseende, djupt lärande och OpenCV kurs online idag. Här lär du dig att framgångsrikt och tryggt tillämpa datorseende till ditt arbete, forskning och projekt. Följ med mig i Computer vision mastery.
inuti PyImageSearch University hittar du:
- &kontrollera; 30 + kurser om grundläggande datorsyn, djupinlärning och OpenCV-ämnen
- &kontrollera; 30+ certifikat för slutförande
- &kontrollera; 39h 44M on-demand video
- &kontrollera; helt nya kurser som släpps varje månad, så att du kan hålla jämna steg med toppmoderna tekniker
- &kontrollera; förkonfigurerade Jupyter-bärbara datorer i Google colab
- &kontrollera; kör alla kodexempel i din webbläsare-fungerar på Windows, MacOS och Linux (ingen dev-miljökonfiguration krävs!)
- &kontrollera; Tillgång till centraliserade kodrepor för alla 500 + tutorials på PyImageSearch
- &kontrollera; lätt ett klick nedladdningar för kod, datamängder, Pre-utbildade modeller, etc.
- &kontrollera; åtkomst på mobil, bärbar dator, skrivbord etc.
Klicka här för att gå med i PyImageSearch University
sammanfattning
i det här blogginlägget lärde vi oss att upptäcka maskinläsbara zoner (MRZ) i passskanningar med endast grundläggande bildbehandlingstekniker, nämligen:
- tröskelvärde.
- gradienter.
- morfologiska operationer (specifikt stängningar och erosioner).
- konture egenskaper.
dessa operationer, medan de var enkla, gjorde det möjligt för oss att upptäcka MRZ-regionerna i bilder utan att behöva förlita oss på mer avancerade funktioner extraktion och maskininlärningsmetoder som linjär SVM + HOG för objektdetektering.
kom ihåg att när du står inför ett utmanande datorsynproblem — alltid överväga problemet och dina antaganden! Som det här blogginlägget visar kan du bli förvånad över vilka grundläggande bildbehandlingsfunktioner som används i tandem kan åstadkomma.
återigen, ett stort tack till PyImageSearch Gurus medlem, Hans Boone, som försåg oss med dessa exempel passbilder! Tack Hans!