diff --git a/cmake/templates/opencv_abi.xml.in b/cmake/templates/opencv_abi.xml.in index 212b6d67d4..711c4e99ee 100644 --- a/cmake/templates/opencv_abi.xml.in +++ b/cmake/templates/opencv_abi.xml.in @@ -32,6 +32,7 @@ opencv2/flann/hdf5.h opencv2/imgcodecs/imgcodecs_c.h opencv2/imgcodecs/ios.h + opencv2/imgcodecs/macosx.h opencv2/videoio/videoio_c.h opencv2/videoio/cap_ios.h opencv2/xobjdetect/private.hpp diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index f8bfd18e1f..80f7e1c248 100644 --- a/modules/imgcodecs/CMakeLists.txt +++ b/modules/imgcodecs/CMakeLists.txt @@ -113,10 +113,18 @@ file(GLOB imgcodecs_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/legacy/*.h" ) +if(APPLE) + list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/apple_conversions.h) + list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/apple_conversions.mm) +endif() if(IOS) list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/ios_conversions.mm) list(APPEND IMGCODECS_LIBRARIES "-framework UIKit" "-framework AssetsLibrary") endif() +if(APPLE AND (NOT IOS)) + list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/macosx_conversions.mm) + list(APPEND IMGCODECS_LIBRARIES "-framework AppKit") +endif() if(APPLE_FRAMEWORK) list(APPEND IMGCODECS_LIBRARIES "-framework Accelerate" "-framework CoreGraphics" "-framework QuartzCore") endif() diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 97ca866e1b..c07a905914 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -50,6 +50,7 @@ @{ @defgroup imgcodecs_c C API @defgroup imgcodecs_ios iOS glue + @defgroup imgcodecs_macosx MacOS(OSX) glue @} */ diff --git a/modules/imgcodecs/include/opencv2/imgcodecs/macosx.h b/modules/imgcodecs/include/opencv2/imgcodecs/macosx.h new file mode 100644 index 0000000000..f5d9c082c4 --- /dev/null +++ b/modules/imgcodecs/include/opencv2/imgcodecs/macosx.h @@ -0,0 +1,20 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#if !defined(__APPLE__) || !defined(__MACH__) +#error This header should be used in macOS ObjC/Swift projects. +#endif + +#import +#include "opencv2/core.hpp" + +//! @addtogroup imgcodecs_macosx +//! @{ + +CV_EXPORTS CGImageRef MatToCGImage(const cv::Mat& image); +CV_EXPORTS void CGImageToMat(const CGImageRef image, cv::Mat& m, bool alphaExist = false); +CV_EXPORTS NSImage* MatToNSImage(const cv::Mat& image); +CV_EXPORTS void NSImageToMat(const NSImage* image, cv::Mat& m, bool alphaExist = false); + +//! @} diff --git a/modules/imgcodecs/misc/objc/macosx/Mat+Converters.h b/modules/imgcodecs/misc/objc/macosx/Mat+Converters.h new file mode 100644 index 0000000000..4abf806d1e --- /dev/null +++ b/modules/imgcodecs/misc/objc/macosx/Mat+Converters.h @@ -0,0 +1,32 @@ +// +// Mat+Converters.h +// +// Created by Masaya Tsuruta on 2020/10/08. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#else +#define CV_EXPORTS +#endif + +#import +#import +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +CV_EXPORTS @interface Mat (Converters) + +-(CGImageRef)toCGImage; +-(instancetype)initWithCGImage:(CGImageRef)image; +-(instancetype)initWithCGImage:(CGImageRef)image alphaExist:(BOOL)alphaExist; +-(NSImage*)toNSImage; +-(instancetype)initWithNSImage:(NSImage*)image; +-(instancetype)initWithNSImage:(NSImage*)image alphaExist:(BOOL)alphaExist; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/imgcodecs/misc/objc/macosx/Mat+Converters.mm b/modules/imgcodecs/misc/objc/macosx/Mat+Converters.mm new file mode 100644 index 0000000000..725569784a --- /dev/null +++ b/modules/imgcodecs/misc/objc/macosx/Mat+Converters.mm @@ -0,0 +1,44 @@ +// +// Mat+Converters.mm +// +// Created by Masaya Tsuruta on 2020/10/08. +// + +#import "Mat+Converters.h" +#import + +@implementation Mat (Converters) + +-(CGImageRef)toCGImage { + return MatToCGImage(self.nativeRef); +} + +-(instancetype)initWithCGImage:(CGImageRef)image { + return [self initWithCGImage:image alphaExist:NO]; +} + +-(instancetype)initWithCGImage:(CGImageRef)image alphaExist:(BOOL)alphaExist { + self = [self init]; + if (self) { + CGImageToMat(image, self.nativeRef, (bool)alphaExist); + } + return self; +} + +-(NSImage*)toNSImage { + return MatToNSImage(self.nativeRef); +} + +-(instancetype)initWithNSImage:(NSImage*)image { + return [self initWithNSImage:image alphaExist:NO]; +} + +-(instancetype)initWithNSImage:(NSImage*)image alphaExist:(BOOL)alphaExist { + self = [self init]; + if (self) { + NSImageToMat(image, self.nativeRef, (bool)alphaExist); + } + return self; +} + +@end diff --git a/modules/imgcodecs/src/apple_conversions.h b/modules/imgcodecs/src/apple_conversions.h new file mode 100644 index 0000000000..2762424379 --- /dev/null +++ b/modules/imgcodecs/src/apple_conversions.h @@ -0,0 +1,11 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#import +#import +#import +#include "opencv2/core.hpp" + +CV_EXPORTS CGImageRef MatToCGImage(const cv::Mat& image); +CV_EXPORTS void CGImageToMat(const CGImageRef image, cv::Mat& m, bool alphaExist); diff --git a/modules/imgcodecs/src/apple_conversions.mm b/modules/imgcodecs/src/apple_conversions.mm new file mode 100644 index 0000000000..6126039ce0 --- /dev/null +++ b/modules/imgcodecs/src/apple_conversions.mm @@ -0,0 +1,94 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "apple_conversions.h" +#include "precomp.hpp" + +CGImageRef MatToCGImage(const cv::Mat& image) { + NSData *data = [NSData dataWithBytes:image.data + length:image.step.p[0] * image.rows]; + + CGColorSpaceRef colorSpace; + + if (image.elemSize() == 1) { + colorSpace = CGColorSpaceCreateDeviceGray(); + } else { + colorSpace = CGColorSpaceCreateDeviceRGB(); + } + + CGDataProviderRef provider = + CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + + // Preserve alpha transparency, if exists + bool alpha = image.channels() == 4; + CGBitmapInfo bitmapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault; + + // Creating CGImage from cv::Mat + CGImageRef imageRef = CGImageCreate(image.cols, + image.rows, + 8 * image.elemSize1(), + 8 * image.elemSize(), + image.step.p[0], + colorSpace, + bitmapInfo, + provider, + NULL, + false, + kCGRenderingIntentDefault + ); + + CGDataProviderRelease(provider); + CGColorSpaceRelease(colorSpace); + + return imageRef; +} + +void CGImageToMat(const CGImageRef image, cv::Mat& m, bool alphaExist) { + CGColorSpaceRef colorSpace = CGImageGetColorSpace(image); + CGFloat cols = CGImageGetWidth(image), rows = CGImageGetHeight(image); + CGContextRef contextRef; + CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast; + if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelMonochrome) + { + m.create(rows, cols, CV_8UC1); // 8 bits per component, 1 channel + bitmapInfo = kCGImageAlphaNone; + if (!alphaExist) + bitmapInfo = kCGImageAlphaNone; + else + m = cv::Scalar(0); + contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8, + m.step[0], colorSpace, + bitmapInfo); + } + else if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelIndexed) + { + // CGBitmapContextCreate() does not support indexed color spaces. + colorSpace = CGColorSpaceCreateDeviceRGB(); + m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels + if (!alphaExist) + bitmapInfo = kCGImageAlphaNoneSkipLast | + kCGBitmapByteOrderDefault; + else + m = cv::Scalar(0); + contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8, + m.step[0], colorSpace, + bitmapInfo); + CGColorSpaceRelease(colorSpace); + } + else + { + m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels + if (!alphaExist) + bitmapInfo = kCGImageAlphaNoneSkipLast | + kCGBitmapByteOrderDefault; + else + m = cv::Scalar(0); + contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8, + m.step[0], colorSpace, + bitmapInfo); + } + CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), + image); + CGContextRelease(contextRef); +} diff --git a/modules/imgcodecs/src/ios_conversions.mm b/modules/imgcodecs/src/ios_conversions.mm index 53fb788d65..2aba323a2d 100644 --- a/modules/imgcodecs/src/ios_conversions.mm +++ b/modules/imgcodecs/src/ios_conversions.mm @@ -41,105 +41,23 @@ //M*/ #import -#import -#import -#import -#include "opencv2/core.hpp" -#include "precomp.hpp" +#include "apple_conversions.h" CV_EXPORTS UIImage* MatToUIImage(const cv::Mat& image); CV_EXPORTS void UIImageToMat(const UIImage* image, cv::Mat& m, bool alphaExist); UIImage* MatToUIImage(const cv::Mat& image) { - - NSData *data = [NSData dataWithBytes:image.data - length:image.step.p[0] * image.rows]; - - CGColorSpaceRef colorSpace; - - if (image.elemSize() == 1) { - colorSpace = CGColorSpaceCreateDeviceGray(); - } else { - colorSpace = CGColorSpaceCreateDeviceRGB(); - } - - CGDataProviderRef provider = - CGDataProviderCreateWithCFData((__bridge CFDataRef)data); - - // Preserve alpha transparency, if exists - bool alpha = image.channels() == 4; - CGBitmapInfo bitmapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault; - // Creating CGImage from cv::Mat - CGImageRef imageRef = CGImageCreate(image.cols, - image.rows, - 8 * image.elemSize1(), - 8 * image.elemSize(), - image.step.p[0], - colorSpace, - bitmapInfo, - provider, - NULL, - false, - kCGRenderingIntentDefault - ); - + CGImageRef imageRef = MatToCGImage(image); // Getting UIImage from CGImage - UIImage *finalImage = [UIImage imageWithCGImage:imageRef]; + UIImage *uiImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); - CGDataProviderRelease(provider); - CGColorSpaceRelease(colorSpace); - return finalImage; + return uiImage; } -void UIImageToMat(const UIImage* image, - cv::Mat& m, bool alphaExist) { - CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage); - CGFloat cols = CGImageGetWidth(image.CGImage), rows = CGImageGetHeight(image.CGImage); - CGContextRef contextRef; - CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast; - if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelMonochrome) - { - m.create(rows, cols, CV_8UC1); // 8 bits per component, 1 channel - bitmapInfo = kCGImageAlphaNone; - if (!alphaExist) - bitmapInfo = kCGImageAlphaNone; - else - m = cv::Scalar(0); - contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8, - m.step[0], colorSpace, - bitmapInfo); - } - else if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelIndexed) - { - // CGBitmapContextCreate() does not support indexed color spaces. - colorSpace = CGColorSpaceCreateDeviceRGB(); - m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels - if (!alphaExist) - bitmapInfo = kCGImageAlphaNoneSkipLast | - kCGBitmapByteOrderDefault; - else - m = cv::Scalar(0); - contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8, - m.step[0], colorSpace, - bitmapInfo); - CGColorSpaceRelease(colorSpace); - } - else - { - m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels - if (!alphaExist) - bitmapInfo = kCGImageAlphaNoneSkipLast | - kCGBitmapByteOrderDefault; - else - m = cv::Scalar(0); - contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8, - m.step[0], colorSpace, - bitmapInfo); - } - CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), - image.CGImage); - CGContextRelease(contextRef); +void UIImageToMat(const UIImage* image, cv::Mat& m, bool alphaExist) { + CGImageRef imageRef = image.CGImage; + CGImageToMat(imageRef, m, alphaExist); } diff --git a/modules/imgcodecs/src/macosx_conversions.mm b/modules/imgcodecs/src/macosx_conversions.mm new file mode 100644 index 0000000000..c1827e71f1 --- /dev/null +++ b/modules/imgcodecs/src/macosx_conversions.mm @@ -0,0 +1,25 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#import +#include "apple_conversions.h" + +CV_EXPORTS NSImage* MatToNSImage(const cv::Mat& image); +CV_EXPORTS void NSImageToMat(const NSImage* image, cv::Mat& m, bool alphaExist); + +NSImage* MatToNSImage(const cv::Mat& image) { + // Creating CGImage from cv::Mat + CGImageRef imageRef = MatToCGImage(image); + + // Getting NSImage from CGImage + NSImage *nsImage = [[NSImage alloc] initWithCGImage:imageRef size:CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef))]; + CGImageRelease(imageRef); + + return nsImage; +} + +void NSImageToMat(const NSImage* image, cv::Mat& m, bool alphaExist) { + CGImageRef imageRef = [image CGImageForProposedRect:NULL context:NULL hints:NULL]; + CGImageToMat(imageRef, m, alphaExist); +} diff --git a/modules/objc/generator/gen_objc.py b/modules/objc/generator/gen_objc.py index 1ae00ab5f1..8c4a5b2cac 100755 --- a/modules/objc/generator/gen_objc.py +++ b/modules/objc/generator/gen_objc.py @@ -1584,6 +1584,11 @@ if __name__ == "__main__": if os.path.exists(ios_files_dir): copied_files += copy_objc_files(ios_files_dir, objc_base_path, module, True) + if args.target == 'osx': + osx_files_dir = os.path.join(misc_location, 'macosx') + if os.path.exists(osx_files_dir): + copied_files += copy_objc_files(osx_files_dir, objc_base_path, module, True) + objc_test_files_dir = os.path.join(misc_location, 'test') if os.path.exists(objc_test_files_dir): copy_objc_files(objc_test_files_dir, objc_test_base_path, 'test', False)