From 8c6782300a9b60841d6ecb2e4e8944546e3c38a7 Mon Sep 17 00:00:00 2001 From: Artem Myagkov Date: Tue, 23 Aug 2011 11:19:58 +0000 Subject: [PATCH] modified highgui and ts modules and cmakelists for ios cross-compiling, added ios toolchains, added ios build tutorial --- CMakeLists.txt | 26 +- doc/tutorials/definitions/tocDefinitions.rst | 1 + .../introduction/ios_install/ios_install.rst | 58 + .../images/ios4_logo.jpg | Bin 0 -> 6970 bytes .../table_of_content_introduction.rst | 24 +- ios/cmake/Modules/Platform/iOS.cmake | 161 +++ .../Toolchains/Toolchain-iPhoneOS_Xcode.cmake | 24 + .../Toolchain-iPhoneSimulator_Xcode.cmake | 24 + ios/configure-device_xcode.sh | 1 + ios/configure-simulator_xcode.sh | 1 + ios/readme.txt | 15 + modules/CMakeLists.txt | 5 +- modules/highgui/CMakeLists.txt | 30 +- .../highgui/include/opencv2/highgui/highgui_c.h | 13 +- modules/highgui/src/cap.cpp | 21 +- modules/highgui/src/cap_avfoundation.mm | 1325 ++++++++++++++++++++ modules/highgui/src/grfmt_imageio.cpp | 18 +- modules/highgui/src/grfmt_imageio.hpp | 11 + modules/highgui/src/window_cocoa.mm | 2 +- modules/ts/include/opencv2/ts/ts_gtest.h | 4 + modules/ts/src/ts_gtest.cpp | 4 +- 21 files changed, 1743 insertions(+), 25 deletions(-) create mode 100644 doc/tutorials/introduction/ios_install/ios_install.rst create mode 100644 doc/tutorials/introduction/table_of_content_introduction/images/ios4_logo.jpg create mode 100644 ios/cmake/Modules/Platform/iOS.cmake create mode 100644 ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake create mode 100644 ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake create mode 100755 ios/configure-device_xcode.sh create mode 100755 ios/configure-simulator_xcode.sh create mode 100644 ios/readme.txt create mode 100644 modules/highgui/src/cap_avfoundation.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 0709624..3b09582 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ endif(NOT CMAKE_TOOLCHAIN_FILE) # -------------------------------------------------------------- # Top level OpenCV project # -------------------------------------------------------------- -cmake_minimum_required(VERSION 2.4) +cmake_minimum_required(VERSION 2.6) project(OpenCV) set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configs" FORCE) @@ -72,8 +72,11 @@ endif() # Build static or dynamic libs? # Default: dynamic libraries # ---------------------------------------------------------------------------- -set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)") - +if(NOT IOS) + set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)") +else() + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)") +endif() # ---------------------------------------------------------------------------- # Include debug info into debug libs? # Default: yes @@ -327,7 +330,11 @@ endif() # Build tests: # =================================================== -set(BUILD_TESTS ON CACHE BOOL "Build tests") +if(NOT IOS) + set(BUILD_TESTS ON CACHE BOOL "Build tests") +else() + set(BUILD_TESTS OFF CACHE BOOL "Build tests") +endif() # Build 3rdparty libraries under unix # =================================================== @@ -337,7 +344,10 @@ else() set(OPENCV_BUILD_3RDPARTY_LIBS FALSE CACHE BOOL "Build 3rd party libraries") endif() +if(NOT IOS) include(OpenCVPCHSupport.cmake REQUIRED) +endif() + include(OpenCVModule.cmake REQUIRED) if(ANDROID) include(OpenCVAndroidProject.cmake REQUIRED) @@ -398,11 +408,13 @@ endif() # allow fine grained control over which libraries not to link, even if # they are available on the system # ==================================================================== +if(NOT IOS) set(WITH_PNG ON CACHE BOOL "Include PNG support") set(WITH_JPEG ON CACHE BOOL "Include JPEG support") set(WITH_JASPER ON CACHE BOOL "Include JPEG2K support") set(WITH_TIFF ON CACHE BOOL "Include TIFF support") set(WITH_OPENEXR ON CACHE BOOL "Include ILM support via OpenEXR") +endif() if(UNIX) set(WITH_FFMPEG ON CACHE BOOL "Include FFMPEG support") @@ -1678,7 +1690,11 @@ if(UNIX AND NOT APPLE) endif() endif() elseif(APPLE) - status(" Video I/O:" WITH_QUICKTIME THEN QuickTime ELSE QTKit) + if(NOT IOS) + status(" Video I/O:" WITH_QUICKTIME THEN QuickTime ELSE QTKit) + else() + status(" Video I/O: AVFoundation") + endif() elseif(WIN32) status(" Video I/O:" HAVE_VIDEOINPUT THEN DirectShow ELSE NO) endif() diff --git a/doc/tutorials/definitions/tocDefinitions.rst b/doc/tutorials/definitions/tocDefinitions.rst index 2621c58..f850bb8 100644 --- a/doc/tutorials/definitions/tocDefinitions.rst +++ b/doc/tutorials/definitions/tocDefinitions.rst @@ -3,6 +3,7 @@ .. |Author_AndreyK| unicode:: Andrey U+0020 Kamaev .. |Author_LeonidBLB| unicode:: Leonid U+0020 Beynenson .. |Author_VictorE| unicode:: Victor U+0020 Eruhimov +.. |Author_ArtemM| unicode:: Artem U+0020 Myagkov .. |Author_FernandoI| unicode:: Fernando U+0020 Iglesias U+0020 Garc U+00ED a diff --git a/doc/tutorials/introduction/ios_install/ios_install.rst b/doc/tutorials/introduction/ios_install/ios_install.rst new file mode 100644 index 0000000..4b4f551 --- /dev/null +++ b/doc/tutorials/introduction/ios_install/ios_install.rst @@ -0,0 +1,58 @@ +.. _iOS-Installation: + +Installation in iOS +*********************** + +Required packages +================== + + * GCC 4.x or later + * CMake 2.6 or higher + * Xcode 4.0 or higher + +Getting the cutting-edge OpenCV from SourceForge SVN repository +----------------------------------------------------------------- + +Launch SVN client and checkout either + +a. the current OpenCV snapshot from here: https://code.ros.org/svn/opencv/trunk + +#. or the latest tested OpenCV snapshot from here: http://code.ros.org/svn/opencv/tags/latest_tested_snapshot + +In MacOS it can be done using the following command in Terminal: + +.. code-block:: bash + + cd ~/ + svn co https://code.ros.org/svn/opencv/trunk + + +Building OpenCV from source using CMake, using the command line +================================================================ + +#. Create a temporary directory, which we denote as , where you want to put the generated Makefiles, project files as well the object filees and output binaries + +#. Enter the and type + + .. code-block:: bash + + cmake [] + + For example + + .. code-block:: bash + + cd ~/opencv + cd .. + mkdir release + cd release + cmake -GXcode -DCMAKE_TOOLCHAIN_FILE=../opencv/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake -DCMAKE_INSTALL_PREFIX=../OpenCV_iPhoneOS -DCMAKE_BUILD_TYPE=RELEASE ../opencv + + +#. Enter the created temporary directory () and proceed with: + + .. code-block:: bash + + xcodebuild -sdk iphoneos -configuration Release -target ALL_BUILD + xcodebuild -sdk iphoneos -configuration Release -target install install + diff --git a/doc/tutorials/introduction/table_of_content_introduction/images/ios4_logo.jpg b/doc/tutorials/introduction/table_of_content_introduction/images/ios4_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..28a257ffdaf985b849d290cda0b59289299bc949 GIT binary patch literal 6970 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 Q_@|bj{6DqCzgLU@1w@JnF8}}l literal 0 HcmV?d00001 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 04351ee..a77612e 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 @@ -139,10 +139,27 @@ Here you can read tutorials about how to set up your computer to work with the O :height: 90pt :width: 90pt -* **From where to start?** +* **iOS** - .. tabularcolumns:: m{100pt} m{300pt} - .. cssclass:: toctableopencv +.. tabularcolumns:: m{100pt} m{300pt} +.. cssclass:: toctableopencv + +=========== ====================================================== +|Install_2| **Title:** :ref:`iOS-Installation` + + *Compatibility:* > OpenCV 2.3.1 + + *Author:* |Author_ArtemM| + + We will learn how to setup OpenCV for using it in iOS! + +=========== ====================================================== + +.. |Install_2| image:: images/ios4_logo.jpg + :width: 90pt + +.. tabularcolumns:: m{100pt} m{300pt} +.. cssclass:: toctableopencv ============= ====================================================== |Beginners_1| **Title:** :ref:`Display_Image` @@ -209,6 +226,7 @@ Here you can read tutorials about how to set up your computer to work with the O ../windows_visual_studio_Opencv/windows_visual_studio_Opencv ../android_binary_package/android_binary_package ../android_binary_package/android_binary_package_using_with_NDK + ../ios_install/ios_install ../display_image/display_image ../load_save_image/load_save_image ../how_to_write_a_tutorial/how_to_write_a_tutorial \ No newline at end of file diff --git a/ios/cmake/Modules/Platform/iOS.cmake b/ios/cmake/Modules/Platform/iOS.cmake new file mode 100644 index 0000000..c657b80 --- /dev/null +++ b/ios/cmake/Modules/Platform/iOS.cmake @@ -0,0 +1,161 @@ +# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake +# files which are included with CMake 2.8.4 +# It has been altered for iOS development +set (UNIX 1) +set (APPLE 1) +set (IOS 1) + +# Darwin versions: +# 6.x == Mac OSX 10.2 +# 7.x == Mac OSX 10.3 +# 8.x == Mac OSX 10.4 +# 9.x == Mac OSX 10.5 +# 10.x == Mac OSX 10.6 (Snow Leopard) +string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_SYSTEM_VERSION}") +string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\2" DARWIN_MINOR_VERSION "${CMAKE_SYSTEM_VERSION}") + +# Do not use the "-Wl,-search_paths_first" flag with the OSX 10.2 compiler. +# Done this way because it is too early to do a TRY_COMPILE. +if (NOT DEFINED HAVE_FLAG_SEARCH_PATHS_FIRST) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + if ("${DARWIN_MAJOR_VERSION}" GREATER 6) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 1) + endif ("${DARWIN_MAJOR_VERSION}" GREATER 6) +endif (NOT DEFINED HAVE_FLAG_SEARCH_PATHS_FIRST) +# More desirable, but does not work: +#INCLUDE(CheckCXXCompilerFlag) +#CHECK_CXX_COMPILER_FLAG("-Wl,-search_paths_first" HAVE_FLAG_SEARCH_PATHS_FIRST) + +set (CMAKE_SHARED_LIBRARY_PREFIX "lib") +set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set (CMAKE_SHARED_MODULE_PREFIX "lib") +set (CMAKE_SHARED_MODULE_SUFFIX ".so") +set (CMAKE_MODULE_EXISTS 1) +set (CMAKE_DL_LIBS "") + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +# Hidden visibilty is required for cxx on iOS +set (CMAKE_C_FLAGS "") +set (CMAKE_CXX_FLAGS "-headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden") + +if (HAVE_FLAG_SEARCH_PATHS_FIRST) + set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") + set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") +endif (HAVE_FLAG_SEARCH_PATHS_FIRST) + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree +# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache +# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun) +# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex +if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) +endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + +# Setup iOS developer location +if (IPHONEOS) + set (_CMAKE_IOS_DEVELOPER_ROOT "/Developer/Platforms/iPhoneOS.platform/Developer") +else () + if (IPHONESIMULATOR) + set (_CMAKE_IOS_DEVELOPER_ROOT "/Developer/Platforms/iPhoneSimulator.platform/Developer") + endif () +endif () +# Find installed iOS SDKs +file (GLOB _CMAKE_IOS_SDKS "${_CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") + +# Find and use the most recent iOS sdk +if (_CMAKE_IOS_SDKS) + list (SORT _CMAKE_IOS_SDKS) + list (REVERSE _CMAKE_IOS_SDKS) + list (GET _CMAKE_IOS_SDKS 0 _CMAKE_IOS_SDK_ROOT) + + # Set the sysroot default to the most recent SDK + set (CMAKE_OSX_SYSROOT ${_CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") + + # set the architecture for iOS - this env var sets armv6,armv7 and appears to be XCode's standard. The other found is ARCHS_UNIVERSAL_IPHONE_OS but that is armv7 only + set (CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_BIT)" CACHE string "Build architecture for iOS") + + # Set the default based on this file and not the environment variable + set (CMAKE_FIND_ROOT_PATH ${_CMAKE_IOS_DEVELOPER_ROOT} ${_CMAKE_IOS_SDK_ROOT} CACHE string "iOS library search path root") + + # default to searching for frameworks first + set (CMAKE_FIND_FRAMEWORK FIRST) + + # set up the default search directories for frameworks + set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${_CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks + ${_CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${_CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks + ) +endif (_CMAKE_IOS_SDKS) + +if ("${CMAKE_BACKWARDS_COMPATIBILITY}" MATCHES "^1\\.[0-6]$") + set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -flat_namespace -undefined suppress") +endif ("${CMAKE_BACKWARDS_COMPATIBILITY}" MATCHES "^1\\.[0-6]$") + +if (NOT XCODE) + # Enable shared library versioning. This flag is not actually referenced + # but the fact that the setting exists will cause the generators to support + # soname computation. + set (CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") +endif (NOT XCODE) + +# Xcode does not support -isystem yet. +if (XCODE) + set (CMAKE_INCLUDE_SYSTEM_FLAG_C) + set (CMAKE_INCLUDE_SYSTEM_FLAG_CXX) +endif (XCODE) + +# Need to list dependent shared libraries on link line. When building +# with -isysroot (for universal binaries), the linker always looks for +# dependent libraries under the sysroot. Listing them on the link +# line works around the problem. +set (CMAKE_LINK_DEPENDENT_LIBRARY_FILES 1) + +set (CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w) +set (CMAKE_CXX_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w) + +set (CMAKE_C_CREATE_SHARED_LIBRARY + " -o -install_name ") +set (CMAKE_CXX_CREATE_SHARED_LIBRARY + " -o -install_name ") + +set (CMAKE_CXX_CREATE_SHARED_MODULE + " -o ") + +set (CMAKE_C_CREATE_SHARED_MODULE + " -o ") + +set (CMAKE_C_CREATE_MACOSX_FRAMEWORK + " -o -install_name ") +set (CMAKE_CXX_CREATE_MACOSX_FRAMEWORK + " -o -install_name ") + + +# Add the install directory of the running cmake to the search directories +# CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up +get_filename_component (_CMAKE_INSTALL_DIR "${CMAKE_ROOT}" PATH) +get_filename_component (_CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" PATH) + +# List common installation prefixes. These will be used for all search types +list (APPEND CMAKE_SYSTEM_PREFIX_PATH + # Standard + ${_CMAKE_IOS_DEVELOPER_ROOT}/usr + ${_CMAKE_IOS_SDK_ROOT}/usr + + # CMake install location + "${_CMAKE_INSTALL_DIR}" + + # Project install destination. + "${CMAKE_INSTALL_PREFIX}" +) \ No newline at end of file diff --git a/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake b/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake new file mode 100644 index 0000000..a5c9f0f --- /dev/null +++ b/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake @@ -0,0 +1,24 @@ +message (STATUS "Setting up iPhoneOS toolchain") +set (IPHONEOS TRUE) + +# Standard settings +set (CMAKE_SYSTEM_NAME iOS) +# Include extra modules for the iOS platform files +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/ios/cmake/Modules") + +# Force the compilers to gcc for iOS +include (CMakeForceCompiler) +CMAKE_FORCE_C_COMPILER (gcc gcc) +CMAKE_FORCE_CXX_COMPILER (g++ g++) + +# Skip the platform compiler checks for cross compiling +set (CMAKE_CXX_COMPILER_WORKS TRUE) +set (CMAKE_C_COMPILER_WORKS TRUE) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +message (STATUS "iPhoneOS toolchain loaded") \ No newline at end of file diff --git a/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake b/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake new file mode 100644 index 0000000..30189cc --- /dev/null +++ b/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake @@ -0,0 +1,24 @@ +message (STATUS "Setting up iPhoneOS toolchain") +set (IPHONESIMULATOR TRUE) + +# Standard settings +set (CMAKE_SYSTEM_NAME iOS) +# Include extra modules for the iOS platform files +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/ios/cmake/Modules") + +# Force the compilers to gcc for iOS +include (CMakeForceCompiler) +CMAKE_FORCE_C_COMPILER (gcc gcc) +CMAKE_FORCE_CXX_COMPILER (g++ g++) + +# Skip the platform compiler checks for cross compiling +set (CMAKE_CXX_COMPILER_WORKS TRUE) +set (CMAKE_C_COMPILER_WORKS TRUE) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +message (STATUS "iPhoneOS toolchain loaded") \ No newline at end of file diff --git a/ios/configure-device_xcode.sh b/ios/configure-device_xcode.sh new file mode 100755 index 0000000..8c28a3e --- /dev/null +++ b/ios/configure-device_xcode.sh @@ -0,0 +1 @@ +cmake -GXcode -DCMAKE_TOOLCHAIN_FILE=../opencv/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake -DCMAKE_INSTALL_PREFIX=../OpenCV_iPhoneOS ../opencv diff --git a/ios/configure-simulator_xcode.sh b/ios/configure-simulator_xcode.sh new file mode 100755 index 0000000..50e0026 --- /dev/null +++ b/ios/configure-simulator_xcode.sh @@ -0,0 +1 @@ +cmake -GXcode -DCMAKE_TOOLCHAIN_FILE=../opencv/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake -DCMAKE_INSTALL_PREFIX=../OpenCV_iPhoneSimulator ../opencv diff --git a/ios/readme.txt b/ios/readme.txt new file mode 100644 index 0000000..1441b24 --- /dev/null +++ b/ios/readme.txt @@ -0,0 +1,15 @@ +Assuming that your build directory is on the same level that opencv source, +From the build directory run + ../opencv/ios/configure-device_xcode.sh +or + ../opencv/ios/configure-simulator_xcode.sh + +Then from the same folder invoke + +xcodebuild -sdk iphoneos -configuration Release -target ALL_BUILD +xcodebuild -sdk iphoneos -configuration Release -target install install + +or + +xcodebuild -sdk iphonesimulator -configuration Release -target ALL_BUILD +xcodebuild -sdk iphonesimulator -configuration Release -target install install \ No newline at end of file diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 17d71f1..43ae437 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -33,10 +33,13 @@ if(BUILD_JAVA_SUPPORT) endif() add_subdirectory(video) + +if(NOT IOS) add_subdirectory(traincascade) add_subdirectory(haartraining) +endif() -if(NOT ANDROID) +if(NOT (ANDROID OR IOS)) add_subdirectory(gpu) add_subdirectory(stitching) endif() diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index b0975d7..5712eb3 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -126,6 +126,8 @@ if(WIN32) endif() if(UNIX) + if(NOT IOS) + if(NOT HAVE_QT) if(HAVE_GTK) set(highgui_srcs ${highgui_srcs} src/window_gtk.cpp) @@ -172,6 +174,8 @@ if(UNIX) endif() endif() + endif(NOT IOS) + foreach(P ${HIGHGUI_INCLUDE_DIRS}) include_directories(${P}) endforeach() @@ -188,7 +192,7 @@ if(WITH_OPENNI AND HAVE_OPENNI) endif() #YV -if(APPLE) +if(APPLE AND NOT IOS) add_definitions(-DHAVE_QUICKTIME=1) if(NOT OPENCV_BUILD_3RDPARTY_LIBS) add_definitions(-DHAVE_IMAGEIO=1) @@ -205,11 +209,17 @@ if(APPLE) endif() if(WITH_QUICKTIME) - set(highgui_srcs ${highgui_srcs} src/cap_qt.cpp) - else() - set(highgui_srcs ${highgui_srcs} src/cap_qtkit.mm) - endif() -endif(APPLE) + set(highgui_srcs ${highgui_srcs} src/cap_qt.cpp) + else() + set(highgui_srcs ${highgui_srcs} src/cap_qtkit.mm) + endif() + +endif(APPLE AND NOT IOS) + +if (IOS) + add_definitions(-DHAVE_IMAGEIO=1) + set(highgui_srcs ${highgui_srcs} src/cap_avfoundation.mm) +endif() if(WITH_ANDROID_CAMERA) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../androidcamera/include") @@ -346,7 +356,7 @@ if( OPENNI_LIBRARY ) target_link_libraries(${the_target} ${OPENNI_LIBRARY}) endif() -if(APPLE) +if(APPLE AND NOT IOS) target_link_libraries(${the_target} "-lbz2 -framework Cocoa -framework QuartzCore") if(WITH_CARBON) target_link_libraries(${the_target} "-framework Carbon") @@ -359,6 +369,10 @@ if(APPLE) endif() endif() +if (IOS) + target_link_libraries(${the_target} "-lbz2 -framework QuartzCore -framework CoreFoundation -framework ImageIO -framework CoreGraphics -framework AVFoundation") +endif() + install(TARGETS ${the_target} RUNTIME DESTINATION bin COMPONENT main LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main @@ -394,7 +408,7 @@ if(BUILD_TESTS) source_group("Include" FILES ${test_hdrs}) set(the_target "opencv_test_highgui") - + add_executable(${the_target} ${test_srcs} ${test_hdrs}) add_opencv_precompiled_headers(${the_target}) diff --git a/modules/highgui/include/opencv2/highgui/highgui_c.h b/modules/highgui/include/opencv2/highgui/highgui_c.h index 52fa3a8..841cfcc 100644 --- a/modules/highgui/include/opencv2/highgui/highgui_c.h +++ b/modules/highgui/include/opencv2/highgui/highgui_c.h @@ -289,7 +289,9 @@ enum CV_CAP_ANDROID =1000, // Android - CV_CAP_XIAPI =1100 // XIMEA Camera API + CV_CAP_XIAPI =1100, // XIMEA Camera API + + CV_CAP_AVFOUNDATION = 1200 // AVFoundation framework for iOS (OS X Lion will have the same API) }; /* start capturing frames from camera: index = camera_index + domain_offset (CV_CAP_*) */ @@ -397,7 +399,14 @@ enum CV_CAP_PROP_XI_AE_MAX_LIMIT = 417, // Maximum limit of exposure in AEAG procedure CV_CAP_PROP_XI_AG_MAX_LIMIT = 418, // Maximum limit of gain in AEAG procedure CV_CAP_PROP_XI_AEAG_LEVEL = 419, // Average intensity of output signal AEAG should achieve(in %) - CV_CAP_PROP_XI_TIMEOUT = 420 // Image capture timeout in milliseconds + CV_CAP_PROP_XI_TIMEOUT = 420, // Image capture timeout in milliseconds + + // Properties of cameras available through AVFOUNDATION interface + CV_CAP_PROP_IOS_DEVICE_FOCUS = 9001, + CV_CAP_PROP_IOS_DEVICE_EXPOSURE = 9002, + CV_CAP_PROP_IOS_DEVICE_FLASH = 9003, + CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE = 9004, + CV_CAP_PROP_IOS_DEVICE_TORCH = 9005 }; enum diff --git a/modules/highgui/src/cap.cpp b/modules/highgui/src/cap.cpp index cde89de..5f72684 100644 --- a/modules/highgui/src/cap.cpp +++ b/modules/highgui/src/cap.cpp @@ -136,6 +136,7 @@ CV_IMPL CvCapture * cvCreateCameraCapture (int index) #ifdef HAVE_XIMEA CV_CAP_XIAPI, #endif + CV_CAP_AVFOUNDATION -1 }; @@ -155,7 +156,8 @@ CV_IMPL CvCapture * cvCreateCameraCapture (int index) defined(HAVE_CAMV4L) || defined (HAVE_CAMV4L2) || defined(HAVE_GSTREAMER) || \ defined(HAVE_DC1394_2) || defined(HAVE_DC1394) || defined(HAVE_CMU1394) || \ defined(HAVE_GSTREAMER) || defined(HAVE_MIL) || defined(HAVE_QUICKTIME) || \ - defined(HAVE_UNICAP) || defined(HAVE_PVAPI) || defined(HAVE_OPENNI) || defined(HAVE_ANDROID_NATIVE_CAMERA) + defined(HAVE_UNICAP) || defined(HAVE_PVAPI) || defined(HAVE_OPENNI) || defined(HAVE_ANDROID_NATIVE_CAMERA) || \ + defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) // local variable to memorize the captured device CvCapture *capture; #endif @@ -278,6 +280,14 @@ CV_IMPL CvCapture * cvCreateCameraCapture (int index) return capture; break; #endif + + #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + case CV_CAP_AVFOUNDATION: + capture = cvCreateCameraCapture_AVFoundation (index); + if (capture) + return capture; + break; + #endif } } @@ -311,6 +321,11 @@ CV_IMPL CvCapture * cvCreateFileCapture (const char * filename) result = cvCreateFileCapture_QT (filename); #endif + #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + if (! result) + result = cvCreateFileCapture_AVFoundation (filename); + #endif + if (! result) result = cvCreateFileCapture_Images (filename); @@ -339,6 +354,10 @@ CV_IMPL CvVideoWriter* cvCreateVideoWriter( const char* filename, int fourcc, result = cvCreateVideoWriter_XINE(filename, fourcc, fps, frameSize, is_color); #endif */ + #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + if (! result) + result = cvCreateVideoWriter_AVFoundation(filename, fourcc, fps, frameSize, is_color); + #endif #ifdef HAVE_QUICKTIME if(!result) diff --git a/modules/highgui/src/cap_avfoundation.mm b/modules/highgui/src/cap_avfoundation.mm new file mode 100644 index 0000000..e189224 --- /dev/null +++ b/modules/highgui/src/cap_avfoundation.mm @@ -0,0 +1,1325 @@ +/* + * cap_avfoundation.mm + * For iOS video I/O + * by Xiaochao Yang on 06/15/11 modified from + * cap_qtkit.mm for Nicholas Butko for Mac OS version. + * Copyright 2011. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include +#import +#import + + +//#import + +using namespace std; + +/********************** Declaration of class headers ************************/ + +/***************************************************************************** + * + * CaptureDelegate Declaration. + * + * CaptureDelegate is notified on a separate thread by the OS whenever there + * is a new frame. When "updateImage" is called from the main thread, it + * copies this new frame into an IplImage, but only if this frame has not + * been copied before. When "getOutput" is called from the main thread, + * it gives the last copied IplImage. + * + *****************************************************************************/ + +#define DISABLE_AUTO_RESTART 999 + +@interface CaptureDelegate : NSObject +{ + int newFrame; + CVImageBufferRef mCurrentImageBuffer; + char* imagedata; + IplImage* image; + char* bgr_imagedata; + IplImage* bgr_image; + IplImage* bgr_image_r90; + size_t currSize; +} + +- (void)captureOutput:(AVCaptureOutput *)captureOutput +didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer +fromConnection:(AVCaptureConnection *)connection; + + +- (int)updateImage; +- (IplImage*)getOutput; + +@end + +/***************************************************************************** + * + * CvCaptureCAM Declaration. + * + * CvCaptureCAM is the instantiation of a capture source for cameras. + * + *****************************************************************************/ + +class CvCaptureCAM : public CvCapture { + public: + CvCaptureCAM(int cameraNum = -1) ; + ~CvCaptureCAM(); + virtual bool grabFrame(); + virtual IplImage* retrieveFrame(int); + virtual IplImage* queryFrame(); + virtual double getProperty(int property_id); + virtual bool setProperty(int property_id, double value); + virtual int didStart(); + + private: + AVCaptureSession *mCaptureSession; + AVCaptureDeviceInput *mCaptureDeviceInput; + AVCaptureVideoDataOutput *mCaptureDecompressedVideoOutput; + AVCaptureDevice *mCaptureDevice; + CaptureDelegate *capture; + + int startCaptureDevice(int cameraNum); + void stopCaptureDevice(); + + void setWidthHeight(); + bool grabFrame(double timeOut); + + int camNum; + int width; + int height; + int settingWidth; + int settingHeight; + int started; + int disableAutoRestart; +}; + + +/***************************************************************************** + * + * CvCaptureFile Declaration. + * + * CvCaptureFile is the instantiation of a capture source for video files. + * + *****************************************************************************/ + +class CvCaptureFile : public CvCapture { + public: + + CvCaptureFile(const char* filename) ; + ~CvCaptureFile(); + virtual bool grabFrame(); + virtual IplImage* retrieveFrame(int); + virtual IplImage* queryFrame(); + virtual double getProperty(int property_id); + virtual bool setProperty(int property_id, double value); + virtual int didStart(); + + private: + + AVAssetReader *mMovieReader; + char* imagedata; + IplImage* image; + char* bgr_imagedata; + IplImage* bgr_image; + size_t currSize; + + IplImage* retrieveFramePixelBuffer(); + double getFPS(); + + int movieWidth; + int movieHeight; + double movieFPS; + double currentFPS; + double movieDuration; + int changedPos; + + int started; +}; + + +/***************************************************************************** + * + * CvCaptureFile Declaration. + * + * CvCaptureFile is the instantiation of a capture source for video files. + * + *****************************************************************************/ + +class CvVideoWriter_AVFoundation : public CvVideoWriter{ + public: + CvVideoWriter_AVFoundation(const char* filename, int fourcc, + double fps, CvSize frame_size, + int is_color=1); + ~CvVideoWriter_AVFoundation(); + bool writeFrame(const IplImage* image); + private: + IplImage* argbimage; + + AVAssetWriter *mMovieWriter; + AVAssetWriterInput* mMovieWriterInput; + AVAssetWriterInputPixelBufferAdaptor* mMovieWriterAdaptor; + + unsigned char* imagedata; + NSString* path; + NSString* codec; + NSString* fileType; + double movieFPS; + CvSize movieSize; + int movieColor; + unsigned long frameCount; +}; + + +/****************** Implementation of interface functions ********************/ + + +CvCapture* cvCreateFileCapture_AVFoundation(const char* filename) { + CvCaptureFile *retval = new CvCaptureFile(filename); + + if(retval->didStart()) + return retval; + delete retval; + return NULL; +} + +CvCapture* cvCreateCameraCapture_AVFoundation(int index ) { + + CvCapture* retval = new CvCaptureCAM(index); + if (!((CvCaptureCAM *)retval)->didStart()) + cvReleaseCapture(&retval); + return retval; + +} + +CvVideoWriter* cvCreateVideoWriter_AVFoundation(const char* filename, int fourcc, + double fps, CvSize frame_size, + int is_color) { + return new CvVideoWriter_AVFoundation(filename, fourcc, fps, frame_size,is_color); +} + +/********************** Implementation of Classes ****************************/ +/***************************************************************************** + * + * CvCaptureCAM Implementation. + * + * CvCaptureCAM is the instantiation of a capture source for cameras. + * + *****************************************************************************/ + +CvCaptureCAM::CvCaptureCAM(int cameraNum) { + mCaptureSession = nil; + mCaptureDeviceInput = nil; + mCaptureDecompressedVideoOutput = nil; + capture = nil; + + width = 0; + height = 0; + settingWidth = 0; + settingHeight = 0; + disableAutoRestart = 0; + + camNum = cameraNum; + + if (!startCaptureDevice(camNum)) { + cout << "Warning, camera failed to properly initialize!" << endl; + started = 0; + } else { + started = 1; + } + +} + +CvCaptureCAM::~CvCaptureCAM() { + stopCaptureDevice(); + //cout << "Cleaned up camera." << endl; +} + +int CvCaptureCAM::didStart() { + return started; +} + + +bool CvCaptureCAM::grabFrame() { + return grabFrame(5); +} + +bool CvCaptureCAM::grabFrame(double timeOut) { + + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + double sleepTime = 0.005; + double total = 0; + + NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime]; + while (![capture updateImage] && (total += sleepTime)<=timeOut && + [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode + beforeDate:loopUntil]) + loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime]; + + [localpool drain]; + + return total <= timeOut; +} + +IplImage* CvCaptureCAM::retrieveFrame(int) { + return [capture getOutput]; +} + +IplImage* CvCaptureCAM::queryFrame() { + while (!grabFrame()) { + cout << "WARNING: Couldn't grab new frame from camera!!!" << endl; + /* + cout << "Attempting to restart camera; set capture property DISABLE_AUTO_RESTART to disable." << endl; + stopCaptureDevice(); + startCaptureDevice(camNum); + */ + } + return retrieveFrame(0); +} + +void CvCaptureCAM::stopCaptureDevice() { + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + [mCaptureSession stopRunning]; + + [mCaptureSession release]; + [mCaptureDeviceInput release]; + + [mCaptureDecompressedVideoOutput release]; + [capture release]; + [localpool drain]; + +} + +int CvCaptureCAM::startCaptureDevice(int cameraNum) { + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + capture = [[CaptureDelegate alloc] init]; + + AVCaptureDevice *device; + NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; + if ([devices count] == 0) { + cout << "AV Foundation didn't find any attached Video Input Devices!" << endl; + [localpool drain]; + return 0; + } + + if (cameraNum >= 0) { + camNum = cameraNum % [devices count]; + if (camNum != cameraNum) { + cout << "Warning: Max Camera Num is " << [devices count]-1 << "; Using camera " << camNum << endl; + } + device = [devices objectAtIndex:camNum]; + } else { + device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] ; + } + mCaptureDevice = device; + //int success; + NSError* error; + + if (device) { + + mCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error] ; + mCaptureSession = [[AVCaptureSession alloc] init] ; + + /* + success = [mCaptureSession addInput:mCaptureDeviceInput]; + + if (!success) { + cout << "AV Foundation failed to start capture session with opened Capture Device" << endl; + [localpool drain]; + return 0; + } + */ + + mCaptureDecompressedVideoOutput = [[AVCaptureVideoDataOutput alloc] init]; + + dispatch_queue_t queue = dispatch_queue_create("cameraQueue", NULL); + [mCaptureDecompressedVideoOutput setSampleBufferDelegate:capture queue:queue]; + dispatch_release(queue); + + + NSDictionary *pixelBufferOptions ; + if (width > 0 && height > 0) { + pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey, + [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey, + [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], + (id)kCVPixelBufferPixelFormatTypeKey, + nil]; + } else { + pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], + (id)kCVPixelBufferPixelFormatTypeKey, + nil]; + } + + //TODO: add new interface for setting fps and capturing resolution. + [mCaptureDecompressedVideoOutput setVideoSettings:pixelBufferOptions]; + mCaptureDecompressedVideoOutput.alwaysDiscardsLateVideoFrames = YES; + mCaptureDecompressedVideoOutput.minFrameDuration = CMTimeMake(1, 30); + + //Slow. 1280*720 for iPhone4, iPod back camera. 640*480 for front camera + //mCaptureSession.sessionPreset = AVCaptureSessionPresetHigh; // fps ~= 5 slow for OpenCV + + mCaptureSession.sessionPreset = AVCaptureSessionPresetMedium; //480*360 + if (width == 0 ) width = 480; + if (height == 0 ) height = 360; + + [mCaptureSession addInput:mCaptureDeviceInput]; + [mCaptureSession addOutput:mCaptureDecompressedVideoOutput]; + + /* + // Does not work! This is the preferred way (hardware acceleration) to change pixel buffer orientation. + // I'm now using cvtranspose and cvflip instead, which takes cpu cycles. + AVCaptureConnection *connection = [[mCaptureDecompressedVideoOutput connections] objectAtIndex:0]; + if([connection isVideoOrientationSupported]) { + //NSLog(@"Setting pixel buffer orientation"); + connection.videoOrientation = AVCaptureVideoOrientationPortrait; + } + */ + + [mCaptureSession startRunning]; + + grabFrame(60); + [localpool drain]; + return 1; + } + + [localpool drain]; + return 0; +} + +void CvCaptureCAM::setWidthHeight() { + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + NSDictionary* pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey, + [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey, + [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], + (id)kCVPixelBufferPixelFormatTypeKey, + nil]; + + [mCaptureDecompressedVideoOutput setVideoSettings:pixelBufferOptions]; + grabFrame(60); + [localpool drain]; +} + +//added macros into headers in highgui_c.h +/* +#define CV_CAP_PROP_IOS_DEVICE_FOCUS 9001 +#define CV_CAP_PROP_IOS_DEVICE_EXPOSURE 9002 +#define CV_CAP_PROP_IOS_DEVICE_FLASH 9003 +#define CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE 9004 +#define CV_CAP_PROP_IOS_DEVICE_TORCH 9005 +*/ + + +/* +// All available settings are taken from iOS API + +enum { + AVCaptureFlashModeOff = 0, + AVCaptureFlashModeOn = 1, + AVCaptureFlashModeAuto = 2 +}; +typedef NSInteger AVCaptureFlashMode; + +enum { + AVCaptureTorchModeOff = 0, + AVCaptureTorchModeOn = 1, + AVCaptureTorchModeAuto = 2 +}; +typedef NSInteger AVCaptureTorchMode; + +enum { + AVCaptureFocusModeLocked = 0, + AVCaptureFocusModeAutoFocus = 1, + AVCaptureFocusModeContinuousAutoFocus = 2, +}; +typedef NSInteger AVCaptureFocusMode; + +enum { + AVCaptureExposureModeLocked = 0, + AVCaptureExposureModeAutoExpose = 1, + AVCaptureExposureModeContinuousAutoExposure = 2, +}; +typedef NSInteger AVCaptureExposureMode; + +enum { + AVCaptureWhiteBalanceModeLocked = 0, + AVCaptureWhiteBalanceModeAutoWhiteBalance = 1, + AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance = 2, +}; +typedef NSInteger AVCaptureWhiteBalanceMode; +*/ + +double CvCaptureCAM::getProperty(int property_id){ + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + /* + NSArray* connections = [mCaptureDeviceInput connections]; + QTFormatDescription* format = [[connections objectAtIndex:0] formatDescription]; + NSSize s1 = [[format attributeForKey:QTFormatDescriptionVideoCleanApertureDisplaySizeAttribute] sizeValue]; + */ + + NSArray* ports = mCaptureDeviceInput.ports; + CMFormatDescriptionRef format = [[ports objectAtIndex:0] formatDescription]; + CGSize s1 = CMVideoFormatDescriptionGetPresentationDimensions(format, YES, YES); + + int width=(int)s1.width, height=(int)s1.height; + + [localpool drain]; + + switch (property_id) { + case CV_CAP_PROP_FRAME_WIDTH: + return width; + case CV_CAP_PROP_FRAME_HEIGHT: + return height; + + case CV_CAP_PROP_IOS_DEVICE_FOCUS: + return mCaptureDevice.focusMode; + case CV_CAP_PROP_IOS_DEVICE_EXPOSURE: + return mCaptureDevice.exposureMode; + case CV_CAP_PROP_IOS_DEVICE_FLASH: + return mCaptureDevice.flashMode; + case CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE: + return mCaptureDevice.whiteBalanceMode; + case CV_CAP_PROP_IOS_DEVICE_TORCH: + return mCaptureDevice.torchMode; + + default: + return 0; + } + + +} + +bool CvCaptureCAM::setProperty(int property_id, double value) { + switch (property_id) { + case CV_CAP_PROP_FRAME_WIDTH: + width = value; + settingWidth = 1; + if (settingWidth && settingHeight) { + setWidthHeight(); + settingWidth =0; + settingHeight = 0; + } + return true; + + case CV_CAP_PROP_FRAME_HEIGHT: + height = value; + settingHeight = 1; + if (settingWidth && settingHeight) { + setWidthHeight(); + settingWidth =0; + settingHeight = 0; + } + return true; + + case CV_CAP_PROP_IOS_DEVICE_FOCUS: + if ([mCaptureDevice isFocusModeSupported:(int)value]){ + NSError* error = nil; + [mCaptureDevice lockForConfiguration:&error]; + if (error) return false; + [mCaptureDevice setFocusMode:(int)value]; + [mCaptureDevice unlockForConfiguration]; + //NSLog(@"Focus set"); + return true; + }else { + return false; + } + + case CV_CAP_PROP_IOS_DEVICE_EXPOSURE: + if ([mCaptureDevice isExposureModeSupported:(int)value]){ + NSError* error = nil; + [mCaptureDevice lockForConfiguration:&error]; + if (error) return false; + [mCaptureDevice setExposureMode:(int)value]; + [mCaptureDevice unlockForConfiguration]; + //NSLog(@"Exposure set"); + return true; + }else { + return false; + } + + case CV_CAP_PROP_IOS_DEVICE_FLASH: + if ( [mCaptureDevice hasFlash] && [mCaptureDevice isFlashModeSupported:(int)value]){ + NSError* error = nil; + [mCaptureDevice lockForConfiguration:&error]; + if (error) return false; + [mCaptureDevice setFlashMode:(int)value]; + [mCaptureDevice unlockForConfiguration]; + //NSLog(@"Flash mode set"); + return true; + }else { + return false; + } + + case CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE: + if ([mCaptureDevice isWhiteBalanceModeSupported:(int)value]){ + NSError* error = nil; + [mCaptureDevice lockForConfiguration:&error]; + if (error) return false; + [mCaptureDevice setWhiteBalanceMode:(int)value]; + [mCaptureDevice unlockForConfiguration]; + //NSLog(@"White balance set"); + return true; + }else { + return false; + } + + case CV_CAP_PROP_IOS_DEVICE_TORCH: + if ([mCaptureDevice hasFlash] && [mCaptureDevice isTorchModeSupported:(int)value]){ + NSError* error = nil; + [mCaptureDevice lockForConfiguration:&error]; + if (error) return false; + [mCaptureDevice setTorchMode:(int)value]; + [mCaptureDevice unlockForConfiguration]; + //NSLog(@"Torch mode set"); + return true; + }else { + return false; + } + + case DISABLE_AUTO_RESTART: + disableAutoRestart = value; + return 1; + default: + return false; + } +} + + +/***************************************************************************** + * + * CaptureDelegate Implementation. + * + * CaptureDelegate is notified on a separate thread by the OS whenever there + * is a new frame. When "updateImage" is called from the main thread, it + * copies this new frame into an IplImage, but only if this frame has not + * been copied before. When "getOutput" is called from the main thread, + * it gives the last copied IplImage. + * + *****************************************************************************/ + + +@implementation CaptureDelegate + +- (id)init { + [super init]; + newFrame = 0; + imagedata = NULL; + bgr_imagedata = NULL; + currSize = 0; + image = NULL; + bgr_image = NULL; + bgr_image_r90 = NULL; + return self; +} + + +-(void)dealloc { + if (imagedata != NULL) free(imagedata); + if (bgr_imagedata != NULL) free(bgr_imagedata); + cvReleaseImage(&image); + cvReleaseImage(&bgr_image); + cvReleaseImage(&bgr_image_r90); + [super dealloc]; +} + + + +- (void)captureOutput:(AVCaptureOutput *)captureOutput +didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer +fromConnection:(AVCaptureConnection *)connection{ + + // Failed + // connection.videoOrientation = AVCaptureVideoOrientationPortrait; + + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + + CVBufferRetain(imageBuffer); + CVImageBufferRef imageBufferToRelease = mCurrentImageBuffer; + + @synchronized (self) { + + mCurrentImageBuffer = imageBuffer; + newFrame = 1; + } + + CVBufferRelease(imageBufferToRelease); + +} + + +-(IplImage*) getOutput { + //return bgr_image; + return bgr_image_r90; +} + +-(int) updateImage { + if (newFrame==0) return 0; + CVPixelBufferRef pixels; + + @synchronized (self){ + pixels = CVBufferRetain(mCurrentImageBuffer); + newFrame = 0; + } + + CVPixelBufferLockBaseAddress(pixels, 0); + uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels); + + size_t width = CVPixelBufferGetWidth(pixels); + size_t height = CVPixelBufferGetHeight(pixels); + size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels); + + if (rowBytes != 0) { + + if (currSize != rowBytes*height*sizeof(char)) { + currSize = rowBytes*height*sizeof(char); + if (imagedata != NULL) free(imagedata); + if (bgr_imagedata != NULL) free(bgr_imagedata); + imagedata = (char*)malloc(currSize); + bgr_imagedata = (char*)malloc(currSize); + } + + memcpy(imagedata, baseaddress, currSize); + + if (image == NULL) { + image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4); + } + image->width =width; + image->height = height; + image->nChannels = 4; + image->depth = IPL_DEPTH_8U; + image->widthStep = rowBytes; + image->imageData = imagedata; + image->imageSize = currSize; + + if (bgr_image == NULL) { + bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3); + } + bgr_image->width =width; + bgr_image->height = height; + bgr_image->nChannels = 3; + bgr_image->depth = IPL_DEPTH_8U; + bgr_image->widthStep = rowBytes; + bgr_image->imageData = bgr_imagedata; + bgr_image->imageSize = currSize; + + cvCvtColor(image, bgr_image, CV_BGRA2BGR); + + // image taken from the buffer is incorrected rotated. I'm using cvTranspose + cvFlip. + // There should be an option in iOS API to rotate the buffer output orientation. + // iOS provides hardware accelerated rotation through AVCaptureConnection class + // I can't get it work. + if (bgr_image_r90 == NULL){ + bgr_image_r90 = cvCreateImage(cvSize(height, width), IPL_DEPTH_8U, 3); + } + cvTranspose(bgr_image, bgr_image_r90); + cvFlip(bgr_image_r90, NULL, 1); + + } + + CVPixelBufferUnlockBaseAddress(pixels, 0); + CVBufferRelease(pixels); + + return 1; +} + +@end + + +/***************************************************************************** + * + * CvCaptureFile Implementation. + * + * CvCaptureFile is the instantiation of a capture source for video files. + * + *****************************************************************************/ + +CvCaptureFile::CvCaptureFile(const char* filename) { + + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + mMovieReader = nil; + image = NULL; + bgr_image = NULL; + imagedata = NULL; + bgr_imagedata = NULL; + currSize = 0; + + movieWidth = 0; + movieHeight = 0; + movieFPS = 0; + currentFPS = 0; + movieDuration = 0; + changedPos = 0; + + started = 0; + + AVURLAsset *asset = [AVURLAsset URLAssetWithURL: + [NSURL fileURLWithPath: [NSString stringWithUTF8String:filename]] + options:nil]; + + AVAssetTrack* videoTrack = nil; + NSArray* tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; + if ([tracks count] == 1) + { + videoTrack = [tracks objectAtIndex:0]; + + movieWidth = videoTrack.naturalSize.width; + movieHeight = videoTrack.naturalSize.height; + movieFPS = videoTrack.nominalFrameRate; + + currentFPS = movieFPS; //Debugging !! should be getFPS(); + //Debugging. need to be checked + + // In ms + movieDuration = videoTrack.timeRange.duration.value/videoTrack.timeRange.duration.timescale * 1000; + + started = 1; + NSError* error = nil; + mMovieReader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; + if (error) + NSLog(@"%@", [error localizedDescription]); + + NSDictionary* videoSettings = + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] + forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]; + + [mMovieReader addOutput:[AVAssetReaderTrackOutput + assetReaderTrackOutputWithTrack:videoTrack + outputSettings:videoSettings]]; + [mMovieReader startReading]; + } + + /* + // Asynchronously open the video in another thread. Always fail. + [asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler: + ^{ + // The completion block goes here. + dispatch_async(dispatch_get_main_queue(), + ^{ + AVAssetTrack* ::videoTrack = nil; + NSArray* ::tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; + if ([tracks count] == 1) + { + videoTrack = [tracks objectAtIndex:0]; + + movieWidth = videoTrack.naturalSize.width; + movieHeight = videoTrack.naturalSize.height; + movieFPS = videoTrack.nominalFrameRate; + currentFPS = movieFPS; //Debugging !! should be getFPS(); + //Debugging. need to be checked + movieDuration = videoTrack.timeRange.duration.value/videoTrack.timeRange.duration.timescale * 1000; + started = 1; + + NSError* ::error = nil; + // mMovieReader is a member variable + mMovieReader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; + if (error) + NSLog(@"%@", [error localizedDescription]); + + NSDictionary* ::videoSettings = + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] +forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]; + +[mMovieReader addOutput:[AVAssetReaderTrackOutput +assetReaderTrackOutputWithTrack:videoTrack +outputSettings:videoSettings]]; +[mMovieReader startReading]; +} +}); + +}]; + */ + +[localpool drain]; +} + +CvCaptureFile::~CvCaptureFile() { + + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + if (imagedata != NULL) free(imagedata); + if (bgr_imagedata != NULL) free(bgr_imagedata); + cvReleaseImage(&image); + cvReleaseImage(&bgr_image); + [mMovieReader release]; + [localpool drain]; +} + +int CvCaptureFile::didStart() { + return started; +} + +bool CvCaptureFile::grabFrame() { + + //everything is done in queryFrame; + currentFPS = movieFPS; + return 1; + + + /* + double t1 = getProperty(CV_CAP_PROP_POS_MSEC); + [mCaptureSession stepForward]; + double t2 = getProperty(CV_CAP_PROP_POS_MSEC); + if (t2>t1 && !changedPos) { + currentFPS = 1000.0/(t2-t1); + } else { + currentFPS = movieFPS; + } + changedPos = 0; + + */ + +} + + +IplImage* CvCaptureFile::retrieveFramePixelBuffer() { + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + if (mMovieReader.status != AVAssetReaderStatusReading){ + + return NULL; + } + + + AVAssetReaderTrackOutput * output = [mMovieReader.outputs objectAtIndex:0]; + CMSampleBufferRef sampleBuffer = [output copyNextSampleBuffer]; + if (!sampleBuffer) { + [localpool drain]; + return NULL; + } + CVPixelBufferRef frame = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferRef pixels = CVBufferRetain(frame); + + CVPixelBufferLockBaseAddress(pixels, 0); + + uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels); + size_t width = CVPixelBufferGetWidth(pixels); + size_t height = CVPixelBufferGetHeight(pixels); + size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels); + + if (rowBytes != 0) { + + if (currSize != rowBytes*height*sizeof(char)) { + currSize = rowBytes*height*sizeof(char); + if (imagedata != NULL) free(imagedata); + if (bgr_imagedata != NULL) free(bgr_imagedata); + imagedata = (char*)malloc(currSize); + bgr_imagedata = (char*)malloc(currSize); + } + + memcpy(imagedata, baseaddress, currSize); + + if (image == NULL) { + image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4); + } + + image->width =width; + image->height = height; + image->nChannels = 4; + image->depth = IPL_DEPTH_8U; + image->widthStep = width*4; + image->imageData = imagedata; + image->imageSize = currSize; + + + if (bgr_image == NULL) { + bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3); + } + + bgr_image->width =width; + bgr_image->height = height; + bgr_image->nChannels = 3; + bgr_image->depth = IPL_DEPTH_8U; + bgr_image->widthStep = width*4; + bgr_image->imageData = bgr_imagedata; + bgr_image->imageSize = currSize; + + cvCvtColor(image, bgr_image,CV_BGRA2BGR); + + } + + CVPixelBufferUnlockBaseAddress(pixels, 0); + CVBufferRelease(pixels); + + [localpool drain]; + return bgr_image; +} + + +IplImage* CvCaptureFile::retrieveFrame(int) { + return retrieveFramePixelBuffer(); +} + +IplImage* CvCaptureFile::queryFrame() { + grabFrame(); + return retrieveFrame(0); +} + +double CvCaptureFile::getFPS() { + + /* + if (mCaptureSession == nil) return 0; + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + double now = getProperty(CV_CAP_PROP_POS_MSEC); + double retval = 0; + if (now == 0) { + [mCaptureSession stepForward]; + double t2 = getProperty(CV_CAP_PROP_POS_MSEC); + [mCaptureSession stepBackward]; + retval = 1000.0 / (t2-now); + } else { + [mCaptureSession stepBackward]; + double t2 = getProperty(CV_CAP_PROP_POS_MSEC); + [mCaptureSession stepForward]; + retval = 1000.0 / (now-t2); + } + [localpool drain]; + return retval; + */ + return 30.0; //TODO: Debugging +} + +double CvCaptureFile::getProperty(int property_id){ + + /* + if (mCaptureSession == nil) return 0; + + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + double retval; + QTTime t; + + switch (property_id) { + case CV_CAP_PROP_POS_MSEC: + [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t]; + retval = t.timeValue * 1000.0 / t.timeScale; + break; + case CV_CAP_PROP_POS_FRAMES: + retval = movieFPS * getProperty(CV_CAP_PROP_POS_MSEC) / 1000; + break; + case CV_CAP_PROP_POS_AVI_RATIO: + retval = (getProperty(CV_CAP_PROP_POS_MSEC)) / (movieDuration ); + break; + case CV_CAP_PROP_FRAME_WIDTH: + retval = movieWidth; + break; + case CV_CAP_PROP_FRAME_HEIGHT: + retval = movieHeight; + break; + case CV_CAP_PROP_FPS: + retval = currentFPS; + break; + case CV_CAP_PROP_FOURCC: + default: + retval = 0; + } + + [localpool drain]; + return retval; + */ + return 1.0; //Debugging +} + +bool CvCaptureFile::setProperty(int property_id, double value) { + + /* + if (mCaptureSession == nil) return false; + + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + bool retval = false; + QTTime t; + + double ms; + + switch (property_id) { + case CV_CAP_PROP_POS_MSEC: + [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t]; + t.timeValue = value * t.timeScale / 1000; + [mCaptureSession setCurrentTime:t]; + changedPos = 1; + retval = true; + break; + case CV_CAP_PROP_POS_FRAMES: + ms = (value*1000.0 -5)/ currentFPS; + retval = setProperty(CV_CAP_PROP_POS_MSEC, ms); + break; + case CV_CAP_PROP_POS_AVI_RATIO: + ms = value * movieDuration; + retval = setProperty(CV_CAP_PROP_POS_MSEC, ms); + break; + case CV_CAP_PROP_FRAME_WIDTH: + //retval = movieWidth; + break; + case CV_CAP_PROP_FRAME_HEIGHT: + //retval = movieHeight; + break; + case CV_CAP_PROP_FPS: + //etval = currentFPS; + break; + case CV_CAP_PROP_FOURCC: + default: + retval = false; + } + + [localpool drain]; + + return retval; + */ + return true; +} + + +/***************************************************************************** + * + * CvVideoWriter Implementation. + * + * CvVideoWriter is the instantiation of a video output class + * + *****************************************************************************/ + + +CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int fourcc, + double fps, CvSize frame_size, + int is_color) { + + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + + frameCount = 0; + movieFPS = fps; + movieSize = frame_size; + movieColor = is_color; + argbimage = cvCreateImage(movieSize, IPL_DEPTH_8U, 4); + path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain]; + + + /* + AVFileTypeQuickTimeMovie + UTI for the QuickTime movie file format. + The value of this UTI is com.apple.quicktime-movie. Files are identified with the .mov and .qt extensions. + + AVFileTypeMPEG4 + UTI for the MPEG-4 file format. + The value of this UTI is public.mpeg-4. Files are identified with the .mp4 extension. + + AVFileTypeAppleM4V + UTI for the iTunes video file format. + The value of this UTI is com.apple.mpeg-4-video. Files are identified with the .m4v extension. + + AVFileType3GPP + UTI for the 3GPP file format. + The value of this UTI is public.3gpp. Files are identified with the .3gp, .3gpp, and .sdv extensions. + */ + + NSString *fileExt =[[[path pathExtension] lowercaseString] copy]; + if ([fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"qt"]){ + fileType = [AVFileTypeQuickTimeMovie copy]; + }else if ([fileExt isEqualToString:@"mp4"]){ + fileType = [AVFileTypeMPEG4 copy]; + }else if ([fileExt isEqualToString:@"m4v"]){ + fileType = [AVFileTypeAppleM4V copy]; + }else if ([fileExt isEqualToString:@"3gp"] || [fileExt isEqualToString:@"3gpp"] || [fileExt isEqualToString:@"sdv"] ){ + fileType = [AVFileType3GPP copy]; + }else{ + fileType = [AVFileTypeMPEG4 copy]; //default mp4 + } + [fileExt release]; + + char cc[5]; + cc[0] = fourcc & 255; + cc[1] = (fourcc >> 8) & 255; + cc[2] = (fourcc >> 16) & 255; + cc[3] = (fourcc >> 24) & 255; + cc[4] = 0; + int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]); + if (cc2!=fourcc) { + cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc + << " but got " << cc2 << "." << endl; + //exception; + } + + // Two codec supported AVVideoCodecH264 AVVideoCodecJPEG + // On iPhone 3G H264 is not supported. + if (fourcc == CV_FOURCC('J','P','E','G') || fourcc == CV_FOURCC('j','p','e','g') || + fourcc == CV_FOURCC('M','J','P','G') || fourcc == CV_FOURCC('m','j','p','g') ){ + codec = [AVVideoCodecJPEG copy]; // Use JPEG codec if specified, otherwise H264 + }else if(fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')){ + codec = [AVVideoCodecH264 copy]; + }else{ + codec = [AVVideoCodecH264 copy]; // default canonical H264. + + } + + //NSLog(@"Path: %@", path); + + NSError *error = nil; + + + // Make sure the file does not already exist. Necessary to overwirte?? + /* + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:path]){ + [fileManager removeItemAtPath:path error:&error]; + } + */ + + // Wire the writer: + // Supported file types: + // AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP + + mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] + fileType:fileType + error:&error]; + //NSParameterAssert(mMovieWriter); + + NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: + codec, AVVideoCodecKey, + [NSNumber numberWithInt:movieSize.width], AVVideoWidthKey, + [NSNumber numberWithInt:movieSize.height], AVVideoHeightKey, + nil]; + + mMovieWriterInput = [[AVAssetWriterInput + assetWriterInputWithMediaType:AVMediaTypeVideo + outputSettings:videoSettings] retain]; + + //NSParameterAssert(mMovieWriterInput); + //NSParameterAssert([mMovieWriter canAddInput:mMovieWriterInput]); + + [mMovieWriter addInput:mMovieWriterInput]; + + mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil]; + + + //Start a session: + [mMovieWriter startWriting]; + [mMovieWriter startSessionAtSourceTime:kCMTimeZero]; + + + if(mMovieWriter.status == AVAssetWriterStatusFailed){ + NSLog(@"%@", [mMovieWriter.error localizedDescription]); + // TODO: error handling, cleanup. Throw execption? + // return; + } + + [localpool drain]; +} + + +CvVideoWriter_AVFoundation::~CvVideoWriter_AVFoundation() { + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + [mMovieWriterInput markAsFinished]; + [mMovieWriter finishWriting]; + [mMovieWriter release]; + [mMovieWriterInput release]; + [mMovieWriterAdaptor release]; + [path release]; + [codec release]; + [fileType release]; + cvReleaseImage(&argbimage); + + [localpool drain]; + +} + +bool CvVideoWriter_AVFoundation::writeFrame(const IplImage* iplimage) { + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; + + // writer status check + if (![mMovieWriterInput isReadyForMoreMediaData] || mMovieWriter.status != AVAssetWriterStatusWriting ) { + NSLog(@"[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ..."); + NSLog(@"mMovieWriter.status: %d. Error: %@", mMovieWriter.status, [mMovieWriter.error localizedDescription]); + [localpool drain]; + return false; + } + + BOOL success = FALSE; + + if (iplimage->height!=movieSize.height || iplimage->width!=movieSize.width){ + cout<<"Frame size does not match video size."<nChannels == 3); + cvCvtColor(iplimage, argbimage, CV_BGR2BGRA); + }else{ + //assert(iplimage->nChannels == 1); + cvCvtColor(iplimage, argbimage, CV_GRAY2BGRA); + } + //IplImage -> CGImage conversion + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + NSData *nsData = [NSData dataWithBytes:argbimage->imageData length:argbimage->imageSize]; + CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)nsData); + CGImageRef cgImage = CGImageCreate(argbimage->width, argbimage->height, + argbimage->depth, argbimage->depth * argbimage->nChannels, argbimage->widthStep, + colorSpace, kCGImageAlphaLast|kCGBitmapByteOrderDefault, + provider, NULL, false, kCGRenderingIntentDefault); + + //CGImage -> CVPixelBufferRef coversion + CVPixelBufferRef pixelBuffer = NULL; + CFDataRef cfData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage)); + int status = CVPixelBufferCreateWithBytes(NULL, + movieSize.width, + movieSize.height, + kCVPixelFormatType_32BGRA, + (void*)CFDataGetBytePtr(cfData), + CGImageGetBytesPerRow(cgImage), + NULL, + 0, + NULL, + &pixelBuffer); + if(status == kCVReturnSuccess){ + success = [mMovieWriterAdaptor appendPixelBuffer:pixelBuffer + withPresentationTime:CMTimeMake(frameCount, movieFPS)]; + } + + //cleanup + CGImageRelease(cgImage); + CGDataProviderRelease(provider); + CGColorSpaceRelease(colorSpace); + + [localpool drain]; + + if (success) { + frameCount ++; + //NSLog(@"Frame #%d", frameCount); + return true; + }else{ + NSLog(@"Frame appendPixelBuffer failed."); + return false; + } + +} + diff --git a/modules/highgui/src/grfmt_imageio.cpp b/modules/highgui/src/grfmt_imageio.cpp index 1368c49..379d8b31 100644 --- a/modules/highgui/src/grfmt_imageio.cpp +++ b/modules/highgui/src/grfmt_imageio.cpp @@ -113,7 +113,11 @@ bool ImageIODecoder::readData( Mat& img ) } else if( color == CV_LOAD_IMAGE_COLOR ) { +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + colorSpace = CGColorSpaceCreateDeviceRGB(); +#else colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGBLinear ); +#endif bpp = 4; /* CG only has 8 and 32 bit color spaces, so we waste a byte */ alphaInfo = kCGImageAlphaNoneSkipLast; } @@ -272,10 +276,20 @@ bool ImageIOEncoder::write( const Mat& img, const vector& params ) CGColorSpaceRef colorSpace; uchar* bitmapData = NULL; - if( bpp == 1 ) + if( bpp == 1 ) { +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + colorSpace = CGColorSpaceCreateDeviceGray(); +#else colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericGray ); - else if( bpp == 4 ) +#endif + } + else if( bpp == 4 ) { +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + colorSpace = CGColorSpaceCreateDeviceRGB(); +#else colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGBLinear ); +#endif + } if( !colorSpace ) return false; diff --git a/modules/highgui/src/grfmt_imageio.hpp b/modules/highgui/src/grfmt_imageio.hpp index 9c152da..00a919d 100644 --- a/modules/highgui/src/grfmt_imageio.hpp +++ b/modules/highgui/src/grfmt_imageio.hpp @@ -12,8 +12,19 @@ #ifdef HAVE_IMAGEIO #include "grfmt_base.hpp" +#include + +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + +#include +#include + +#else + #include +#endif + namespace cv { diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index f141e22..e54e1ba 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -40,6 +40,7 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ +#include "precomp.hpp" #import @@ -67,7 +68,6 @@ CV_IMPL int cvWaitKey (int maxWait) {return 0;} //*** end IphoneOS Stubs ***/ #else -#include "precomp.hpp" #import #include diff --git a/modules/ts/include/opencv2/ts/ts_gtest.h b/modules/ts/include/opencv2/ts/ts_gtest.h index fab73fc..9872440 100644 --- a/modules/ts/include/opencv2/ts/ts_gtest.h +++ b/modules/ts/include/opencv2/ts/ts_gtest.h @@ -306,6 +306,10 @@ #endif // _WIN32_WCE #elif defined __APPLE__ #define GTEST_OS_MAC 1 +#include +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR +#define GTEST_OS_MAC_IOS 1 +#endif #elif defined __linux__ #define GTEST_OS_LINUX 1 #elif defined __MVS__ diff --git a/modules/ts/src/ts_gtest.cpp b/modules/ts/src/ts_gtest.cpp index 43a5b5c..9b1daef 100644 --- a/modules/ts/src/ts_gtest.cpp +++ b/modules/ts/src/ts_gtest.cpp @@ -6078,7 +6078,7 @@ void InitGoogleTest(int* argc, wchar_t** argv) { #if GTEST_HAS_DEATH_TEST -#if GTEST_OS_MAC +#if GTEST_OS_MAC && !GTEST_OS_MAC_IOS #include #endif // GTEST_OS_MAC @@ -6871,7 +6871,7 @@ struct ExecDeathTestArgs { int close_fd; // File descriptor to close; the read end of a pipe }; -#if GTEST_OS_MAC +#if GTEST_OS_MAC && !GTEST_OS_MAC_IOS inline char** GetEnviron() { // When Google Test is built as a framework on MacOS X, the environ variable // is unavailable. Apple's documentation (man environ) recommends using -- 2.7.4