PureBasic Interface to OpenCV

Developed or developing a new product in PureBasic? Tell the world about it.
AAT
Enthusiast
Enthusiast
Posts: 256
Joined: Sun Jun 15, 2008 3:13 am
Location: Russia

Re: OpenCV (v2)

Post by AAT »

Hi
Example of the practical use of the function cvHoughCircles() for detecting the trimmer's center on the PCB, you can download from http://rghost.ru/51132977 (file will be available 30 days)
Example based on cv_hough_2.pb written by JHPJHP (look at the first post of the topic) with minor changes. Test images are included.

Enjoy!
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: OpenCV (v2)

Post by JHPJHP »

That's great AAT, I'm happy to see that my efforts are finding a practical use - thank you for your contributions & feedback!

I've done a lot more work since my last post, adding multiple examples... I'll update the package sometime after Christmas or in the New Year. :wink:

Seasons Greeting,
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: OpenCV (v2)

Post by JHPJHP »

The latest updated is one of the more significant updates to date.

There has always been a few caveats to using OpenCV with PureBasic, the one that seemed to cause the most consternation was: Returning a Structure from a Function; this has been resolved.

My first thought was to ignore this limitation by working around it: cv_contours_5.pb.

My next thought was to utilize the legacy functions: le_contours_6a.pb, le_contours_6b.pb.
- most of the functions that now returned Structures were designed with the Structures included as parameters
-- examples with le_* (use legacy functions)

While writing the new examples, and with each step - I found the "door" to calling the OpenCV functions that returned Structutes.
- you may find it interesting to compare the legacy (le_*) examples to there counterparts (cv_*): different methods returning the same results

Something else you may find interesting is the SURF (Speeded Up Robust Features) examples... I'm in the process of writing an image comparison (SIFT - Scale Invariant Feature Transform) example - and will post it in the next few days.

Some of the Changes
- added the complete list of OpenCV binaries (needed when legacy functions were added)
- removed x64 binaries (64-bit not currently working / reduce package size)
- added / updated Constants, Structures, Functions
- updated all examples with minor improvements
- added / renamed examples
-- cv_contours_6a.pb: calculates the contour areas, showing degree of tilt with bounding rectangle
-- cv_contours_6b.pb: calculates the contour areas, showing degree of tilt with bounding ellipse
-- cv_sequence_2a.pb: creates a sequence of points, bounding them in a rectangle of the minimal area
-- cv_sequence_2b.pb: creates a sequence of points, bounding them in an ellipse of the minimal area
-- le_cam_SURF.pb: detects keypoints and computes SURF (Speeded Up Robust Features) descriptors for them on the webcam interface
-- le_contours_6a.pb: legacy version of cv_contours_6a.pb
-- le_contours_6b.pb: legacy version of cv_contours_6b.pb
-- le_sequence_2a.pb: legacy version of cv_sequence_2a.pb
-- le_sequence_2b.pb: legacy version of cv_sequence_2b.pb
-- le_SURF.pb: detects keypoints and computes SURF (Speeded Up Robust Features) descriptors for them
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: OpenCV (v2)

Post by Little John »

Great!
Thanks again, and keep up the good work!
AAT
Enthusiast
Enthusiast
Posts: 256
Joined: Sun Jun 15, 2008 3:13 am
Location: Russia

Re: OpenCV (v2)

Post by AAT »

Hi, JHPJHP
My congratulations to you! This is a breakthrough.
I have tested new functions, they are working fine.
Thanks!
AAT
Enthusiast
Enthusiast
Posts: 256
Joined: Sun Jun 15, 2008 3:13 am
Location: Russia

Re: OpenCV (v2)

Post by AAT »

Small addition: function cvMaxRect()
It computes a new rectangle from two input rectangles. The new rectangle is the smallest rectangle that will bound both inputs.

Code: Select all

IncludeFile "includes/cv_functions.pbi"

Global ExitCV.b, lpPrevWndFunc

#CV_WINDOW_NAME = "PureBasic Interface to OpenCV"
#CV_DESCRIPTION = "Computes a new rectangle from two input rectangles. The new rectangle is the smallest rectangle that will bound both inputs."

ImportC "opencv_imgproc247.lib"
  cvMaxRect(*maxrect, *rect1, *rect2)
EndImport

Procedure WindowCallback(hWnd, Msg, wParam, lParam)
  Select Msg
    Case #WM_COMMAND
      Select wParam
        Case 1
          keybd_event_(#VK_ESCAPE, 0, 0, 0)
      EndSelect
    Case #WM_DESTROY
      ExitCV = #True
  EndSelect
  ProcedureReturn CallWindowProc_(lpPrevWndFunc, hWnd, Msg, wParam, lParam)
EndProcedure

ProcedureC CvMouseCallback(event, x, y, flags, *param.USER_INFO)
  Select event
    Case #CV_EVENT_RBUTTONDOWN
      DisplayPopupMenu(0, *param\uValue)
  EndSelect
EndProcedure

Rect1.CvRect
Rect2.CvRect
MaxRect.CvRect
font.CvFont

cvNamedWindow(#CV_WINDOW_NAME, #CV_WINDOW_NORMAL)
window_handle = cvGetWindowHandle(#CV_WINDOW_NAME)
*window_name = cvGetWindowName(window_handle)
lpPrevWndFunc = SetWindowLongPtr_(window_handle, #GWL_WNDPROC, @WindowCallback())

If CreatePopupImageMenu(0, #PB_Menu_ModernLook)
  MenuItem(1, "Exit")
EndIf
hwnd = GetParent_(window_handle)
ExtractIconEx_("shell32.dll", 1, #Null, @phiconSmall, 1)
SendMessage_(hwnd, #WM_SETICON, 0, phiconSmall)
wStyle = GetWindowLongPtr_(hwnd, #GWL_STYLE)
SetWindowLongPtr_(hwnd, #GWL_STYLE, wStyle & ~(#WS_MAXIMIZEBOX | #WS_MINIMIZEBOX | #WS_SIZEBOX))

*image.IplImage = cvCreateImage(600, 400, #IPL_DEPTH_8U, 3)
cvResizeWindow(#CV_WINDOW_NAME, *image\width, *image\height)
cvMoveWindow(#CV_WINDOW_NAME, 20, 20)
ToolTip(window_handle, #CV_DESCRIPTION)

rect1\x = 40
rect1\y = 100
rect1\height = 150     
rect1\width = 50
cvRectangleR(*image, rect1\x, rect1\y, rect1\width, rect1\height, 50, 50, 255, 0, 1, CV_AA, 0);
rect2\x = 110
rect2\y = 50
rect2\height = 100
rect2\width = 60
cvRectangleR(*image, rect2\x, rect2\y, rect2\width, rect2\height, 50, 255, 50, 0, 1, CV_AA, 0);
cvMaxRect(@maxrect, @rect1, @rect2)  
cvRectangleR(*image, maxrect\x-2, maxrect\y-2, maxrect\width+4, maxrect\height+4, 0, 255, 255, 0, 1, CV_AA, 0);

rect1\x = 210
rect1\y = 250
rect1\height = 60     
rect1\width = 150
cvRectangleR(*image, rect1\x, rect1\y, rect1\width, rect1\height, 50, 50, 255, 0, 1, CV_AA, 0);
rect2\x = 250
rect2\y = 125
rect2\height = 200
rect2\width = 90
cvRectangleR(*image, rect2\x, rect2\y, rect2\width, rect2\height, 50, 255, 50, 0, 1, CV_AA, 0);
cvMaxRect(@maxrect, @rect1, @rect2)  
cvRectangleR(*image, maxrect\x-2, maxrect\y-2, maxrect\width+4, maxrect\height+4, 0, 255, 255, 0, 1, CV_AA, 0);

cvInitFont(@font, #CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, #Null, 1, #CV_AA)
cvPutText(*image, "Rectangle1 - red", 400, 60, @font, 50, 50, 255, 0)
cvPutText(*image, "Rectangle2 - green", 400, 100, @font, 50, 255, 50, 0)
cvPutText(*image, "MaxRectangle - yellow", 400, 140, @font, 50, 255, 255, 0) 
 
*param.USER_INFO = AllocateMemory(SizeOf(USER_INFO))
*param\uValue = window_handle
cvSetMouseCallback(*window_name, @cvMouseCallback(), *param)

Repeat
  If *image
    cvShowImage(#CV_WINDOW_NAME, *image)
    keyPressed = cvWaitKey(0)
  EndIf
Until keyPressed = 27 Or ExitCV
FreeMemory(*param)
cvReleaseImage(@*image)
cvDestroyWindow(#CV_WINDOW_NAME)
Good luck!
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: OpenCV (v2)

Post by JHPJHP »

Hi Little John,

Thank you for the acknowledgement - it's very much appreciated.

--------------------------------------

Hi AAT,

As always your support and contributions are great additions to the "PureBasic Interface to OpenCV".

--------------------------------------

Note:
- Something you may find useful... Example: le_SURF.pb - the context menu item [ Save ] not only saves the modified image, but also keypoints.xml & descriptors.xml.

--------------------------------------

Updates:
- added Structures, Functions
- added 1 example (contributed by AAT)
-- cv_maxrect.pb: finds bounding rectangle for two given rectangles
Last edited by JHPJHP on Thu Jan 02, 2014 6:09 pm, edited 1 time in total.
Philippe-felixer76-3
User
User
Posts: 45
Joined: Mon Dec 30, 2013 10:12 pm

Re: PureBasic Interface to OpenCV

Post by Philippe-felixer76-3 »

Is it possible to use a PB movie as input or to use/convert a PB image file?
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: PureBasic Interface to OpenCV

Post by JHPJHP »

Please provide additional details - I'm not sure I fully understand your requirements, but maybe the following will help?
cvCreateFileCapture

Initializes capturing video from file
CvCapture* cvCreateFileCapture( const char* filename );

filenameName of the video file.
The function cvCreateFileCapture allocates and initialized the CvCapture structure for reading the video stream from the specified file.

After the allocated structure is not used any more it should be released by cvReleaseCapture function.
The cv_cam* examples use cvCreateCameraCapture, by substituting this function with cvCreateFileCapture, and some modification to the existing code, you should be able to manipulate each "movie" frame to fit your needs.

PureBasic's built-in functions can be used to convert most images to an OpenCV supported format, otherwise you can combine the two technologies like I did in the pb_* examples.
Philippe-felixer76-3
User
User
Posts: 45
Joined: Mon Dec 30, 2013 10:12 pm

Re: PureBasic Interface to OpenCV

Post by Philippe-felixer76-3 »

JHPJHP wrote:Please provide additional details - I'm not sure I understand your requirements, but maybe the following will help?
cvCreateFileCapture

Initializes capturing video from file
CvCapture* cvCreateFileCapture( const char* filename );

filenameName of the video file.
The function cvCreateFileCapture allocates and initialized the CvCapture structure for reading the video stream from the specified file.

After the allocated structure is not used any more it should be released by cvReleaseCapture function.
The cv_cam* examples use cvCreateCameraCapture, by substituting this function with cvCreateFileCapture, and some modification to the existing code, you should be able to manipulate each frame to fit your needs.
I'm trying to detect faces in videos, already have it running on android. I thought i needed to use the PB Movie library to decode frame per frame and pass it to OpenCV as IplImage.

I was trying to convert a PB image to IplImage with cvSetData and DrawingOutput(), no luck so far.

But now im going to try it with cvCreateFileCapture( const char* filename ), tnx.
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: PureBasic Interface to OpenCV

Post by JHPJHP »

Hi Philippe-felixer76-3,

For a quick test I modified the example: cv_cam_face.pb; only needing to change the following bit of code...

From:

Code: Select all

Repeat
  nCreate + 1
  *capture = cvCreateCameraCapture(0)
Until nCreate = 5 Or *capture
To:

Code: Select all

Repeat
  nCreate + 1
  *capture = cvCreateFileCapture("C:\Users\Administrator\Videos\test.avi")
Until nCreate = 5 Or *capture
... And it worked OOTB.

NB*: To optimize results various setting needed to be modified.

--------------------------------------------------------------------------------

Update:
- the following takes a "PB Image" and converts it for use in OpenCV

Code: Select all

UsePNGImageDecoder()
ReadFile(0, "C:\Users\Administrator\Pictures\hand.png")
length = Lof(0)
*buffer = AllocateMemory(length)
ReadData(0, *buffer, length)
CloseFile(0)
CatchImage(0, *buffer)

If IsImage(0)
  *mat.CvMat = cvCreateMatHeader(ImageWidth(0), ImageHeight(0), CV_MAKETYPE(#CV_32F, 1))
  cvSetData(*mat, *buffer, #CV_AUTOSTEP)
  *decode.IplImage = cvDecodeImage(*mat, #CV_LOAD_IMAGE_COLOR)
  FreeImage(0)

  Repeat
    If *decode
      cvShowImage(#CV_WINDOW_NAME, *decode)
      keyPressed = cvWaitKey(0)
    EndIf
  Until keyPressed = 27 Or ExitCV
  cvReleaseImage(@*decode)
  cvReleaseMat(@*mat)
EndIf
FreeMemory(*buffer)
Philippe-felixer76-3
User
User
Posts: 45
Joined: Mon Dec 30, 2013 10:12 pm

Re: PureBasic Interface to OpenCV

Post by Philippe-felixer76-3 »

JHPJHP wrote:Hi Philippe-felixer76-3,

Code: Select all

Repeat
  nCreate + 1
  *capture = cvCreateFileCapture("C:\Users\Administrator\Videos\test.avi")
Until nCreate = 5 Or *capture
... And it worked OOTB.
Tnx, but already figured that out yesterday, first thing i did after your post :)
Update:
- the following takes a "PB Image" and converts it for use in OpenCV

Code: Select all

UsePNGImageDecoder()
ReadFile(0, "C:\Users\Administrator\Pictures\hand.png")
length = Lof(0)
*buffer = AllocateMemory(length)
ReadData(0, *buffer, length)
CloseFile(0)
CatchImage(0, *buffer)

If IsImage(0)
  *mat.CvMat = cvCreateMatHeader(ImageWidth(0), ImageHeight(0), CV_MAKETYPE(#CV_32F, 1))
  cvSetData(*mat, *buffer, #CV_AUTOSTEP)
  *decode.IplImage = cvDecodeImage(*mat, #CV_LOAD_IMAGE_COLOR)
  FreeImage(0)

  Repeat
    If *decode
      cvShowImage(#CV_WINDOW_NAME, *decode)
      keyPressed = cvWaitKey(0)
    EndIf
  Until keyPressed = 27 Or ExitCV
  cvReleaseImage(@*decode)
  cvReleaseMat(@*mat)
EndIf
FreeMemory(*buffer)
Interesting, but not very efficient for frame per frame processing..

The following seems to work:

Code: Select all

 
UseJPEGImageDecoder()
...
Procedure PBImage2CVImage(pbimg.l)
  If IsImage(pbimg)
    *cvimg.IplImage = cvCreateImage(ImageWidth(pbimg), ImageHeight(pbimg), #IPL_DEPTH_8U, 3)
    StartDrawing( ImageOutput( pbimg) )
       CopyMemory(DrawingBuffer(), *cvimg\imageData, ImageWidth(pbimg) * ImageHeight(pbimg) * 3)
    StopDrawing()
    cvFlip(*cvimg, #Null, 0) 
    FreeImage(pbimg)
    ProcedureReturn *cvimg   
  Else
    ProcedureReturn 0
  EndIf
EndProcedure
...
*image.IplImage = PBImage2CVImage(LoadImage( #PB_Any, "C:\Program Files (x86)\PureBasic 5.21\Examples\OpenCV\binaries\images\faces.jpg" ))
And some code for PB movie frames:

Code: Select all

Procedure CaptureFrame(MovieNumber, *cvimg.IplImage)
  Protected *Movie.PB_StructureMovie, *Video.IBasicVideo, *Window.IVideoWindow
  Protected *ImageData.BITMAPINFOHEADER, DataSize, Parent, Result
  *Movie  = IsMovie(MovieNumber)  
  *Window = *Movie\Window
  *Video  = *Movie\Video
  If *Video\GetCurrentImage(@DataSize, 0) = #S_OK
    *ImageData = AllocateMemory(DataSize)
    If *ImageData
      If *Video\GetCurrentImage(@DataSize, *ImageData) = #S_OK
        If *cvimg.IplImage =0
          *cvimg.IplImage = cvCreateImage(*ImageData\biWidth, *ImageData\biHeight, #IPL_DEPTH_8U, (*ImageData\biBitCount / 8))
        EndIf
        CopyMemory(*ImageData, *cvimg\imageData, *ImageData\biWidth * *ImageData\biHeight * (*ImageData\biBitCount / 8))
      EndIf
      FreeMemory(*ImageData)
      cvFlip(*cvimg, #Null, 0) 
      ProcedureReturn *cvimg
    EndIf
  EndIf   
  ProcedureReturn 0
EndProcedure
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: PureBasic Interface to OpenCV

Post by JHPJHP »

Hi Philippe-felixer76-3,

Nice work...

--------------------------------------
Tnx, but already figured that out yesterday, first thing i did after your post
Sorry to be so obvious, but in my defense it was your first day. :lol:
Interesting, but not very efficient for frame per frame processing.
I haven't done any speed comparisons, but are you sure about the "efficiency" using my method?

Code: Select all

UseJPEGImageDecoder()
UseJPEGImageEncoder()
LoadImage(0, "C:\Users\Administrator\Pictures\faces.jpg")
width = ImageWidth(0)
height = ImageHeight(0)
*buffer = EncodeImage(0, #PB_ImagePlugin_JPEG)
FreeImage(0)
*mat.CvMat = cvCreateMatHeader(width, height, CV_MAKETYPE(#CV_32F, 1))
cvSetData(*mat, *buffer, #CV_AUTOSTEP)
*decode.IplImage = cvDecodeImage(*mat, #CV_LOAD_IMAGE_COLOR)
Let me know the speed difference if you end up comparing the two - this should make it easier:

Code: Select all

UseJPEGImageDecoder()
UseJPEGImageEncoder()
...
Procedure PBImage2CVImage(pbimg)
  If IsImage(pbimg)
    *mat.CvMat = cvCreateMatHeader(ImageWidth(pbimg), ImageHeight(pbimg), CV_MAKETYPE(#CV_32F, 1))
    *buffer = EncodeImage(pbimg, #PB_ImagePlugin_JPEG)
    cvSetData(*mat, *buffer, #CV_AUTOSTEP)
    *cvimg.IplImage = cvDecodeImage(*mat, #CV_LOAD_IMAGE_COLOR)
    FreeImage(pbimg)
    ProcedureReturn *cvimg
  Else
    ProcedureReturn 0
  EndIf
EndProcedure
...
*image.IplImage = PBImage2CVImage(LoadImage(#PB_Any, "images/faces.jpg"))
Happy New Year!!!
Philippe-felixer76-3
User
User
Posts: 45
Joined: Mon Dec 30, 2013 10:12 pm

Re: PureBasic Interface to OpenCV

Post by Philippe-felixer76-3 »

JHPJHP wrote:Hi Philippe-felixer76-3,

Let me know the speed difference if you end up comparing the two - this should make it easier:

Happy New Year!!!
My goal was to use the PB Movie functions to process frames, not loading single pictures from disk, but i did a quick speed comparing and my code loaded a jpg as cv picture in 93ms and yours in 281ms.
Flipping the picture was not needed using your code and made it about 4ms faster.
But if i use *cvimg\imageData = DrawingBuffer() i.s.o. CopyMemory my code is about 10ms faster.

But the speed of the purebasic movie function compared to the opencv video function is about 100000% slower :)
So i will endup using OpenCV (ffmpeg) code for now.
Anyway tnx for the work you put into this handy interface, BTW i have a 64bit OS, would that speed things up if it worked?

Happy New Year!
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: PureBasic Interface to OpenCV

Post by JHPJHP »

Good to know - efficient both ways. :wink:

Your welcome, I'm glad it's finding a use.

ffmpeg... I may add the full list of functions to the package: opencv_ffmpeg247.dll
- no lib, requires Prototyping

-----------------

I think it would make a big difference when dealing with large video files.

I've done some testing with the 64-bit DLLs working with PureBasic; some functions need to be declared differently from the method I used in cv_functions.pbi... or more accurately they need to be declared the same way they're referenced in the manual; there are also some Structure discrepancies (alignment issues) between versions...

With the 32-bit DLL functions I needed to split out the Structure parameters, but for the same 64-bit DLL functions the Structure parameters needed to remain intact in order to work; a lot of trial and error.

Something like: [ CvRect ] vs [ x, y, width, height ]

And because there was/is so much to do creating an interface between PureBasic and OpenCV - I decided to remove the 64-bit part of the package, at least for the interim - until I'm comfortable with the progress.
Locked