Locate QR code within an image

Share your advanced PureBasic knowledge/code with the community.
User avatar
Michael Vogel
Addict
Addict
Posts: 2666
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Locate QR code within an image

Post by Michael Vogel »

I need to extract QR codes from images (do be recalculated and viewed on a smartwatch then). The images are screenshots, so the sources are aligned and the code block uses real black and white colors. Anyhow the scaling often is a little bit fuzzy, so it takes a lot of time to extract the code using graphic tools like Paintshop Pro.

That's why I wrote the following tool today - which worked fine here with a lot of images - hopefully it will do the right job also for you:

Code: Select all

EnableExplicit

#QR="QR.png"

; Define

	#W=500
	#Max=100
	#Scan=20

	#NoiseFilter=$F8F8F8

	UsePNGImageDecoder()
	UseJPEGImageDecoder()

	Procedure.i iMin(val1.i,val2.i)

		EnableASM
		CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
			MOV eax, val1
			CMP eax, val2
			CMOVG eax, val2
		CompilerElse
			MOV rax, val1
			CMP rax, val2
			CMOVG rax, val2
		CompilerEndIf
		DisableASM
		ProcedureReturn

	EndProcedure
	Procedure.i iMax(val1.i,val2.i)

		EnableASM
		CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
			MOV eax, val1
			CMP eax, val2
			CMOVNG eax, val2
		CompilerElse
			MOV rax, val1
			CMP rax, val2
			CMOVNG rax, val2
		CompilerEndIf
		DisableASM
		ProcedureReturn
	EndProcedure

; EndDefine
Procedure CheckQR(log=#Null)

	Protected Dim l(#Scan)
	Protected Dim z(#Scan)
	Protected Dim b(#Scan,#Max)
	Protected Dim w(#Scan,#Max)
	Protected Dim c(#Max,#Max)
	Protected Dim d(#Max)

	Protected w,h
	Protected x,y,z
	Protected box
	Protected i,j,pi,pj
	Protected c,d,s
	Protected xmin,xmax
	Protected ymin,ymax
	Protected scan

	If FileSize(#QR) And LoadImage(0,#QR)

		w=ImageWidth(0)
		h=ImageHeight(0)

		
		; Path I - detecting black/white areas
		
		StartDrawing(ImageOutput(0))
		scan=iMin(#Scan,h/3)
		s=h/scan
		
		For i=1 To scan-1
			y=s*i
			c=0
			z=0
			For x=0 To w-1
				Select Point(x,y)&#NoiseFilter
				Case #Black
					If c<>1; c=0
						If z<#Max
							z+1
							b(i,z)=x
						EndIf
						;Box(x-2,y-5,3,10,#Magenta)
						c=1
					EndIf
				Case #NoiseFilter
					If c
						If c=1
							w(i,z)=x
						EndIf
						;Box(x,y-10,1,20,#Green)
						c=0
					EndIf
				Default
					If c=1
						w(i,z)=x
					EndIf
					;Debug Hex(Point(x,y))
					;Box(x,y-20,1,40,#Red)
					c=2
				EndSelect
			Next x

			If z
				l(i)=y
				z(i)=z
			EndIf
		Next i
		StopDrawing()
		
		
		; Path II - find nice start point
		
		box=w
		For i=2 To scan-2
			If z(i)>1 And z(i-1)>1 And z(i+1)>1
				For j=2 To z(i)
					d=b(i,j)-b(i,j-1)
					If d<box
						box=d
						pi=i
						pj=j
					EndIf
				Next j
			EndIf
		Next i

		If pi*pj=#Null Or box<3 Or w(pi,pj)-b(pi,pj)<box/2
			Debug "No start point for QR pattern detected."
			ProcedureReturn #False
		EndIf
		
		
		; Path III - find square center and pattern

		StartDrawing(ImageOutput(0))
		x=b(pi,pj)+box/7
		y=l(pi)
		While y And Point(x+i,y)&#NoiseFilter=#Black
			y-1
		Wend
		x=b(pi,pj)+box/4
		y+box/4

		ymin=#Max
		ymax=0
		xmin=#Max
		xmax=0

		c=#Max/2
		d=box/2
		For i=0 To #Max
			pi=x+(i-c)*box/2
			For j=0 To #Max
				pj=y+(j-c)*box/2
				If pi>=0 And pj>=0 And pi<w And pj<h
					Select Point(pi,pj)&#NoiseFilter
					Case #Black
						c(i,j)=1
						xmin=iMin(xmin,i)
						ymin=iMin(ymin,j)
						xmax=iMax(xmax,i)
						ymax=iMax(ymax,j)
						Box(pi-d/2,pj-d/2,d,d,#Green)
					Case #NoiseFilter
						c(i,j)=0
					Default
						c(i,j)=2
					EndSelect
				Else
					c(i,j)=2
				EndIf
			Next j
		Next i

		; 		Repeat
		; 			z=0
		; 			For i=0 To 3
		; 				dx=i>>1-i&1
		; 				dy=i!3>>1-i&1
		; 				Debug Str(dx)+" "+Str(dy)
		; 			Next i
		; 		Until z=0

		StopDrawing()


		; Path IV - reduce surrounding border
		
		Repeat
			z=0
			j=ymin
			While j<ymax
				c=0
				d=0
				For i=xmin To xmax
					c+Bool(c(i,j)=1)
					d+Bool(c(i,j)=2)
				Next i
				If c=0 Or d
					If ymax-j<j-ymin
						ymax=j-1
						z=1
					Else
						ymin=j+1
						z=2
					EndIf
				EndIf
				j+1
			Wend

			i=xmin
			While i<xmax
				c=0
				d=0
				For j=ymin To ymax
					c+Bool(c(i,j)=1)
					d+Bool(c(i,j)=2)
				Next j
				If c=0 Or d
					If xmax-i<i-xmin
						xmax=i-1
						z=3
					Else
						xmin=i+1
						z=4
					EndIf
				EndIf
				i+1
			Wend
		Until z=0

		If ymax-ymin<>xmax-xmin
			Debug "QR not squared."
		EndIf


		; Path V - draw image
		
		c=xmax-xmin+1
		CreateImage(0,c,ymax-ymin+1)

		StartDrawing(ImageOutput(0))
		For j=ymin To ymax
			Protected t.s=""
			For i=xmin To xmax
				Plot(i-xmin,j-ymin,#White*(c(i,j)!1))
				t+Mid(" #.",c(i,j)+1,1)
			Next i
			If log 
				Debug t
			EndIf
		Next j
		StopDrawing()

		ProcedureReturn c

	Else
		Debug "Could not load image "+#QR+"."
		ProcedureReturn #False

	EndIf

EndProcedure


#Zoom=16
Define c=CheckQR()


If c
	c*#Zoom
	OpenWindow(0,0,0,c,c,"QR",#PB_Window_ScreenCentered)
	CanvasGadget(0,0,0,c,c)

	StartDrawing(CanvasOutput(0))
	ResizeImage(0,c,c,#PB_Image_Raw)
	DrawImage(ImageID(0),0,0)
	StopDrawing()

	Repeat
		Select WaitWindowEvent()
		Case #WM_CHAR, #PB_Event_CloseWindow
			End
		EndSelect
	ForEver
EndIf
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Locate QR code within an image

Post by Dude »

Thanks Michael. I haven't tested it but I trust it works, because I know I'll use it one day. :)
dige
Addict
Addict
Posts: 1247
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Locate QR code within an image

Post by dige »

Hi Michael, tried it with a Screenshot of web.whatsapp.com .. but it dont find a pattern.
Ciao Dige
"Daddy, I'll run faster, then it is not so far..."
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Locate QR code within an image

Post by Mijikai »

Did some speed tests for fun (PB 5.62 x64 - Windows 7).
The fasm (inline) function would be even faster without the overhead of PB (fasm -> lib/obj).
However a Macro (omitting the function call) outperforms both.
User avatar
Michael Vogel
Addict
Addict
Posts: 2666
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Locate QR code within an image

Post by Michael Vogel »

The code needs (a lot of) polishing for handling photos, I needed to deal with screenshots from web pages which had a lot of white space...
...actually I am programming in monkey C to use the QR codes on a Garmin watch - will take a while until I am able to do some refreshs on that code :cry:
Post Reply