I don't know if this has been discussed, but I tried working with this in "native" PB environment.
Namely, I wanted to have multiple display sources in one window, something like this.
https://youtu.be/Sp7VcjPlgGA
Here is my code.
Code: Select all
IncludeFile "includes/cv_functions.pbi"
EnableExplicit
Enumeration #PB_Event_FirstCustomValue
#Event_ThreadMessage
#EventType_UpdateFrame
EndEnumeration
Global *capture, *param.USER_INFO
Global.i ThreadFlag = 1
Global.cvFont font1, font2, font3
Global Dim Thr.i(4)
Threaded.i i, j, Parameter
Threaded.b adjust1, adjust2, adjust3
Threaded *image.IplImage
Define.i nCreate, CaptureThr, Event, FrameSyncMutex
Procedure ErrorHandler()
Protected ErrorMessage$
ErrorMessage$ = "A program error was detected:" + Chr(13)
ErrorMessage$ + Chr(13)
ErrorMessage$ + "Error Message: " + ErrorMessage() + Chr(13)
ErrorMessage$ + "Error Code: " + Str(ErrorCode()) + Chr(13)
ErrorMessage$ + "Code Address: " + Str(ErrorAddress()) + Chr(13)
If ErrorCode() = #PB_OnError_InvalidMemory
ErrorMessage$ + "Target Address: " + Str(ErrorTargetAddress()) + Chr(13)
EndIf
If ErrorLine() = -1
ErrorMessage$ + "Sourcecode line: Enable OnError lines support to get code line information." + Chr(13)
Else
ErrorMessage$ + "Sourcecode line: " + Str(ErrorLine()) + Chr(13)
ErrorMessage$ + "Sourcecode file: " + ErrorFile() + Chr(13)
EndIf
ErrorMessage$ + Chr(13)
ErrorMessage$ + "Register content:" + Chr(13)
CompilerSelect #PB_Compiler_Processor
CompilerCase #PB_Processor_x86
ErrorMessage$ + "EAX = " + Str(ErrorRegister(#PB_OnError_EAX)) + Chr(13)
ErrorMessage$ + "EBX = " + Str(ErrorRegister(#PB_OnError_EBX)) + Chr(13)
ErrorMessage$ + "ECX = " + Str(ErrorRegister(#PB_OnError_ECX)) + Chr(13)
ErrorMessage$ + "EDX = " + Str(ErrorRegister(#PB_OnError_EDX)) + Chr(13)
ErrorMessage$ + "EBP = " + Str(ErrorRegister(#PB_OnError_EBP)) + Chr(13)
ErrorMessage$ + "ESI = " + Str(ErrorRegister(#PB_OnError_ESI)) + Chr(13)
ErrorMessage$ + "EDI = " + Str(ErrorRegister(#PB_OnError_EDI)) + Chr(13)
ErrorMessage$ + "ESP = " + Str(ErrorRegister(#PB_OnError_ESP)) + Chr(13)
CompilerCase #PB_Processor_x64
ErrorMessage$ + "RAX = " + Str(ErrorRegister(#PB_OnError_RAX)) + Chr(13)
ErrorMessage$ + "RBX = " + Str(ErrorRegister(#PB_OnError_RBX)) + Chr(13)
ErrorMessage$ + "RCX = " + Str(ErrorRegister(#PB_OnError_RCX)) + Chr(13)
ErrorMessage$ + "RDX = " + Str(ErrorRegister(#PB_OnError_RDX)) + Chr(13)
ErrorMessage$ + "RBP = " + Str(ErrorRegister(#PB_OnError_RBP)) + Chr(13)
ErrorMessage$ + "RSI = " + Str(ErrorRegister(#PB_OnError_RSI)) + Chr(13)
ErrorMessage$ + "RDI = " + Str(ErrorRegister(#PB_OnError_RDI)) + Chr(13)
ErrorMessage$ + "RSP = " + Str(ErrorRegister(#PB_OnError_RSP)) + Chr(13)
ErrorMessage$ + "Display of registers R8-R15 skipped." + Chr(13)
CompilerCase #PB_Processor_PowerPC
ErrorMessage$ + "r0 = " + Str(ErrorRegister(#PB_OnError_r0)) + Chr(13)
ErrorMessage$ + "r1 = " + Str(ErrorRegister(#PB_OnError_r1)) + Chr(13)
ErrorMessage$ + "r2 = " + Str(ErrorRegister(#PB_OnError_r2)) + Chr(13)
ErrorMessage$ + "r3 = " + Str(ErrorRegister(#PB_OnError_r3)) + Chr(13)
ErrorMessage$ + "r4 = " + Str(ErrorRegister(#PB_OnError_r4)) + Chr(13)
ErrorMessage$ + "r5 = " + Str(ErrorRegister(#PB_OnError_r5)) + Chr(13)
ErrorMessage$ + "r6 = " + Str(ErrorRegister(#PB_OnError_r6)) + Chr(13)
ErrorMessage$ + "r7 = " + Str(ErrorRegister(#PB_OnError_r7)) + Chr(13)
ErrorMessage$ + "Display of registers r8-R31 skipped." + Chr(13)
CompilerEndSelect
ThreadFlag = 0
Delay(250)
For i = 0 To 3
If IsThread(Thr(i))
KillThread(Thr(i))
EndIf
Next
MessageRequester("OnError example", ErrorMessage$)
End
EndProcedure
OnErrorCall(@ErrorHandler())
Repeat
nCreate + 1
*capture = cvCreateCameraCapture(0)
Until nCreate = 5 Or *capture
Procedure RefreshFrame(*p)
Shared FrameSyncMutex
Parameter = *p
While ThreadFlag
LockMutex(FrameSyncMutex)
*image = cvQueryFrame(*capture)
UnlockMutex(FrameSyncMutex)
If *image
cvPutText(*image, "Part " + Str(Parameter), 0, 50, @font1, 0, 0, 255, 0)
cvPutText(*image, FormatDate("%yyyy-%mm-%dd",Date()), 0, 440, @font2, 0, 255, 0, 0)
cvPutText(*image, FormatDate("%hh-%ii-%ss", Date()), 370, 440, @font3, 255, 0, 0, 0)
cvFlip(*image, #Null, 0)
StartDrawing(ImageOutput(Parameter))
DrawImage(ImageID(Parameter),0,0)
CopyMemory(*image\imageData, DrawingBuffer(), (*image\width) * (*image\height) * 3)
StopDrawing()
PostEvent(#Event_ThreadMessage, 0, Parameter, #EventType_UpdateFrame, Parameter)
EndIf
Wend
ProcedureReturn
EndProcedure
Procedure OnThreadMessage()
Select EventType()
Case #EventType_UpdateFrame
ResizeImage(EventData(),320,240)
SetGadgetState(EventGadget(), ImageID(EventData()))
ResizeImage(EventData(),640,480)
EndSelect
ProcedureReturn
EndProcedure
If *capture
If OpenWindow(0, 0, 0, 640, 520, "PB Native@OpenCV", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
BindEvent(#Event_ThreadMessage, @OnThreadMessage())
FrameSyncMutex = CreateMutex()
ButtonGadget(100, 10, 490, 200, 20, "Exit")
CreateImage(0, 640, 480)
CreateImage(1, 640, 480)
CreateImage(2, 640, 480)
CreateImage(3, 640, 480)
ImageGadget(0,0,0,320,240, ImageID(0))
ImageGadget(1,320,0,320,240, ImageID(1))
ImageGadget(2,0,240,320,240, ImageID(2))
ImageGadget(3,320,240,320,240, ImageID(3))
font1.CvFont
font2.CvFont
font3.CvFont
cvInitFont(@font1, #CV_FONT_HERSHEY_SCRIPT_COMPLEX, 1.5, 1.5, #Null, 1, #CV_AA)
cvInitFont(@font2, #CV_FONT_HERSHEY_SIMPLEX, 1.5, 1.5, #Null, 1, #CV_AA)
cvInitFont(@font3, #CV_FONT_HERSHEY_TRIPLEX | #CV_FONT_ITALIC, 1.5, 1.5, #Null, 1, #CV_AA)
*param.USER_INFO = AllocateMemory(SizeOf(USER_INFO))
*param\uValue = WindowID(0)
For i = 0 To 3
Thr(i) = CreateThread(@RefreshFrame(), i)
Next
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
Select EventGadget()
Case 100
ThreadFlag = 0
FreeMemory(*param)
cvReleaseCapture(@*capture)
End 0
EndSelect
EndSelect
Until Event = #PB_Event_CloseWindow
EndIf
EndIf
It would be sane that there are separated capture sequences (for example, multiple cameras), however, what would be a proper way to sync threads if one capture is made? How to achieve (mutex, semaphores?) than that same capture would be copied to "display" threads for manipulation, and only after "post-processing" is done in each thread, new capture frame is taken?
In addition to that, is there a better way to handle resize of picture, since capture is 640x480 and I'd like to display 320x240 images (or any other resolution)? Image size must be 640x480 since I use CopyMemory and if resolution is changed, there's a memory violation.
Furthermore, is OpenCV fully thread safe in general? For some reason, I get memory access errors on DrawImage(ImageID(Parameter),0,0) or in CopyMemory below that line, so I don't know if that's related to OpenCV or my lousy coding skills.
When that happens, process remains active (I guess threads don't die), so I have put the following code in ErrorHandler procedure.
Code: Select all
ThreadFlag = 0
Delay(250)
For i = 0 To 3
If IsThread(Thr(i))
KillThread(Thr(i))
EndIf
Next
Should this prevent that kind of behavior? I am asking "should it" since I've fiddled with code in the meanwhile and I can't reproduce that particular memory error, do'h.
Edit: well, I've let it run through the night and no crashes happened. In addition to that, it also exits gracefully without leaving any threads. Would still like to hear some comments about above mentioned issues in general! Thanks in advance!
Thanks to all in advance,
Bruno