123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- /*
- * The example of interoperability between SYCL/OpenCL and OpenCV.
- * - SYCL: https://www.khronos.org/sycl/
- * - SYCL runtime parameters: https://github.com/intel/llvm/blob/sycl/sycl/doc/EnvironmentVariables.md
- */
- #include <CL/sycl.hpp>
- #include <opencv2/core.hpp>
- #include <opencv2/highgui.hpp>
- #include <opencv2/videoio.hpp>
- #include <opencv2/imgproc.hpp>
- #include <opencv2/core/ocl.hpp>
- class sycl_inverse_kernel; // can be omitted - modern SYCL versions doesn't require this
- using namespace cv;
- class App
- {
- public:
- App(const CommandLineParser& cmd);
- ~App();
- void initVideoSource();
- void initSYCL();
- void process_frame(cv::Mat& frame);
- /// to check result with CPU-only reference code
- Mat process_frame_reference(const cv::Mat& frame);
- int run();
- bool isRunning() { return m_running; }
- bool doProcess() { return m_process; }
- void setRunning(bool running) { m_running = running; }
- void setDoProcess(bool process) { m_process = process; }
- protected:
- void handleKey(char key);
- private:
- bool m_running;
- bool m_process;
- bool m_show_ui;
- int64 m_t0;
- int64 m_t1;
- float m_time;
- float m_frequency;
- std::string m_file_name;
- int m_camera_id;
- cv::VideoCapture m_cap;
- cv::Mat m_frame;
- cl::sycl::queue sycl_queue;
- };
- App::App(const CommandLineParser& cmd)
- {
- m_camera_id = cmd.get<int>("camera");
- m_file_name = cmd.get<std::string>("video");
- m_running = false;
- m_process = false;
- } // ctor
- App::~App()
- {
- // nothing
- }
- void App::initSYCL()
- {
- using namespace cl::sycl;
- // Configuration details: https://github.com/intel/llvm/blob/sycl/sycl/doc/EnvironmentVariables.md
- cl::sycl::default_selector selector;
- sycl_queue = cl::sycl::queue(selector, [](cl::sycl::exception_list l)
- {
- // exception_handler
- for (auto ep : l)
- {
- try
- {
- std::rethrow_exception(ep);
- }
- catch (const cl::sycl::exception& e)
- {
- std::cerr << "SYCL exception: " << e.what() << std::endl;
- }
- }
- });
- auto device = sycl_queue.get_device();
- auto platform = device.get_platform();
- std::cout << "SYCL device: " << device.get_info<info::device::name>()
- << " @ " << device.get_info<info::device::driver_version>()
- << " (platform: " << platform.get_info<info::platform::name>() << ")" << std::endl;
- if (device.is_host())
- {
- std::cerr << "SYCL can't select OpenCL device. Host is used for computations, interoperability is not available" << std::endl;
- }
- else
- {
- // bind OpenCL context/device/queue from SYCL to OpenCV
- try
- {
- auto ctx = cv::ocl::OpenCLExecutionContext::create(
- platform.get_info<info::platform::name>(),
- platform.get(),
- sycl_queue.get_context().get(),
- device.get()
- );
- ctx.bind();
- }
- catch (const cv::Exception& e)
- {
- std::cerr << "OpenCV: Can't bind SYCL OpenCL context/device/queue: " << e.what() << std::endl;
- }
- std::cout << "OpenCV uses OpenCL: " << (cv::ocl::useOpenCL() ? "True" : "False") << std::endl;
- }
- } // initSYCL()
- void App::initVideoSource()
- {
- if (!m_file_name.empty() && m_camera_id == -1)
- {
- m_cap.open(samples::findFileOrKeep(m_file_name));
- if (!m_cap.isOpened())
- throw std::runtime_error(std::string("can't open video stream: ") + m_file_name);
- }
- else if (m_camera_id != -1)
- {
- m_cap.open(m_camera_id);
- if (!m_cap.isOpened())
- throw std::runtime_error(std::string("can't open camera: ") + std::to_string(m_camera_id));
- }
- else
- throw std::runtime_error(std::string("specify video source"));
- } // initVideoSource()
- void App::process_frame(cv::Mat& frame)
- {
- using namespace cl::sycl;
- // cv::Mat => cl::sycl::buffer
- {
- CV_Assert(frame.isContinuous());
- CV_CheckTypeEQ(frame.type(), CV_8UC1, "");
- buffer<uint8_t, 2> frame_buffer(frame.data, range<2>(frame.rows, frame.cols));
- // done automatically: frame_buffer.set_write_back(true);
- sycl_queue.submit([&](handler& cgh) {
- auto pixels = frame_buffer.get_access<access::mode::read_write>(cgh);
- cgh.parallel_for<class sycl_inverse_kernel>(range<2>(frame.rows, frame.cols), [=](item<2> item) {
- uint8_t v = pixels[item];
- pixels[item] = ~v;
- });
- });
- sycl_queue.wait_and_throw();
- }
- // No way to extract cl_mem from cl::sycl::buffer (ref: 3.6.11 "Interfacing with OpenCL" of SYCL 1.2.1)
- // We just reusing OpenCL context/device/queue from SYCL here (see initSYCL() bind part) and call UMat processing
- {
- UMat blurResult;
- {
- UMat umat_buffer = frame.getUMat(ACCESS_RW);
- cv::blur(umat_buffer, blurResult, Size(3, 3)); // UMat doesn't support inplace
- }
- Mat result;
- blurResult.copyTo(result);
- swap(result, frame);
- }
- }
- Mat App::process_frame_reference(const cv::Mat& frame)
- {
- Mat result;
- cv::bitwise_not(frame, result);
- Mat blurResult;
- cv::blur(result, blurResult, Size(3, 3)); // avoid inplace
- blurResult.copyTo(result);
- return result;
- }
- int App::run()
- {
- std::cout << "Initializing..." << std::endl;
- initSYCL();
- initVideoSource();
- std::cout << "Press ESC to exit" << std::endl;
- std::cout << " 'p' to toggle ON/OFF processing" << std::endl;
- m_running = true;
- m_process = true;
- m_show_ui = true;
- int processedFrames = 0;
- cv::TickMeter timer;
- // Iterate over all frames
- while (isRunning() && m_cap.read(m_frame))
- {
- Mat m_frameGray;
- cvtColor(m_frame, m_frameGray, COLOR_BGR2GRAY);
- bool checkWithReference = (processedFrames == 0);
- Mat reference_result;
- if (checkWithReference)
- {
- reference_result = process_frame_reference(m_frameGray);
- }
- timer.reset();
- timer.start();
- if (m_process)
- {
- process_frame(m_frameGray);
- }
- timer.stop();
- if (checkWithReference)
- {
- double diffInf = cv::norm(reference_result, m_frameGray, NORM_INF);
- if (diffInf > 0)
- {
- std::cerr << "Result is not accurate. diffInf=" << diffInf << std::endl;
- imwrite("reference.png", reference_result);
- imwrite("actual.png", m_frameGray);
- }
- }
- Mat img_to_show = m_frameGray;
- std::ostringstream msg;
- msg << "Frame " << processedFrames << " (" << m_frame.size
- << ") Time: " << cv::format("%.2f", timer.getTimeMilli()) << " msec"
- << " (process: " << (m_process ? "True" : "False") << ")";
- std::cout << msg.str() << std::endl;
- putText(img_to_show, msg.str(), Point(5, 150), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2);
- if (m_show_ui)
- {
- try
- {
- imshow("sycl_interop", img_to_show);
- int key = waitKey(1);
- switch (key)
- {
- case 27: // ESC
- m_running = false;
- break;
- case 'p': // fallthru
- case 'P':
- m_process = !m_process;
- break;
- default:
- break;
- }
- }
- catch (const std::exception& e)
- {
- std::cerr << "ERROR(OpenCV UI): " << e.what() << std::endl;
- if (processedFrames > 0)
- throw;
- m_show_ui = false; // UI is not available
- }
- }
- processedFrames++;
- if (!m_show_ui)
- {
- if (processedFrames > 100)
- m_running = false;
- }
- }
- return 0;
- }
- int main(int argc, char** argv)
- {
- const char* keys =
- "{ help h ? | | print help message }"
- "{ camera c | -1 | use camera as input }"
- "{ video v | | use video as input }";
- CommandLineParser cmd(argc, argv, keys);
- if (cmd.has("help"))
- {
- cmd.printMessage();
- return EXIT_SUCCESS;
- }
- try
- {
- App app(cmd);
- if (!cmd.check())
- {
- cmd.printErrors();
- return 1;
- }
- app.run();
- }
- catch (const cv::Exception& e)
- {
- std::cout << "FATAL: OpenCV error: " << e.what() << std::endl;
- return 1;
- }
- catch (const std::exception& e)
- {
- std::cout << "FATAL: C++ error: " << e.what() << std::endl;
- return 1;
- }
- catch (...)
- {
- std::cout << "FATAL: unknown C++ exception" << std::endl;
- return 1;
- }
- return EXIT_SUCCESS;
- } // main()
|