diff --git a/doc/tutorials/definitions/tocDefinitions.rst b/doc/tutorials/definitions/tocDefinitions.rst index c036b0b70..52d5e5b27 100644 --- a/doc/tutorials/definitions/tocDefinitions.rst +++ b/doc/tutorials/definitions/tocDefinitions.rst @@ -11,5 +11,6 @@ .. |Author_EricCh| unicode:: Eric U+0020 Christiansen .. |Author_AndreyP| unicode:: Andrey U+0020 Pavlenko .. |Author_AlexS| unicode:: Alexander U+0020 Smorkalov +.. |Author_MimmoC| unicode:: Mimmo U+0020 Cosenza .. |Author_BarisD| unicode:: Bar U+0131 U+015F U+0020 Evrim U+0020 Demir U+00F6 z .. |Author_DomenicoB| unicode:: Domenico U+0020 Daniele U+0020 Bloisi diff --git a/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst b/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst new file mode 100644 index 000000000..248abdf6d --- /dev/null +++ b/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst @@ -0,0 +1,728 @@ +.. _clojure_dev_intro: + +Introduction to OpenCV Development with Clojure +*********************************************** + +As of OpenCV 2.4.4, OpenCV supports desktop Java development using +nearly the same interface as for Android development. + +`Clojure `_ is a contemporary LISP dialect hosted +by the Java Virtual Machine and it offers a complete interoperability +with the underlying JVM. This means that we should even be able to use +the Clojure REPL (Read Eval Print Loop) as and interactive programmable +interface to the underlying OpenCV engine. + +What we'll do in this tutorial +============================== + +This tutorial will help you in setting up a basic Clojure environment +for interactively learning OpenCV within the fully programmable +CLojure REPL. + +Tutorial source code +-------------------- + +You can find a runnable source code of the sample in the +:file:`samples/java/clojure/simple-sample` folder of the OpenCV +repository. After having installed OpenCV and Clojure as explained in +the tutorial, issue the following command to run the sample from the +command line. + +.. code:: bash + + cd path/to/samples/java/clojure/simple-sample + lein run + +Preamble +======== + +For detailed instruction on installing OpenCV with desktop Java support +refer to the `corresponding tutorial `_. + +If you are in hurry, here is a minimum quick start guide to install +OpenCV on Mac OS X: + + NOTE 1: I'm assuming you already installed + `xcode `_, + `jdk `_ + and `Cmake `_. + +.. code:: bash + + cd ~/ + mkdir opt + git clone https://github.com/Itseez/opencv.git + cd opencv + git checkout 2.4 + mkdir build + cd build + cmake -DBUILD_SHARED_LIBS=OFF .. + ... + ... + make -j8 + # optional + # make install + +Install Leiningen +================= + +Once you installed OpenCV with desktop java support the only other +requirement is to install +`Leiningeng `_ which allows +you to manage the entire life cycle of your CLJ projects. + +The available `installation guide `_ is very easy to be followed: + +1. `Download the script `_ +2. Place it on your ``$PATH`` (cf. ``~/bin`` is a good choice if it is + on your ``path``.) +3. Set the script to be executable. (i.e. ``chmod 755 ~/bin/lein``). + +If you work on Windows, follow `this instruction `_ + +You now have both the OpenCV library and a fully installed basic Clojure +environment. What is now needed is to configure the Clojure environment +to interact with the OpenCV library. + +Install the localrepo Leiningen plugin +======================================= + +The set of commands (tasks in Leiningen parlance) natively supported by +Leiningen can be very easily extended by various plugins. One of them is +the `lein-localrepo `_ +plugin which allows to install any jar lib as an artifact in the local +maven repository of your machine (typically in the ``~/.m2/repository`` +directory of your username). + +We're going to use this ``lein`` plugin to add to the local maven +repository the opencv components needed by Java and Clojure to use the +opencv lib. + +Generally speaking, if you want to use a plugin on project base only, it +can be added directly to a CLJ project created by ``lein``. + +Instead, when you want a plugin to be available to any CLJ project in +your username space, you can add it to the ``profiles.clj`` in the +``~/.lein/`` directory. + +The ``lein-localrepo`` plugin will be useful to me in other CLJ +projects where I need to call native libs wrapped by a Java interface. +So I decide to make it available to any CLJ project: + +.. code:: bash + + mkdir ~/.lein + +Create a file named ``profiles.clj`` in the ``~/.lein`` directory and +copy into it the following content: + +.. code:: clojure + + {:user {:plugins [[lein-localrepo "0.5.2"]]}} + +Here we're saying that the version release ``"0.5.2"`` of the +``lein-localrepo`` plugin will be available to the ``:user`` profile for +any CLJ project created by ``lein``. + +You do not need to do anything else to install the plugin because it +will be automatically downloaded from a remote repository the very first +time you issue any ``lein`` task. + +Install the java specific libs as local repository +================================================== + +If you followed the standard documentation for installing OpenCV on your +computer, you should find the following two libs under the directory +where you built OpenCV: + +- the ``build/bin/opencv-247.jar`` java lib +- the ``build/lib/libopencv_java247.dylib`` native lib (or ``.so`` in + you built OpenCV a GNU/Linux OS) + +They are the only opencv libs needed by the JVM to interact with OpenCV. + +Take apart the needed opencv libs +--------------------------------- + +Create a new directory to store in the above two libs. Start by copying +into it the ``opencv-247.jar`` lib. + +.. code:: bash + + cd ~/opt + mkdir clj-opencv + cd clj-opencv + cp ~/opt/opencv/build/bin/opencv-247.jar . + +First lib done. + +Now, to be able to add the ``libopencv_java247.dylib`` shared native lib +to the local maven repository, we first need to package it as a jar +file. + +The native lib has to be copied into a directories layout which mimics +the names of your operating system and architecture. I'm using a Mac OS +X with a X86 64 bit architecture. So my layout will be the following: + +.. code:: bash + + mkdir -p native/macosx/x86_64 + +Copy into the ``x86_64`` directory the ``libopencv_java247.dylib`` lib. + +.. code:: bash + + cp ~/opt/opencv/build/lib/libopencv_java247.dylib native/macosx/x86_64/ + +If you're running OpenCV from a different OS/Architecture pair, here +is a summary of the mapping you can choose from. + +.. code:: bash + + OS + + Mac OS X -> macosx + Windows -> windows + Linux -> linux + SunOS -> solaris + + Architectures + + amd64 -> x86_64 + x86_64 -> x86_64 + x86 -> x86 + i386 -> x86 + arm -> arm + sparc -> sparc + +Package the native lib as a jar +------------------------------- + +Next you need to package the native lib in a jar file by using the +``jar`` command to create a new jar file from a directory. + +.. code:: bash + + jar -cMf opencv-native-247.jar native + +Note that ehe ``M`` option instructs the ``jar`` command to not create +a MANIFEST file for the artifact. + +Your directories layout should look like the following: + +.. code:: bash + + tree + . + |__ native + |   |__ macosx + |   |__ x86_64 + |   |__ libopencv_java247.dylib + | + |__ opencv-247.jar + |__ opencv-native-247.jar + + 3 directories, 3 files + +Locally install the jars +------------------------ + +We are now ready to add the two jars as artifacts to the local maven +repository with the help of the ``lein-localrepo`` plugin. + +.. code:: bash + + lein localrepo install opencv-247.jar opencv/opencv 2.4.7 + +Here the ``localrepo install`` task creates the ``2.4.7.`` release of +the ``opencv/opencv`` maven artifact from the ``opencv-247.jar`` lib and +then installs it into the local maven repository. The ``opencv/opencv`` +artifact will then be available to any maven compliant project +(Leiningen is internally based on maven). + +Do the same thing with the native lib previously wrapped in a new jar +file. + +.. code:: bash + + lein localrepo install opencv-native-247.jar opencv/opencv-native 2.4.7 + +Note that the groupId, ``opencv``, of the two artifacts is the same. We +are now ready to create a new CLJ project to start interacting with +OpenCV. + +Create a project +---------------- + +Create a new CLJ project by using the ``lein new`` task from the +terminal. + +.. code:: bash + + # cd in the directory where you work with your development projects (e.g. ~/devel) + lein new simple-sample + Generating a project called simple-sample based on the 'default' template. + To see other templates (app, lein plugin, etc), try `lein help new`. + +The above task creates the following ``simple-sample`` directories +layout: + +.. code:: bash + + tree simple-sample/ + simple-sample/ + |__ LICENSE + |__ README.md + |__ doc + |   |__ intro.md + | + |__ project.clj + |__ resources + |__ src + |   |__ simple_sample + |   |__ core.clj + |__ test + |__ simple_sample + |__ core_test.clj + + 6 directories, 6 files + +We need to add the two ``opencv`` artifacts as dependencies of the newly +created project. Open the ``project.clj`` and modify its dependencies +section as follows: + +.. code:: bash + + (defproject simple-sample "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :dependencies [[org.clojure/clojure "1.5.1"] + [opencv/opencv "2.4.7"] ; added line + [opencv/opencv-native "2.4.7"]]) ;added line + + +Note that The Clojure Programming Language is a jar artifact too. This +is why Clojure is called an hosted language. + +To verify that everything went right issue the ``lein deps`` task. The +very first time you run a ``lein`` task it will take sometime to +download all the required dependencies before executing the task +itself. + +.. code:: bash + + cd simple-sample + lein deps + ... + +The ``deps`` task reads and merges from the ``project.clj`` and the +``~/.lein/profiles.clj`` files all the dependencies of the +``simple-sample`` project and verifies if they have already been +cached in the local maven repository. If the task returns without +messages about not being able to retrieve the two new artifacts your +installation is correct, otherwise go back and double check that you +did everything right. + +REPLing with OpenCV +------------------- + +Now ``cd`` in the ``simple-sample`` directory and issue the following +``lein`` task: + +.. code:: bash + + cd simple-sample + lein repl + ... + ... + nREPL server started on port 50907 on host 127.0.0.1 + REPL-y 0.3.0 + Clojure 1.5.1 + Docs: (doc function-name-here) + (find-doc "part-of-name-here") + Source: (source function-name-here) + Javadoc: (javadoc java-object-or-class-here) + Exit: Control+D or (exit) or (quit) + Results: Stored in vars *1, *2, *3, an exception in *e + + user=> + +You can immediately interact with the REPL by issuing any CLJ expression +to be evaluated. + +.. code:: clojure + + user=> (+ 41 1) + 42 + user=> (println "Hello, OpenCV!") + Hello, OpenCV! + nil + user=> (defn foo [] (str "bar")) + #'user/foo + user=> (foo) + "bar" + +When ran from the home directory of a lein based project, even if the +``lein repl`` task automatically loads all the project dependencies, you +still need to load the opencv native library to be able to interact with +the OpenCV. + +.. code:: clojure + + user=> (clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME) + nil + +Then you can start interacting with OpenCV by just referencing the fully +qualified names of its classes. + + NOTE 2: `Here `_ you can find the + full OpenCV Java API. + +.. code:: clojure + + user=> (org.opencv.core.Point. 0 0) + # + +Here we created a two dimensions opencv ``Point`` instance. Even if all +the java packages included within the java interface to OpenCV are +immediately available from the CLJ REPL, it's very annoying to prefix +the ``Point.`` instance constructors with the fully qualified package +name. + +Fortunately CLJ offer a very easy way to overcome this annoyance by +directly importing the ``Point`` class. + +.. code:: clojure + + user=> (import 'org.opencv.core.Point) + org.opencv.core.Point + user=> (def p1 (Point. 0 0)) + #'user/p1 + user=> p1 + # + user=> (def p2 (Point. 100 100)) + #'user/p2 + +We can even inspect the class of an instance and verify if the value of +a symbol is an instance of a ``Point`` java class. + +.. code:: clojure + + user=> (class p1) + org.opencv.core.Point + user=> (instance? org.opencv.core.Point p1) + true + +If we now want to use the opencv ``Rect`` class to create a rectangle, +we again have to fully qualify its constructor even if it leaves in +the same ``org.opencv.core`` package of the ``Point`` class. + +.. code:: clojure + + user=> (org.opencv.core.Rect. p1 p2) + # + +Again, the CLJ importing facilities is very handy and let you to map +more symbols in one shot. + +.. code:: clojure + + user=> (import '[org.opencv.core Point Rect Size]) + org.opencv.core.Size + user=> (def r1 (Rect. p1 p2)) + #'user/r1 + user=> r1 + # + user=> (class r1) + org.opencv.core.Rect + user=> (instance? org.opencv.core.Rect r1) + true + user=> (Size. 100 100) + # + user=> (def sq-100 (Size. 100 100)) + #'user/sq-100 + user=> (class sq-100) + org.opencv.core.Size + user=> (instance? org.opencv.core.Size sq-100) + true + +Obviously you can call methods on instances as well. + +.. code:: clojure + + user=> (.area r1) + 10000.0 + user=> (.area sq-100) + 10000.0 + +Or modify the value of a member field. + +.. code:: clojure + + user=> (set! (.x p1) 10) + 10 + user=> p1 + # + user=> (set! (.width sq-100) 10) + 10 + user=> (set! (.height sq-100) 10) + 10 + user=> (.area sq-100) + 100.0 + +If you find yourself not remembering a OpenCV class behavior, the +REPL gives you the opportunity to easily search the corresponding +javadoc documention: + +.. code:: clojure + + user=> (javadoc Rect) + "http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:org/opencv/core/Rect.html" + +Mimic the OpenCV Java Tutorial Sample in the REPL +------------------------------------------------- + +Let's now try to port to Clojure the `opencv java tutorial sample `_. +Instead of writing it in a source file we're going to evaluate it at the +REPL. + +Following is the original Java source code of the cited sample. + +.. code:: java + + import org.opencv.core.Mat; + import org.opencv.core.CvType; + import org.opencv.core.Scalar; + + class SimpleSample { + + static{ System.loadLibrary("opencv_java244"); } + + public static void main(String[] args) { + Mat m = new Mat(5, 10, CvType.CV_8UC1, new Scalar(0)); + System.out.println("OpenCV Mat: " + m); + Mat mr1 = m.row(1); + mr1.setTo(new Scalar(1)); + Mat mc5 = m.col(5); + mc5.setTo(new Scalar(5)); + System.out.println("OpenCV Mat data:\n" + m.dump()); + } + + } + +Add injections to the project +----------------------------- + +Before start coding, we'd like to eliminate the boring need of +interactively loading the native opencv lib any time we start a new REPL +to interact with it. + +First, stop the REPL by evaluating the ``(exit)`` expression at the REPL +prompt. + +.. code:: clojure + + user=> (exit) + Bye for now! + +Then open your ``project.clj`` file and edit it as follows: + +.. code:: clojure + + (defproject simple-sample "0.1.0-SNAPSHOT" + ... + :injections [(clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)]) + +Here we're saying to load the opencv native lib anytime we run the REPL +in such a way that we have not anymore to remember to manually do it. + +Rerun the ``lein repl`` task + +.. code:: bash + + lein repl + nREPL server started on port 51645 on host 127.0.0.1 + REPL-y 0.3.0 + Clojure 1.5.1 + Docs: (doc function-name-here) + (find-doc "part-of-name-here") + Source: (source function-name-here) + Javadoc: (javadoc java-object-or-class-here) + Exit: Control+D or (exit) or (quit) + Results: Stored in vars *1, *2, *3, an exception in *e + + user=> + +Import the interested OpenCV java interfaces. + +.. code:: clojure + + user=> (import '[org.opencv.core Mat CvType Scalar]) + org.opencv.core.Scalar + +We're going to mimic almost verbatim the original OpenCV java tutorial +to: + +- create a 5x10 matrix with all its elements intialized to 0 +- change the value of every element of the second row to 1 +- change the value of every element of the 6th column to 5 +- print the content of the obtained matrix + +.. code:: clojure + + user=> (def m (Mat. 5 10 CvType/CV_8UC1 (Scalar. 0 0))) + #'user/m + user=> (def mr1 (.row m 1)) + #'user/mr1 + user=> (.setTo mr1 (Scalar. 1 0)) + # + user=> (def mc5 (.col m 5)) + #'user/mc5 + user=> (.setTo mc5 (Scalar. 5 0)) + # + user=> (println (.dump m)) + [0, 0, 0, 0, 0, 5, 0, 0, 0, 0; + 1, 1, 1, 1, 1, 5, 1, 1, 1, 1; + 0, 0, 0, 0, 0, 5, 0, 0, 0, 0; + 0, 0, 0, 0, 0, 5, 0, 0, 0, 0; + 0, 0, 0, 0, 0, 5, 0, 0, 0, 0] + nil + +If you are accustomed to a functional language all those abused and +mutating nouns are going to irritate your preference for verbs. Even +if the CLJ interop syntax is very handy and complete, there is still +an impedance mismatch between any OOP language and any FP language +(bein Scala a mixed paradigms programming language). + +To exit the REPL type ``(exit)``, ``ctr-D`` or ``(quit)`` at the REPL +prompt. + +.. code:: clojure + + user=> (exit) + Bye for now! + +Interactively load and blur an image +------------------------------------ + +In the next sample you will learn how to interactively load and blur and +image from the REPL by using the following OpenCV methods: + +- the ``imread`` static method from the ``Highgui`` class to read an + image from a file +- the ``imwrite`` static method from the ``Highgui`` class to write an + image to a file +- the ``GaussianBlur`` static method from the ``Imgproc`` class to + apply to blur the original image + +We're also going to use the ``Mat`` class which is returned from the +``imread`` method and accpeted as the main argument to both the +``GaussianBlur`` and the ``imwrite`` methods. + +Add an image to the project +--------------------------- + +First we want to add an image file to a newly create directory for +storing static resources of the project. + +.. image:: images/lena.png + :alt: Original Image + :align: center + +.. code:: bash + + mkdir -p resources/images + cp ~/opt/opencv/doc/tutorials/introduction/desktop_java/images/lena.png resource/images/ + +Read the image +-------------- + +Now launch the REPL as usual and start by importing all the OpenCV +classes we're going to use: + +.. code:: clojure + + lein repl + nREPL server started on port 50624 on host 127.0.0.1 + REPL-y 0.3.0 + Clojure 1.5.1 + Docs: (doc function-name-here) + (find-doc "part-of-name-here") + Source: (source function-name-here) + Javadoc: (javadoc java-object-or-class-here) + Exit: Control+D or (exit) or (quit) + Results: Stored in vars *1, *2, *3, an exception in *e + + user=> (import '[org.opencv.core Mat Size CvType] + '[org.opencv.highgui Highgui] + '[org.opencv.imgproc Imgproc]) + org.opencv.imgproc.Imgproc + +Now read the image from the ``resources/images/lena.png`` file. + +.. code:: clojure + + user=> (def lena (Highgui/imread "resources/images/lena.png")) + #'user/lena + user=> lena + # + +As you see, by simply evaluating the ``lena`` symbol we know that +``lena.png`` is a ``512x512`` matrix of ``CV_8UC3`` elements type. Let's +create a new ``Mat`` instance of the same dimensions and elements type. + +.. code:: clojure + + user=> (def blurred (Mat. 512 512 CvType/CV_8UC3)) + #'user/blurred + user=> + +Now apply a ``GaussianBlur`` filter using ``lena`` as the source matrix +and ``blurred`` as the destination matrix. + +.. code:: clojure + + user=> (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3) + nil + +As a last step just save the ``blurred`` matrix in a new image file. + +.. code:: clojure + + user=> (Highgui/imwrite "resources/images/blurred.png" blurred) + true + user=> (exit) + Bye for now! + +Following is the new blurred image of Lena. + +.. image:: images/blurred.png + :alt: Blurred Image + :align: center + +Next Steps +========== + +This tutorial only introduces the very basic environment set up to be +able to interact with OpenCV in a CLJ REPL. + +I recommend any Clojure newbie to read the `Clojure Java Interop chapter `_ to get all you need to know +to interoperate with any plain java lib that has not been wrapped in +Clojure to make it usable in a more idiomatic and functional way within +Clojure. + +The OpenCV Java API does not wrap the ``highgui`` module +functionalities depending on ``Qt`` (e.g. ``namedWindow`` and +``imshow``. If you want to create windows and show images into them +while interacting with OpenCV from the REPL, at the moment you're left +at your own. You could use Java Swing to fill the gap. + + +License +------- + +Copyright © 2013 Giacomo (Mimmo) Cosenza aka Magomimmo + +Distributed under the BSD 3-clause License, the same of OpenCV. diff --git a/doc/tutorials/introduction/clojure_dev_intro/images/blurred.png b/doc/tutorials/introduction/clojure_dev_intro/images/blurred.png new file mode 100644 index 000000000..c8aeea8bc Binary files /dev/null and b/doc/tutorials/introduction/clojure_dev_intro/images/blurred.png differ diff --git a/doc/tutorials/introduction/clojure_dev_intro/images/lena.png b/doc/tutorials/introduction/clojure_dev_intro/images/lena.png new file mode 100644 index 000000000..68342fae5 Binary files /dev/null and b/doc/tutorials/introduction/clojure_dev_intro/images/lena.png differ diff --git a/doc/tutorials/introduction/table_of_content_introduction/images/clojure-logo.png b/doc/tutorials/introduction/table_of_content_introduction/images/clojure-logo.png new file mode 100644 index 000000000..f8a29b965 Binary files /dev/null and b/doc/tutorials/introduction/table_of_content_introduction/images/clojure-logo.png differ diff --git a/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst b/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst index ee8dd67ef..ba67fb6b4 100644 --- a/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst +++ b/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst @@ -156,6 +156,21 @@ world of the OpenCV. :height: 90pt :width: 90pt + ================ ================================================= + |ClojureLogo| **Title:** :ref:`clojure_dev_intro` + + *Compatibility:* > OpenCV 2.4.4 + + *Author:* |Author_MimmoC| + + A tutorial on how to interactively use OpenCV from the Clojure REPL. + + ================ ================================================= + + .. |ClojureLogo| image:: images/clojure-logo.png + :height: 90pt + :width: 90pt + * **Android** .. tabularcolumns:: m{100pt} m{300pt} @@ -314,6 +329,7 @@ world of the OpenCV. ../windows_visual_studio_image_watch/windows_visual_studio_image_watch ../desktop_java/java_dev_intro ../java_eclipse/java_eclipse + ../clojure_dev_intro/clojure_dev_intro ../android_binary_package/android_dev_intro ../android_binary_package/O4A_SDK ../android_binary_package/dev_with_OCV_on_Android diff --git a/modules/core/include/opencv2/core/utility.hpp b/modules/core/include/opencv2/core/utility.hpp index dae38282a..6a3e486b6 100644 --- a/modules/core/include/opencv2/core/utility.hpp +++ b/modules/core/include/opencv2/core/utility.hpp @@ -305,6 +305,32 @@ private: AutoLock& operator = (const AutoLock&); }; +class TLSDataContainer +{ +private: + int key_; +protected: + CV_EXPORTS TLSDataContainer(); + CV_EXPORTS ~TLSDataContainer(); // virtual is not required +public: + virtual void* createDataInstance() const = 0; + virtual void deleteDataInstance(void* data) const = 0; + + CV_EXPORTS void* getData() const; +}; + +template +class TLSData : protected TLSDataContainer +{ +public: + inline TLSData() {} + inline ~TLSData() {} + inline T* get() const { return (T*)getData(); } +private: + virtual void* createDataInstance() const { return new T; } + virtual void deleteDataInstance(void* data) const { delete (T*)data; } +}; + // The CommandLineParser class is designed for command line arguments parsing class CV_EXPORTS CommandLineParser diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 27865971b..38b418879 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -738,26 +738,6 @@ namespace cv { bool __termination = false; } -#if defined CVAPI_EXPORTS && defined WIN32 && !defined WINCE -#ifdef HAVE_WINRT - #pragma warning(disable:4447) // Disable warning 'main' signature found without threading model -#endif - -BOOL WINAPI DllMain( HINSTANCE, DWORD, LPVOID ); - -BOOL WINAPI DllMain( HINSTANCE, DWORD fdwReason, LPVOID lpReserved ) -{ - if( fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH ) - { - if (lpReserved != NULL) // called after ExitProcess() call - cv::__termination = true; - cv::deleteThreadAllocData(); - cv::deleteThreadData(); - } - return TRUE; -} -#endif - namespace cv { @@ -841,7 +821,224 @@ void Mutex::lock() { impl->lock(); } void Mutex::unlock() { impl->unlock(); } bool Mutex::trylock() { return impl->trylock(); } + +//////////////////////////////// thread-local storage //////////////////////////////// + +class TLSStorage +{ + std::vector tlsData_; +public: + TLSStorage() { tlsData_.reserve(16); } + ~TLSStorage(); + inline void* getData(int key) const + { + CV_DbgAssert(key >= 0); + return (key < (int)tlsData_.size()) ? tlsData_[key] : NULL; + } + inline void setData(int key, void* data) + { + CV_DbgAssert(key >= 0); + if (key >= (int)tlsData_.size()) + { + tlsData_.resize(key + 1, NULL); + } + tlsData_[key] = data; + } + + inline static TLSStorage* get(); +}; + +#ifdef WIN32 +#pragma warning(disable:4505) // unreferenced local function has been removed + +#ifdef HAVE_WINRT + // using C++11 thread attribute for local thread data + static __declspec( thread ) TLSStorage* g_tlsdata = NULL; + + static void deleteThreadData() + { + if (g_tlsdata) + { + delete g_tlsdata; + g_tlsdata = NULL; + } + } + + inline TLSStorage* TLSStorage::get() + { + if (!g_tlsdata) + { + g_tlsdata = new TLSStorage; + } + return g_tlsdata; + } +#else +#ifdef WINCE +# define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) +#endif + static DWORD tlsKey = TLS_OUT_OF_INDEXES; + + static void deleteThreadData() + { + if(tlsKey != TLS_OUT_OF_INDEXES) + { + delete (TLSStorage*)TlsGetValue(tlsKey); + TlsSetValue(tlsKey, NULL); + } + } + + inline TLSStorage* TLSStorage::get() + { + if (tlsKey == TLS_OUT_OF_INDEXES) + { + tlsKey = TlsAlloc(); + CV_Assert(tlsKey != TLS_OUT_OF_INDEXES); + } + TLSStorage* d = (TLSStorage*)TlsGetValue(tlsKey); + if (!d) + { + d = new TLSStorage; + TlsSetValue(tlsKey, d); + } + return d; + } +#endif //HAVE_WINRT + +#if defined CVAPI_EXPORTS && defined WIN32 && !defined WINCE +#ifdef HAVE_WINRT + #pragma warning(disable:4447) // Disable warning 'main' signature found without threading model +#endif + +BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID); + +BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) +{ + if (fdwReason == DLL_THREAD_DETACH || fdwReason == DLL_PROCESS_DETACH) + { + cv::deleteThreadAllocData(); + cv::deleteThreadRNGData(); + cv::deleteThreadData(); + } + return TRUE; } +#endif + +#else + static pthread_key_t tlsKey = 0; + static pthread_once_t tlsKeyOnce = PTHREAD_ONCE_INIT; + + static void deleteTLSStorage(void* data) + { + delete (TLSStorage*)data; + } + + static void makeKey() + { + int errcode = pthread_key_create(&tlsKey, deleteTLSStorage); + CV_Assert(errcode == 0); + } + + inline TLSStorage* TLSStorage::get() + { + pthread_once(&tlsKeyOnce, makeKey); + TLSStorage* d = (TLSStorage*)pthread_getspecific(tlsKey); + if( !d ) + { + d = new TLSStorage; + pthread_setspecific(tlsKey, d); + } + return d; + } +#endif + +class TLSContainerStorage +{ + cv::Mutex mutex_; + std::vector tlsContainers_; +public: + TLSContainerStorage() { } + ~TLSContainerStorage() + { + for (size_t i = 0; i < tlsContainers_.size(); i++) + { + CV_DbgAssert(tlsContainers_[i] == NULL); // not all keys released + tlsContainers_[i] = NULL; + } + } + + int allocateKey(TLSDataContainer* pContainer) + { + cv::AutoLock lock(mutex_); + tlsContainers_.push_back(pContainer); + return (int)tlsContainers_.size() - 1; + } + void releaseKey(int id, TLSDataContainer* pContainer) + { + cv::AutoLock lock(mutex_); + CV_Assert(tlsContainers_[id] == pContainer); + tlsContainers_[id] = NULL; + // currently, we don't go into thread's TLSData and release data for this key + } + + void destroyData(int key, void* data) + { + cv::AutoLock lock(mutex_); + TLSDataContainer* k = tlsContainers_[key]; + if (!k) + return; + try + { + k->deleteDataInstance(data); + } + catch (...) + { + CV_DbgAssert(k == NULL); // Debug this! + } + } +}; +static TLSContainerStorage tlsContainerStorage; + +TLSDataContainer::TLSDataContainer() + : key_(-1) +{ + key_ = tlsContainerStorage.allocateKey(this); +} + +TLSDataContainer::~TLSDataContainer() +{ + tlsContainerStorage.releaseKey(key_, this); + key_ = -1; +} + +void* TLSDataContainer::getData() const +{ + CV_Assert(key_ >= 0); + TLSStorage* tlsData = TLSStorage::get(); + void* data = tlsData->getData(key_); + if (!data) + { + data = this->createDataInstance(); + CV_DbgAssert(data != NULL); + tlsData->setData(key_, data); + } + return data; +} + +TLSStorage::~TLSStorage() +{ + for (int i = 0; i < (int)tlsData_.size(); i++) + { + void*& data = tlsData_[i]; + if (data) + { + tlsContainerStorage.destroyData(i, data); + data = NULL; + } + } + tlsData_.clear(); +} + +} // namespace cv //////////////////////////////// thread-local storage //////////////////////////////// diff --git a/modules/features2d/src/freak.cpp b/modules/features2d/src/freak.cpp index 086a2e2e7..de487c6b4 100644 --- a/modules/features2d/src/freak.cpp +++ b/modules/features2d/src/freak.cpp @@ -54,8 +54,9 @@ static const int FREAK_NB_SCALES = FREAK::NB_SCALES; static const int FREAK_NB_PAIRS = FREAK::NB_PAIRS; static const int FREAK_NB_ORIENPAIRS = FREAK::NB_ORIENPAIRS; +// default pairs static const int FREAK_DEF_PAIRS[FREAK::NB_PAIRS] = -{ // default pairs +{ 404,431,818,511,181,52,311,874,774,543,719,230,417,205,11, 560,149,265,39,306,165,857,250,8,61,15,55,717,44,412, 592,134,761,695,660,782,625,487,549,516,271,665,762,392,178, @@ -92,15 +93,17 @@ static const int FREAK_DEF_PAIRS[FREAK::NB_PAIRS] = 670,249,36,581,389,605,331,518,442,822 }; +// used to sort pairs during pairs selection struct PairStat -{ // used to sort pairs during pairs selection +{ double mean; int idx; }; struct sortMean { - bool operator()( const PairStat& a, const PairStat& b ) const { + bool operator()( const PairStat& a, const PairStat& b ) const + { return a.mean < b.mean; } }; @@ -130,17 +133,21 @@ void FREAK::buildPattern() radius[6]/2.0, radius[6]/2.0 }; // fill the lookup table - for( int scaleIdx=0; scaleIdx < FREAK_NB_SCALES; ++scaleIdx ) { + for( int scaleIdx=0; scaleIdx < FREAK_NB_SCALES; ++scaleIdx ) + { patternSizes[scaleIdx] = 0; // proper initialization scalingFactor = std::pow(scaleStep,scaleIdx); //scale of the pattern, scaleStep ^ scaleIdx - for( int orientationIdx = 0; orientationIdx < FREAK_NB_ORIENTATION; ++orientationIdx ) { + for( int orientationIdx = 0; orientationIdx < FREAK_NB_ORIENTATION; ++orientationIdx ) + { theta = double(orientationIdx)* 2*CV_PI/double(FREAK_NB_ORIENTATION); // orientation of the pattern int pointIdx = 0; PatternPoint* patternLookupPtr = &patternLookup[0]; - for( size_t i = 0; i < 8; ++i ) { - for( int k = 0 ; k < n[i]; ++k ) { + for( size_t i = 0; i < 8; ++i ) + { + for( int k = 0 ; k < n[i]; ++k ) + { beta = CV_PI/n[i] * (i%2); // orientation offset so that groups of points on each circles are staggered alpha = double(k)* 2*CV_PI/double(n[i])+beta+theta; @@ -182,7 +189,8 @@ void FREAK::buildPattern() orientationPairs[39].i=30; orientationPairs[39].j=33; orientationPairs[40].i=31; orientationPairs[40].j=34; orientationPairs[41].i=32; orientationPairs[41].j=35; orientationPairs[42].i=36; orientationPairs[42].j=39; orientationPairs[43].i=37; orientationPairs[43].j=40; orientationPairs[44].i=38; orientationPairs[44].j=41; - for( unsigned m = FREAK_NB_ORIENPAIRS; m--; ) { + for( unsigned m = FREAK_NB_ORIENPAIRS; m--; ) + { const float dx = patternLookup[orientationPairs[m].i].x-patternLookup[orientationPairs[m].j].x; const float dy = patternLookup[orientationPairs[m].i].y-patternLookup[orientationPairs[m].j].y; const float norm_sq = (dx*dx+dy*dy); @@ -192,30 +200,37 @@ void FREAK::buildPattern() // build the list of description pairs std::vector allPairs; - for( unsigned int i = 1; i < (unsigned int)FREAK_NB_POINTS; ++i ) { + for( unsigned int i = 1; i < (unsigned int)FREAK_NB_POINTS; ++i ) + { // (generate all the pairs) - for( unsigned int j = 0; (unsigned int)j < i; ++j ) { + for( unsigned int j = 0; (unsigned int)j < i; ++j ) + { DescriptionPair pair = {(uchar)i,(uchar)j}; allPairs.push_back(pair); } } // Input vector provided - if( !selectedPairs0.empty() ) { - if( (int)selectedPairs0.size() == FREAK_NB_PAIRS ) { + if( !selectedPairs0.empty() ) + { + if( (int)selectedPairs0.size() == FREAK_NB_PAIRS ) + { for( int i = 0; i < FREAK_NB_PAIRS; ++i ) descriptionPairs[i] = allPairs[selectedPairs0.at(i)]; } - else { + else + { CV_Error(Error::StsVecLengthErr, "Input vector does not match the required size"); } } - else { // default selected pairs + else // default selected pairs + { for( int i = 0; i < FREAK_NB_PAIRS; ++i ) descriptionPairs[i] = allPairs[FREAK_DEF_PAIRS[i]]; } } -void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat& descriptors ) const { +void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat& descriptors ) const +{ if( image.empty() ) return; @@ -236,8 +251,10 @@ void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat int direction1; // compute the scale index corresponding to the keypoint size and remove keypoints close to the border - if( scaleNormalized ) { - for( size_t k = keypoints.size(); k--; ) { + if( scaleNormalized ) + { + for( size_t k = keypoints.size(); k--; ) + { //Is k non-zero? If so, decrement it and continue" kpScaleIdx[k] = std::max( (int)(std::log(keypoints[k].size/FREAK_SMALLEST_KP_SIZE)*sizeCst+0.5) ,0); if( kpScaleIdx[k] >= FREAK_NB_SCALES ) @@ -247,24 +264,29 @@ void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat keypoints[k].pt.y <= patternSizes[kpScaleIdx[k]] || keypoints[k].pt.x >= image.cols-patternSizes[kpScaleIdx[k]] || keypoints[k].pt.y >= image.rows-patternSizes[kpScaleIdx[k]] - ) { + ) + { keypoints.erase(kpBegin+k); kpScaleIdx.erase(ScaleIdxBegin+k); } } } - else { + else + { const int scIdx = std::max( (int)(1.0986122886681*sizeCst+0.5) ,0); - for( size_t k = keypoints.size(); k--; ) { + for( size_t k = keypoints.size(); k--; ) + { kpScaleIdx[k] = scIdx; // equivalent to the formule when the scale is normalized with a constant size of keypoints[k].size=3*SMALLEST_KP_SIZE - if( kpScaleIdx[k] >= FREAK_NB_SCALES ) { + if( kpScaleIdx[k] >= FREAK_NB_SCALES ) + { kpScaleIdx[k] = FREAK_NB_SCALES-1; } if( keypoints[k].pt.x <= patternSizes[kpScaleIdx[k]] || keypoints[k].pt.y <= patternSizes[kpScaleIdx[k]] || keypoints[k].pt.x >= image.cols-patternSizes[kpScaleIdx[k]] || keypoints[k].pt.y >= image.rows-patternSizes[kpScaleIdx[k]] - ) { + ) + { keypoints.erase(kpBegin+k); kpScaleIdx.erase(ScaleIdxBegin+k); } @@ -272,7 +294,8 @@ void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat } // allocate descriptor memory, estimate orientations, extract descriptors - if( !extAll ) { + if( !extAll ) + { // extract the best comparisons only descriptors = cv::Mat::zeros((int)keypoints.size(), FREAK_NB_PAIRS/8, CV_8U); #if CV_SSE2 @@ -280,20 +303,25 @@ void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat #else std::bitset* ptr = (std::bitset*) (descriptors.data+(keypoints.size()-1)*descriptors.step[0]); #endif - for( size_t k = keypoints.size(); k--; ) { + for( size_t k = keypoints.size(); k--; ) + { // estimate orientation (gradient) - if( !orientationNormalized ) { + if( !orientationNormalized ) + { thetaIdx = 0; // assign 0° to all keypoints keypoints[k].angle = 0.0; } - else { + else + { // get the points intensity value in the un-rotated pattern - for( int i = FREAK_NB_POINTS; i--; ) { + for( int i = FREAK_NB_POINTS; i--; ) + { pointsValue[i] = meanIntensity(image, imgIntegral, keypoints[k].pt.x,keypoints[k].pt.y, kpScaleIdx[k], 0, i); } direction0 = 0; direction1 = 0; - for( int m = 45; m--; ) { + for( int m = 45; m--; ) + { //iterate through the orientation pairs const int delta = (pointsValue[ orientationPairs[m].i ]-pointsValue[ orientationPairs[m].j ]); direction0 += delta*(orientationPairs[m].weight_dx)/2048; @@ -309,7 +337,8 @@ void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat thetaIdx -= FREAK_NB_ORIENTATION; } // extract descriptor at the computed orientation - for( int i = FREAK_NB_POINTS; i--; ) { + for( int i = FREAK_NB_POINTS; i--; ) + { pointsValue[i] = meanIntensity(image, imgIntegral, keypoints[k].pt.x,keypoints[k].pt.y, kpScaleIdx[k], thetaIdx, i); } #if CV_SSE2 @@ -384,24 +413,29 @@ void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat #endif } } - else { // extract all possible comparisons for selection + else // extract all possible comparisons for selection + { descriptors = cv::Mat::zeros((int)keypoints.size(), 128, CV_8U); std::bitset<1024>* ptr = (std::bitset<1024>*) (descriptors.data+(keypoints.size()-1)*descriptors.step[0]); - for( size_t k = keypoints.size(); k--; ) { + for( size_t k = keypoints.size(); k--; ) + { //estimate orientation (gradient) - if( !orientationNormalized ) { + if( !orientationNormalized ) + { thetaIdx = 0;//assign 0° to all keypoints keypoints[k].angle = 0.0; } - else { + else + { //get the points intensity value in the un-rotated pattern for( int i = FREAK_NB_POINTS;i--; ) pointsValue[i] = meanIntensity(image, imgIntegral, keypoints[k].pt.x,keypoints[k].pt.y, kpScaleIdx[k], 0, i); direction0 = 0; direction1 = 0; - for( int m = 45; m--; ) { + for( int m = 45; m--; ) + { //iterate through the orientation pairs const int delta = (pointsValue[ orientationPairs[m].i ]-pointsValue[ orientationPairs[m].j ]); direction0 += delta*(orientationPairs[m].weight_dx)/2048; @@ -418,15 +452,18 @@ void FREAK::computeImpl( const Mat& image, std::vector& keypoints, Mat thetaIdx -= FREAK_NB_ORIENTATION; } // get the points intensity value in the rotated pattern - for( int i = FREAK_NB_POINTS; i--; ) { + for( int i = FREAK_NB_POINTS; i--; ) + { pointsValue[i] = meanIntensity(image, imgIntegral, keypoints[k].pt.x, keypoints[k].pt.y, kpScaleIdx[k], thetaIdx, i); } int cnt(0); - for( int i = 1; i < FREAK_NB_POINTS; ++i ) { + for( int i = 1; i < FREAK_NB_POINTS; ++i ) + { //(generate all the pairs) - for( int j = 0; j < i; ++j ) { + for( int j = 0; j < i; ++j ) + { ptr->set(cnt, pointsValue[i] >= pointsValue[j] ); ++cnt; } @@ -442,7 +479,8 @@ uchar FREAK::meanIntensity( const cv::Mat& image, const cv::Mat& integral, const float kp_y, const unsigned int scale, const unsigned int rot, - const unsigned int point) const { + const unsigned int point) const +{ // get point position in image const PatternPoint& FreakPoint = patternLookup[scale*FREAK_NB_ORIENTATION*FREAK_NB_POINTS + rot*FREAK_NB_POINTS + point]; const float xf = FreakPoint.x+kp_x; @@ -455,7 +493,8 @@ uchar FREAK::meanIntensity( const cv::Mat& image, const cv::Mat& integral, const float radius = FreakPoint.sigma; // calculate output: - if( radius < 0.5 ) { + if( radius < 0.5 ) + { // interpolation multipliers: const int r_x = static_cast((xf-x)*1024); const int r_y = static_cast((yf-y)*1024); @@ -507,7 +546,8 @@ std::vector FREAK::selectPairs(const std::vector& images if( verbose ) std::cout << "Number of images: " << images.size() << std::endl; - for( size_t i = 0;i < images.size(); ++i ) { + for( size_t i = 0;i < images.size(); ++i ) + { Mat descriptorsTmp; computeImpl(images[i],keypoints[i],descriptorsTmp); descriptors.push_back(descriptorsTmp); @@ -520,8 +560,10 @@ std::vector FREAK::selectPairs(const std::vector& images Mat descriptorsFloat = Mat::zeros(descriptors.rows, 903, CV_32F); std::bitset<1024>* ptr = (std::bitset<1024>*) (descriptors.data+(descriptors.rows-1)*descriptors.step[0]); - for( int m = descriptors.rows; m--; ) { - for( int n = 903; n--; ) { + for( int m = descriptors.rows; m--; ) + { + for( int n = 903; n--; ) + { if( ptr->test(n) == true ) descriptorsFloat.at(m,n)=1.0f; } @@ -529,7 +571,8 @@ std::vector FREAK::selectPairs(const std::vector& images } std::vector pairStat; - for( int n = 903; n--; ) { + for( int n = 903; n--; ) + { // the higher the variance, the better --> mean = 0.5 PairStat tmp = { fabs( mean(descriptorsFloat.col(n))[0]-0.5 ) ,n}; pairStat.push_back(tmp); @@ -538,19 +581,22 @@ std::vector FREAK::selectPairs(const std::vector& images std::sort( pairStat.begin(),pairStat.end(), sortMean() ); std::vector bestPairs; - for( int m = 0; m < 903; ++m ) { + for( int m = 0; m < 903; ++m ) + { if( verbose ) std::cout << m << ":" << bestPairs.size() << " " << std::flush; double corrMax(0); - for( size_t n = 0; n < bestPairs.size(); ++n ) { + for( size_t n = 0; n < bestPairs.size(); ++n ) + { int idxA = bestPairs[n].idx; int idxB = pairStat[m].idx; double corr(0); // compute correlation between 2 pairs corr = fabs(compareHist(descriptorsFloat.col(idxA), descriptorsFloat.col(idxB), HISTCMP_CORREL)); - if( corr > corrMax ) { + if( corr > corrMax ) + { corrMax = corr; if( corrMax >= corrTresh ) break; @@ -560,7 +606,8 @@ std::vector FREAK::selectPairs(const std::vector& images if( corrMax < corrTresh/*0.7*/ ) bestPairs.push_back(pairStat[m]); - if( bestPairs.size() >= 512 ) { + if( bestPairs.size() >= 512 ) + { if( verbose ) std::cout << m << std::endl; break; @@ -568,11 +615,13 @@ std::vector FREAK::selectPairs(const std::vector& images } std::vector idxBestPairs; - if( (int)bestPairs.size() >= FREAK_NB_PAIRS ) { + if( (int)bestPairs.size() >= FREAK_NB_PAIRS ) + { for( int i = 0; i < FREAK_NB_PAIRS; ++i ) idxBestPairs.push_back(bestPairs[i].idx); } - else { + else + { if( verbose ) std::cout << "correlation threshold too small (restrictive)" << std::endl; CV_Error(Error::StsError, "correlation threshold too small (restrictive)"); @@ -583,11 +632,13 @@ std::vector FREAK::selectPairs(const std::vector& images /* +// create an image showing the brisk pattern void FREAKImpl::drawPattern() -{ // create an image showing the brisk pattern +{ Mat pattern = Mat::zeros(1000, 1000, CV_8UC3) + Scalar(255,255,255); int sFac = 500 / patternScale; - for( int n = 0; n < kNB_POINTS; ++n ) { + for( int n = 0; n < kNB_POINTS; ++n ) + { PatternPoint& pt = patternLookup[n]; circle(pattern, Point( pt.x*sFac,pt.y*sFac)+Point(500,500), pt.sigma*sFac, Scalar(0,0,255),2); // rectangle(pattern, Point( (pt.x-pt.sigma)*sFac,(pt.y-pt.sigma)*sFac)+Point(500,500), Point( (pt.x+pt.sigma)*sFac,(pt.y+pt.sigma)*sFac)+Point(500,500), Scalar(0,0,255),2); @@ -615,11 +666,13 @@ FREAK::~FREAK() { } -int FREAK::descriptorSize() const { +int FREAK::descriptorSize() const +{ return FREAK_NB_PAIRS / 8; // descriptor length in bytes } -int FREAK::descriptorType() const { +int FREAK::descriptorType() const +{ return CV_8U; } diff --git a/modules/ocl/CMakeLists.txt b/modules/ocl/CMakeLists.txt index 21e0b3085..db9d64e1e 100644 --- a/modules/ocl/CMakeLists.txt +++ b/modules/ocl/CMakeLists.txt @@ -5,4 +5,7 @@ endif() set(the_description "OpenCL-accelerated Computer Vision") ocv_define_module(ocl opencv_core opencv_imgproc opencv_features2d opencv_objdetect opencv_video opencv_calib3d opencv_ml "${OPENCL_LIBRARIES}") +if(TARGET opencv_test_ocl) + target_link_libraries(opencv_test_ocl "${OPENCL_LIBRARIES}") +endif() ocv_warnings_disable(CMAKE_CXX_FLAGS -Wshadow) diff --git a/modules/ocl/doc/structures_and_utility_functions.rst b/modules/ocl/doc/structures_and_utility_functions.rst index aec3f70bf..1d1265c81 100644 --- a/modules/ocl/doc/structures_and_utility_functions.rst +++ b/modules/ocl/doc/structures_and_utility_functions.rst @@ -25,12 +25,26 @@ Returns the list of devices ocl::setDevice -------------- -Returns void +Initialize OpenCL computation context .. ocv:function:: void ocl::setDevice( const DeviceInfo* info ) :param info: device info +ocl::initializeContext +-------------------------------- +Alternative way to initialize OpenCL computation context. + +.. ocv:function:: void ocl::initializeContext(void* pClPlatform, void* pClContext, void* pClDevice) + + :param pClPlatform: selected ``platform_id`` (via pointer, parameter type is ``cl_platform_id*``) + + :param pClContext: selected ``cl_context`` (via pointer, parameter type is ``cl_context*``) + + :param pClDevice: selected ``cl_device_id`` (via pointer, parameter type is ``cl_device_id*``) + +This function can be used for context initialization with D3D/OpenGL interoperability. + ocl::setBinaryPath ------------------ Returns void diff --git a/modules/ocl/include/opencv2/ocl.hpp b/modules/ocl/include/opencv2/ocl.hpp index fd1f6660d..ea1c214e6 100644 --- a/modules/ocl/include/opencv2/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl.hpp @@ -118,6 +118,7 @@ namespace cv const PlatformInfo* platform; DeviceInfo(); + ~DeviceInfo(); }; struct PlatformInfo @@ -136,6 +137,7 @@ namespace cv std::vector devices; PlatformInfo(); + ~PlatformInfo(); }; //////////////////////////////// Initialization & Info //////////////////////// @@ -151,6 +153,10 @@ namespace cv // set device you want to use CV_EXPORTS void setDevice(const DeviceInfo* info); + // Initialize from OpenCL handles directly. + // Argument types is (pointers): cl_platform_id*, cl_context*, cl_device_id* + CV_EXPORTS void initializeContext(void* pClPlatform, void* pClContext, void* pClDevice); + enum FEATURE_TYPE { FEATURE_CL_DOUBLE = 1, diff --git a/modules/ocl/include/opencv2/ocl/matrix_operations.hpp b/modules/ocl/include/opencv2/ocl/matrix_operations.hpp index ab1fe4fb7..410adbd8b 100644 --- a/modules/ocl/include/opencv2/ocl/matrix_operations.hpp +++ b/modules/ocl/include/opencv2/ocl/matrix_operations.hpp @@ -175,6 +175,7 @@ namespace cv data = m.data; datastart = m.datastart; dataend = m.dataend; + clCxt = m.clCxt; wholerows = m.wholerows; wholecols = m.wholecols; offset = m.offset; diff --git a/modules/ocl/src/cl_context.cpp b/modules/ocl/src/cl_context.cpp index 76e7eda19..d6d081fe1 100644 --- a/modules/ocl/src/cl_context.cpp +++ b/modules/ocl/src/cl_context.cpp @@ -57,6 +57,12 @@ namespace cv { namespace ocl { +using namespace cl_utils; + +#if defined(WIN32) +static bool __termination = false; +#endif + struct __Module { __Module(); @@ -71,36 +77,10 @@ cv::Mutex& getInitializationMutex() return __module.initializationMutex; } - -struct PlatformInfoImpl +static cv::Mutex& getCurrentContextMutex() { - cl_platform_id platform_id; - - std::vector deviceIDs; - - PlatformInfo info; - - PlatformInfoImpl() - : platform_id(NULL) - { - } -}; - -struct DeviceInfoImpl -{ - cl_platform_id platform_id; - cl_device_id device_id; - - DeviceInfo info; - - DeviceInfoImpl() - : platform_id(NULL), device_id(NULL) - { - } -}; - -static std::vector global_platforms; -static std::vector global_devices; + return __module.currentContextMutex; +} static bool parseOpenCLVersion(const std::string& versionStr, int& major, int& minor) { @@ -131,6 +111,141 @@ static bool parseOpenCLVersion(const std::string& versionStr, int& major, int& m return true; } +struct PlatformInfoImpl : public PlatformInfo +{ + cl_platform_id platform_id; + + std::vector deviceIDs; + + PlatformInfoImpl() + : platform_id(NULL) + { + } + + void init(int id, cl_platform_id platform) + { + CV_Assert(platform_id == NULL); + + this->_id = id; + platform_id = platform; + + openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_PROFILE, this->platformProfile)); + openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_VERSION, this->platformVersion)); + openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_NAME, this->platformName)); + openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_VENDOR, this->platformVendor)); + openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_EXTENSIONS, this->platformExtensons)); + + parseOpenCLVersion(this->platformVersion, + this->platformVersionMajor, this->platformVersionMinor); + } + +}; + +struct DeviceInfoImpl: public DeviceInfo +{ + cl_platform_id platform_id; + cl_device_id device_id; + + DeviceInfoImpl() + : platform_id(NULL), device_id(NULL) + { + } + + void init(int id, PlatformInfoImpl& platformInfoImpl, cl_device_id device) + { + CV_Assert(device_id == NULL); + + this->_id = id; + platform_id = platformInfoImpl.platform_id; + device_id = device; + + this->platform = &platformInfoImpl; + + cl_device_type type = cl_device_type(-1); + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_TYPE, type)); + this->deviceType = DeviceType(type); + + openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_PROFILE, this->deviceProfile)); + openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_VERSION, this->deviceVersion)); + openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_NAME, this->deviceName)); + openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_VENDOR, this->deviceVendor)); + cl_uint vendorID = 0; + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_VENDOR_ID, vendorID)); + this->deviceVendorId = vendorID; + openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DRIVER_VERSION, this->deviceDriverVersion)); + openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_EXTENSIONS, this->deviceExtensions)); + + parseOpenCLVersion(this->deviceVersion, + this->deviceVersionMajor, this->deviceVersionMinor); + + size_t maxWorkGroupSize = 0; + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_WORK_GROUP_SIZE, maxWorkGroupSize)); + this->maxWorkGroupSize = maxWorkGroupSize; + + cl_uint maxDimensions = 0; + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, maxDimensions)); + std::vector maxWorkItemSizes(maxDimensions); + openCLSafeCall(clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(size_t) * maxDimensions, + (void *)&maxWorkItemSizes[0], 0)); + this->maxWorkItemSizes = maxWorkItemSizes; + + cl_uint maxComputeUnits = 0; + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_COMPUTE_UNITS, maxComputeUnits)); + this->maxComputeUnits = maxComputeUnits; + + cl_ulong localMemorySize = 0; + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_LOCAL_MEM_SIZE, localMemorySize)); + this->localMemorySize = (size_t)localMemorySize; + + cl_ulong maxMemAllocSize = 0; + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, maxMemAllocSize)); + this->maxMemAllocSize = (size_t)maxMemAllocSize; + + cl_bool unifiedMemory = false; + openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_HOST_UNIFIED_MEMORY, unifiedMemory)); + this->isUnifiedMemory = unifiedMemory != 0; + + //initialize extra options for compilation. Currently only fp64 is included. + //Assume 4KB is enough to store all possible extensions. + openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_EXTENSIONS, this->deviceExtensions)); + + size_t fp64_khr = this->deviceExtensions.find("cl_khr_fp64"); + if(fp64_khr != std::string::npos) + { + this->compilationExtraOptions += "-D DOUBLE_SUPPORT"; + this->haveDoubleSupport = true; + } + else + { + this->haveDoubleSupport = false; + } + + size_t intel_platform = platformInfoImpl.platformVendor.find("Intel"); + if(intel_platform != std::string::npos) + { + this->compilationExtraOptions += " -D INTEL_DEVICE"; + this->isIntelDevice = true; + } + else + { + this->isIntelDevice = false; + } + + if (id < 0) + { +#ifdef CL_VERSION_1_2 + if (this->deviceVersionMajor > 1 || (this->deviceVersionMajor == 1 && this->deviceVersionMinor >= 2)) + { + ::clRetainDevice(device); + } +#endif + } + } +}; + +static std::vector global_platforms; +static std::vector global_devices; + static void split(const std::string &s, char delim, std::vector &elems) { std::stringstream ss(s); std::string item; @@ -329,8 +444,6 @@ not_found: static bool __initialized = false; static int initializeOpenCLDevices() { - using namespace cl_utils; - assert(!__initialized); __initialized = true; @@ -351,19 +464,9 @@ static int initializeOpenCLDevices() for (size_t i = 0; i < platforms.size(); ++i) { PlatformInfoImpl& platformInfo = global_platforms[i]; - platformInfo.info._id = i; cl_platform_id platform = platforms[i]; - - platformInfo.platform_id = platform; - openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_PROFILE, platformInfo.info.platformProfile)); - openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_VERSION, platformInfo.info.platformVersion)); - openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_NAME, platformInfo.info.platformName)); - openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_VENDOR, platformInfo.info.platformVendor)); - openCLSafeCall(getStringInfo(clGetPlatformInfo, platform, CL_PLATFORM_EXTENSIONS, platformInfo.info.platformExtensons)); - - parseOpenCLVersion(platformInfo.info.platformVersion, - platformInfo.info.platformVersionMajor, platformInfo.info.platformVersionMinor); + platformInfo.init(i, platform); std::vector devices; cl_int status = getDevices(platform, CL_DEVICE_TYPE_ALL, devices); @@ -375,89 +478,15 @@ static int initializeOpenCLDevices() int baseIndx = global_devices.size(); global_devices.resize(baseIndx + devices.size()); platformInfo.deviceIDs.resize(devices.size()); - platformInfo.info.devices.resize(devices.size()); + platformInfo.devices.resize(devices.size()); for(size_t j = 0; j < devices.size(); ++j) { cl_device_id device = devices[j]; DeviceInfoImpl& deviceInfo = global_devices[baseIndx + j]; - deviceInfo.info._id = baseIndx + j; - deviceInfo.platform_id = platform; - deviceInfo.device_id = device; - - deviceInfo.info.platform = &platformInfo.info; - platformInfo.deviceIDs[j] = deviceInfo.info._id; - - cl_device_type type = cl_device_type(-1); - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_TYPE, type)); - deviceInfo.info.deviceType = DeviceType(type); - - openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_PROFILE, deviceInfo.info.deviceProfile)); - openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_VERSION, deviceInfo.info.deviceVersion)); - openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_NAME, deviceInfo.info.deviceName)); - openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_VENDOR, deviceInfo.info.deviceVendor)); - cl_uint vendorID = 0; - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_VENDOR_ID, vendorID)); - deviceInfo.info.deviceVendorId = vendorID; - openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DRIVER_VERSION, deviceInfo.info.deviceDriverVersion)); - openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_EXTENSIONS, deviceInfo.info.deviceExtensions)); - - parseOpenCLVersion(deviceInfo.info.deviceVersion, - deviceInfo.info.deviceVersionMajor, deviceInfo.info.deviceVersionMinor); - - size_t maxWorkGroupSize = 0; - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_WORK_GROUP_SIZE, maxWorkGroupSize)); - deviceInfo.info.maxWorkGroupSize = maxWorkGroupSize; - - cl_uint maxDimensions = 0; - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, maxDimensions)); - std::vector maxWorkItemSizes(maxDimensions); - openCLSafeCall(clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(size_t) * maxDimensions, - (void *)&maxWorkItemSizes[0], 0)); - deviceInfo.info.maxWorkItemSizes = maxWorkItemSizes; - - cl_uint maxComputeUnits = 0; - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_COMPUTE_UNITS, maxComputeUnits)); - deviceInfo.info.maxComputeUnits = maxComputeUnits; - - cl_ulong localMemorySize = 0; - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_LOCAL_MEM_SIZE, localMemorySize)); - deviceInfo.info.localMemorySize = (size_t)localMemorySize; - - cl_ulong maxMemAllocSize = 0; - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_MAX_MEM_ALLOC_SIZE, maxMemAllocSize)); - deviceInfo.info.maxMemAllocSize = (size_t)maxMemAllocSize; - - cl_bool unifiedMemory = false; - openCLSafeCall(getScalarInfo(clGetDeviceInfo, device, CL_DEVICE_HOST_UNIFIED_MEMORY, unifiedMemory)); - deviceInfo.info.isUnifiedMemory = unifiedMemory != 0; - - //initialize extra options for compilation. Currently only fp64 is included. - //Assume 4KB is enough to store all possible extensions. - openCLSafeCall(getStringInfo(clGetDeviceInfo, device, CL_DEVICE_EXTENSIONS, deviceInfo.info.deviceExtensions)); - - size_t fp64_khr = deviceInfo.info.deviceExtensions.find("cl_khr_fp64"); - if(fp64_khr != std::string::npos) - { - deviceInfo.info.compilationExtraOptions += "-D DOUBLE_SUPPORT"; - deviceInfo.info.haveDoubleSupport = true; - } - else - { - deviceInfo.info.haveDoubleSupport = false; - } - - size_t intel_platform = platformInfo.info.platformVendor.find("Intel"); - if(intel_platform != std::string::npos) - { - deviceInfo.info.compilationExtraOptions += " -D INTEL_DEVICE"; - deviceInfo.info.isIntelDevice = true; - } - else - { - deviceInfo.info.isIntelDevice = false; - } + platformInfo.deviceIDs[j] = baseIndx + j; + deviceInfo.init(baseIndx + j, platformInfo, device); } } } @@ -468,7 +497,7 @@ static int initializeOpenCLDevices() for(size_t j = 0; j < platformInfo.deviceIDs.size(); ++j) { DeviceInfoImpl& deviceInfo = global_devices[platformInfo.deviceIDs[j]]; - platformInfo.info.devices[j] = &deviceInfo.info; + platformInfo.devices[j] = &deviceInfo; } } @@ -487,6 +516,8 @@ DeviceInfo::DeviceInfo() // nothing } +DeviceInfo::~DeviceInfo() { } + PlatformInfo::PlatformInfo() : _id(-1), platformVersionMajor(0), platformVersionMinor(0) @@ -494,40 +525,135 @@ PlatformInfo::PlatformInfo() // nothing } +PlatformInfo::~PlatformInfo() { } + +class ContextImpl; + +struct CommandQueue +{ + ContextImpl* context_; + cl_command_queue clQueue_; + + CommandQueue() : context_(NULL), clQueue_(NULL) { } + ~CommandQueue() { release(); } + + void create(ContextImpl* context_); + void release() + { +#ifdef WIN32 + // if process is on termination stage (ExitProcess was called and other threads were terminated) + // then disable command queue release because it may cause program hang + if (!__termination) +#endif + { + if(clQueue_) + { + openCLSafeCall(clReleaseCommandQueue(clQueue_)); // some cleanup problems are here + } + + } + clQueue_ = NULL; + context_ = NULL; + } +}; + +cv::TLSData commandQueueTLSData; + //////////////////////////////// OpenCL context //////////////////////// //This is a global singleton class used to represent a OpenCL context. class ContextImpl : public Context { public: - const cl_device_id clDeviceID; + cl_device_id clDeviceID; cl_context clContext; - cl_command_queue clCmdQueue; - const DeviceInfo& deviceInfo; + const DeviceInfoImpl& deviceInfoImpl; protected: - ContextImpl(const DeviceInfo& deviceInfo, cl_device_id clDeviceID) - : clDeviceID(clDeviceID), clContext(NULL), clCmdQueue(NULL), deviceInfo(deviceInfo) + ContextImpl(const DeviceInfoImpl& _deviceInfoImpl, cl_context context) + : clDeviceID(_deviceInfoImpl.device_id), clContext(context), deviceInfoImpl(_deviceInfoImpl) { - // nothing +#ifdef CL_VERSION_1_2 + if (supportsFeature(FEATURE_CL_VER_1_2)) + { + openCLSafeCall(clRetainDevice(clDeviceID)); + } +#endif + openCLSafeCall(clRetainContext(clContext)); + + ContextImpl* old = NULL; + { + cv::AutoLock lock(getCurrentContextMutex()); + old = currentContext; + currentContext = this; + } + if (old != NULL) + { + delete old; + } + } + ~ContextImpl() + { + CV_Assert(this != currentContext); + +#ifdef CL_VERSION_1_2 + if (supportsFeature(FEATURE_CL_VER_1_2)) + { + openCLSafeCall(clReleaseDevice(clDeviceID)); + } +#endif + if (deviceInfoImpl._id < 0) // not in the global registry, so we should cleanup it + { +#ifdef CL_VERSION_1_2 + if (supportsFeature(FEATURE_CL_VER_1_2)) + { + openCLSafeCall(clReleaseDevice(deviceInfoImpl.device_id)); + } +#endif + PlatformInfoImpl* platformImpl = (PlatformInfoImpl*)(deviceInfoImpl.platform); + delete platformImpl; + delete const_cast(&deviceInfoImpl); + } + clDeviceID = NULL; + +#ifdef WIN32 + // if process is on termination stage (ExitProcess was called and other threads were terminated) + // then disable command queue release because it may cause program hang + if (!__termination) +#endif + { + if(clContext) + { + openCLSafeCall(clReleaseContext(clContext)); + } + } + clContext = NULL; } - ~ContextImpl(); public: static void setContext(const DeviceInfo* deviceInfo); + static void initializeContext(void* pClPlatform, void* pClContext, void* pClDevice); bool supportsFeature(FEATURE_TYPE featureType) const; static void cleanupContext(void); + static ContextImpl* getContext(); private: ContextImpl(const ContextImpl&); // disabled ContextImpl& operator=(const ContextImpl&); // disabled + + static ContextImpl* currentContext; }; -static ContextImpl* currentContext = NULL; +ContextImpl* ContextImpl::currentContext = NULL; static bool __deviceSelected = false; Context* Context::getContext() +{ + return ContextImpl::getContext(); +} + +ContextImpl* ContextImpl::getContext() { if (currentContext == NULL) { @@ -571,7 +697,7 @@ bool Context::supportsFeature(FEATURE_TYPE featureType) const const DeviceInfo& Context::getDeviceInfo() const { - return ((ContextImpl*)this)->deviceInfo; + return ((ContextImpl*)this)->deviceInfoImpl; } const void* Context::getOpenCLContextPtr() const @@ -581,7 +707,13 @@ const void* Context::getOpenCLContextPtr() const const void* Context::getOpenCLCommandQueuePtr() const { - return &(((ContextImpl*)this)->clCmdQueue); + ContextImpl* pThis = (ContextImpl*)this; + CommandQueue* commandQueue = commandQueueTLSData.get(); + if (commandQueue->context_ != pThis) + { + commandQueue->create(pThis); + } + return &commandQueue->clQueue_; } const void* Context::getOpenCLDeviceIDPtr() const @@ -595,44 +727,18 @@ bool ContextImpl::supportsFeature(FEATURE_TYPE featureType) const switch (featureType) { case FEATURE_CL_INTEL_DEVICE: - return deviceInfo.isIntelDevice; + return deviceInfoImpl.isIntelDevice; case FEATURE_CL_DOUBLE: - return deviceInfo.haveDoubleSupport; + return deviceInfoImpl.haveDoubleSupport; case FEATURE_CL_UNIFIED_MEM: - return deviceInfo.isUnifiedMemory; + return deviceInfoImpl.isUnifiedMemory; case FEATURE_CL_VER_1_2: - return deviceInfo.deviceVersionMajor > 1 || (deviceInfo.deviceVersionMajor == 1 && deviceInfo.deviceVersionMinor >= 2); + return deviceInfoImpl.deviceVersionMajor > 1 || (deviceInfoImpl.deviceVersionMajor == 1 && deviceInfoImpl.deviceVersionMinor >= 2); } CV_Error(CV_StsBadArg, "Invalid feature type"); return false; } -#if defined(WIN32) -static bool __termination = false; -#endif - -ContextImpl::~ContextImpl() -{ -#ifdef WIN32 - // if process is on termination stage (ExitProcess was called and other threads were terminated) - // then disable command queue release because it may cause program hang - if (!__termination) -#endif - { - if(clCmdQueue) - { - openCLSafeCall(clReleaseCommandQueue(clCmdQueue)); // some cleanup problems are here - } - - if(clContext) - { - openCLSafeCall(clReleaseContext(clContext)); - } - } - clCmdQueue = NULL; - clContext = NULL; -} - void fft_teardown(); void clBlasTeardown(); @@ -641,53 +747,69 @@ void ContextImpl::cleanupContext(void) fft_teardown(); clBlasTeardown(); - cv::AutoLock lock(__module.currentContextMutex); + cv::AutoLock lock(getCurrentContextMutex()); if (currentContext) - delete currentContext; - currentContext = NULL; + { + ContextImpl* ctx = currentContext; + currentContext = NULL; + delete ctx; + } } void ContextImpl::setContext(const DeviceInfo* deviceInfo) { - CV_Assert(deviceInfo->_id >= 0 && deviceInfo->_id < (int)global_devices.size()); + CV_Assert(deviceInfo->_id >= 0); // we can't specify custom devices + CV_Assert(deviceInfo->_id < (int)global_devices.size()); { - cv::AutoLock lock(__module.currentContextMutex); + cv::AutoLock lock(getCurrentContextMutex()); if (currentContext) { - if (currentContext->deviceInfo._id == deviceInfo->_id) + if (currentContext->deviceInfoImpl._id == deviceInfo->_id) return; } } DeviceInfoImpl& infoImpl = global_devices[deviceInfo->_id]; - CV_Assert(deviceInfo == &infoImpl.info); + CV_Assert(deviceInfo == &infoImpl); cl_int status = 0; cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)(infoImpl.platform_id), 0 }; cl_context clContext = clCreateContext(cps, 1, &infoImpl.device_id, NULL, NULL, &status); openCLVerifyCall(status); -#ifdef PRINT_KERNEL_RUN_TIME - cl_command_queue clCmdQueue = clCreateCommandQueue(clContext, infoImpl.device_id, CL_QUEUE_PROFILING_ENABLE, &status); -#else /*PRINT_KERNEL_RUN_TIME*/ - cl_command_queue clCmdQueue = clCreateCommandQueue(clContext, infoImpl.device_id, 0, &status); -#endif /*PRINT_KERNEL_RUN_TIME*/ + + ContextImpl* ctx = new ContextImpl(infoImpl, clContext); + clReleaseContext(clContext); + (void)ctx; +} + +void ContextImpl::initializeContext(void* pClPlatform, void* pClContext, void* pClDevice) +{ + CV_Assert(pClPlatform != NULL); + CV_Assert(pClContext != NULL); + CV_Assert(pClDevice != NULL); + cl_platform_id platform = *(cl_platform_id*)pClPlatform; + cl_context context = *(cl_context*)pClContext; + cl_device_id device = *(cl_device_id*)pClDevice; + + PlatformInfoImpl* platformInfoImpl = new PlatformInfoImpl(); + platformInfoImpl->init(-1, platform); + DeviceInfoImpl* deviceInfoImpl = new DeviceInfoImpl(); + deviceInfoImpl->init(-1, *platformInfoImpl, device); + + ContextImpl* ctx = new ContextImpl(*deviceInfoImpl, context); + (void)ctx; +} + +void CommandQueue::create(ContextImpl* context) +{ + release(); + cl_int status = 0; + // TODO add CL_QUEUE_PROFILING_ENABLE + cl_command_queue clCmdQueue = clCreateCommandQueue(context->clContext, context->clDeviceID, 0, &status); openCLVerifyCall(status); - - ContextImpl* ctx = new ContextImpl(infoImpl.info, infoImpl.device_id); - ctx->clCmdQueue = clCmdQueue; - ctx->clContext = clContext; - - ContextImpl* old = NULL; - { - cv::AutoLock lock(__module.currentContextMutex); - old = currentContext; - currentContext = ctx; - } - if (old != NULL) - { - delete old; - } + context_ = context; + clQueue_ = clCmdQueue; } int getOpenCLPlatforms(PlatformsInfo& platforms) @@ -700,7 +822,7 @@ int getOpenCLPlatforms(PlatformsInfo& platforms) for (size_t id = 0; id < global_platforms.size(); ++id) { PlatformInfoImpl& impl = global_platforms[id]; - platforms.push_back(&impl.info); + platforms.push_back(&impl); } return platforms.size(); @@ -730,9 +852,9 @@ int getOpenCLDevices(std::vector &devices, int deviceType, co for (size_t id = 0; id < global_devices.size(); ++id) { DeviceInfoImpl& deviceInfo = global_devices[id]; - if (((int)deviceInfo.info.deviceType & deviceType) != 0) + if (((int)deviceInfo.deviceType & deviceType) != 0) { - devices.push_back(&deviceInfo.info); + devices.push_back(&deviceInfo); } } } @@ -765,6 +887,20 @@ void setDevice(const DeviceInfo* info) } } +void initializeContext(void* pClPlatform, void* pClContext, void* pClDevice) +{ + try + { + ContextImpl::initializeContext(pClPlatform, pClContext, pClDevice); + __deviceSelected = true; + } + catch (...) + { + __deviceSelected = true; + throw; + } +} + bool supportsFeature(FEATURE_TYPE featureType) { return Context::getContext()->supportsFeature(featureType); diff --git a/modules/ocl/test/test_api.cpp b/modules/ocl/test/test_api.cpp index fe8bc49c0..6ca40270d 100644 --- a/modules/ocl/test/test_api.cpp +++ b/modules/ocl/test/test_api.cpp @@ -40,7 +40,7 @@ //M*/ #include "test_precomp.hpp" -#include "opencv2/core/opencl/runtime/opencl_core.hpp" // for OpenCL types: cl_mem +#include "opencv2/core/opencl/runtime/opencl_core.hpp" // for OpenCL types & functions #include "opencv2/core/ocl.hpp" TEST(TestAPI, openCLExecuteKernelInterop) @@ -127,3 +127,87 @@ TEST(OCL_TestTAPI, performance) t = (double)cv::getTickCount() - t; printf("cpu exec time = %gms per iter\n", t*1000./niters/cv::getTickFrequency()); } + +// This test must be DISABLED by default! +// (We can't restore original context for other tests) +TEST(TestAPI, DISABLED_InitializationFromHandles) +{ +#define MAX_PLATFORMS 16 + cl_platform_id platforms[MAX_PLATFORMS] = { NULL }; + cl_uint numPlatforms = 0; + cl_int status = ::clGetPlatformIDs(MAX_PLATFORMS, &platforms[0], &numPlatforms); + ASSERT_EQ(CL_SUCCESS, status) << "clGetPlatformIDs"; + ASSERT_NE(0, (int)numPlatforms); + + int selectedPlatform = 0; + cl_platform_id platform = platforms[selectedPlatform]; + + ASSERT_NE((void*)NULL, platform); + + cl_device_id device = NULL; + status = ::clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 1, &device, NULL); + ASSERT_EQ(CL_SUCCESS, status) << "clGetDeviceIDs"; + ASSERT_NE((void*)NULL, device); + + cl_context_properties cps[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)(platform), 0 }; + cl_context context = ::clCreateContext(cps, 1, &device, NULL, NULL, &status); + ASSERT_EQ(CL_SUCCESS, status) << "clCreateContext"; + ASSERT_NE((void*)NULL, context); + + ASSERT_NO_THROW(cv::ocl::initializeContext(&platform, &context, &device)); + + status = ::clReleaseContext(context); + ASSERT_EQ(CL_SUCCESS, status) << "clReleaseContext"; + +#ifdef CL_VERSION_1_2 +#if 1 + { + cv::ocl::Context* ctx = cv::ocl::Context::getContext(); + ASSERT_NE((void*)NULL, ctx); + if (ctx->supportsFeature(cv::ocl::FEATURE_CL_VER_1_2)) // device supports OpenCL 1.2+ + { + status = ::clReleaseDevice(device); + ASSERT_EQ(CL_SUCCESS, status) << "clReleaseDevice"; + } + } +#else // code below doesn't work on Linux (SEGFAULTs on 1.1- devices are not handled via exceptions) + try + { + status = ::clReleaseDevice(device); // NOTE This works only with !DEVICES! that supports OpenCL 1.2 + (void)status; // no check + } + catch (...) + { + // nothing, there is no problem + } +#endif +#endif + + // print the name of current device + cv::ocl::Context* ctx = cv::ocl::Context::getContext(); + ASSERT_NE((void*)NULL, ctx); + const cv::ocl::DeviceInfo& deviceInfo = ctx->getDeviceInfo(); + std::cout << "Device name: " << deviceInfo.deviceName << std::endl; + std::cout << "Platform name: " << deviceInfo.platform->platformName << std::endl; + + ASSERT_EQ(context, *(cl_context*)ctx->getOpenCLContextPtr()); + ASSERT_EQ(device, *(cl_device_id*)ctx->getOpenCLDeviceIDPtr()); + + // do some calculations and check results + cv::RNG rng; + Size sz(100, 100); + cv::Mat srcMat = cvtest::randomMat(rng, sz, CV_32FC4, -10, 10, false); + cv::Mat dstMat; + + cv::ocl::oclMat srcGpuMat(srcMat); + cv::ocl::oclMat dstGpuMat; + + cv::Scalar v = cv::Scalar::all(1); + cv::add(srcMat, v, dstMat); + cv::ocl::add(srcGpuMat, v, dstGpuMat); + + cv::Mat dstGpuMatMap; + dstGpuMat.download(dstGpuMatMap); + + EXPECT_LE(checkNorm(dstMat, dstGpuMatMap), 1e-3); +} diff --git a/samples/cpp/stereo_match.cpp b/samples/cpp/stereo_match.cpp index 6f0b9f38b..2f6a8b96e 100644 --- a/samples/cpp/stereo_match.cpp +++ b/samples/cpp/stereo_match.cpp @@ -46,7 +46,7 @@ int main(int argc, char** argv) const char* algorithm_opt = "--algorithm="; const char* maxdisp_opt = "--max-disparity="; const char* blocksize_opt = "--blocksize="; - const char* nodisplay_opt = "--no-display="; + const char* nodisplay_opt = "--no-display"; const char* scale_opt = "--scale="; if(argc < 3) diff --git a/samples/java/clojure/simple-sample/.gitignore b/samples/java/clojure/simple-sample/.gitignore new file mode 100644 index 000000000..e04714ba8 --- /dev/null +++ b/samples/java/clojure/simple-sample/.gitignore @@ -0,0 +1,9 @@ +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port diff --git a/samples/java/clojure/simple-sample/project.clj b/samples/java/clojure/simple-sample/project.clj new file mode 100644 index 000000000..2702b8106 --- /dev/null +++ b/samples/java/clojure/simple-sample/project.clj @@ -0,0 +1,14 @@ +(defproject simple-sample "0.1.0-SNAPSHOT" + :pom-addition [:developers [:developer {:id "magomimmo"} + [:name "Mimmo Cosenza"] + [:url "https://github.com/magomimmoo"]]] + + :description "A simple project to start REPLing with OpenCV" + :url "http://example.com/FIXME" + :license {:name "BSD 3-Clause License" + :url "http://opensource.org/licenses/BSD-3-Clause"} + :dependencies [[org.clojure/clojure "1.5.1"] + [opencv/opencv "2.4.7"] + [opencv/opencv-native "2.4.7"]] + :main simple-sample.core + :injections [(clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)]) diff --git a/samples/java/clojure/simple-sample/resources/images/lena.png b/samples/java/clojure/simple-sample/resources/images/lena.png new file mode 100644 index 000000000..68342fae5 Binary files /dev/null and b/samples/java/clojure/simple-sample/resources/images/lena.png differ diff --git a/samples/java/clojure/simple-sample/src/simple_sample/core.clj b/samples/java/clojure/simple-sample/src/simple_sample/core.clj new file mode 100644 index 000000000..86a1b3113 --- /dev/null +++ b/samples/java/clojure/simple-sample/src/simple_sample/core.clj @@ -0,0 +1,16 @@ +;;; to run this code from the terminal: "$ lein run". It will save a +;;; blurred image version of resources/images/lena.png as +;;; resources/images/blurred.png + +(ns simple-sample.core + (:import [org.opencv.core Point Rect Mat CvType Size Scalar] + org.opencv.highgui.Highgui + org.opencv.imgproc.Imgproc)) + +(defn -main [& args] + (let [lena (Highgui/imread "resources/images/lena.png") + blurred (Mat. 512 512 CvType/CV_8UC3)] + (print "Blurring...") + (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3) + (Highgui/imwrite "resources/images/blurred.png" blurred) + (println "done!"))) diff --git a/samples/java/clojure/simple-sample/test/simple_sample/core_test.clj b/samples/java/clojure/simple-sample/test/simple_sample/core_test.clj new file mode 100644 index 000000000..d84585e01 --- /dev/null +++ b/samples/java/clojure/simple-sample/test/simple_sample/core_test.clj @@ -0,0 +1,7 @@ +(ns simple-sample.core-test + (:require [clojure.test :refer :all] + [simple-sample.core :refer :all])) + +(deftest a-test + (testing "FIXME, I fail." + (is (= 0 1))))