diff --git a/.gitignore b/.gitignore index 8f9ef226cc..f34c2100f3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ bin/ build node_modules CMakeSettings.json +xcuserdata/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c0b3880fc..166257eca5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -452,6 +452,7 @@ OCV_OPTION(BUILD_FAT_JAVA_LIB "Create Java wrapper exporting all functions OCV_OPTION(BUILD_ANDROID_SERVICE "Build OpenCV Manager for Google Play" OFF IF ANDROID ) OCV_OPTION(BUILD_CUDA_STUBS "Build CUDA modules stubs when no CUDA SDK" OFF IF (NOT APPLE_FRAMEWORK) ) OCV_OPTION(BUILD_JAVA "Enable Java support" (ANDROID OR NOT CMAKE_CROSSCOMPILING) IF (ANDROID OR (NOT APPLE_FRAMEWORK AND NOT WINRT)) ) +OCV_OPTION(BUILD_OBJC "Enable Objective-C support" ON IF APPLE_FRAMEWORK ) # OpenCV installation options # =================================================== @@ -1595,6 +1596,12 @@ if(BUILD_JAVA) status(" Java tests:" BUILD_TESTS AND opencv_test_java_BINARY_DIR THEN YES ELSE NO) endif() +# ========================== Objective-C ======================= +if(BUILD_OBJC) + status("") + status(" Objective-C wrappers:" HAVE_opencv_objc THEN YES ELSE NO) +endif() + ocv_cmake_hook(STATUS_DUMP_EXTRA) # ========================== auxiliary ========================== diff --git a/cmake/OpenCVGenConfig.cmake b/cmake/OpenCVGenConfig.cmake index 699128990f..838852c4e7 100644 --- a/cmake/OpenCVGenConfig.cmake +++ b/cmake/OpenCVGenConfig.cmake @@ -30,6 +30,11 @@ if(BUILD_FAT_JAVA_LIB AND HAVE_opencv_java) list(APPEND OPENCV_MODULES_CONFIGCMAKE opencv_java) endif() +if(BUILD_OBJC AND HAVE_opencv_objc) + list(APPEND OPENCV_MODULES_CONFIGCMAKE opencv_objc) +endif() + + # ------------------------------------------------------------------------------------------- # Part 1/3: ${BIN_DIR}/OpenCVConfig.cmake -> For use *without* "make install" # ------------------------------------------------------------------------------------------- diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 90539573bf..c66855a239 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -788,6 +788,7 @@ macro(ocv_glob_module_sources) if (APPLE) file(GLOB_RECURSE lib_srcs_apple "${CMAKE_CURRENT_LIST_DIR}/src/*.mm" + "${CMAKE_CURRENT_LIST_DIR}/src/*.swift" ) list(APPEND lib_srcs ${lib_srcs_apple}) endif() diff --git a/modules/calib3d/CMakeLists.txt b/modules/calib3d/CMakeLists.txt index 9af6570b09..0953e3cd7e 100644 --- a/modules/calib3d/CMakeLists.txt +++ b/modules/calib3d/CMakeLists.txt @@ -7,5 +7,5 @@ if(DEBUG_opencv_calib3d) list(APPEND debug_modules opencv_highgui) endif() ocv_define_module(calib3d opencv_imgproc opencv_features2d opencv_flann ${debug_modules} - WRAP java python js + WRAP java objc python js ) diff --git a/modules/calib3d/misc/objc/gen_dict.json b/modules/calib3d/misc/objc/gen_dict.json new file mode 100644 index 0000000000..aeed9f5b44 --- /dev/null +++ b/modules/calib3d/misc/objc/gen_dict.json @@ -0,0 +1,5 @@ +{ + "func_arg_fix" : { + "findCirclesGrid" : { "blobDetector" : {"defval" : "cv::SimpleBlobDetector::create()"} } + } +} diff --git a/modules/calib3d/misc/objc/test/Calib3dTest.swift b/modules/calib3d/misc/objc/test/Calib3dTest.swift new file mode 100644 index 0000000000..85786e2fde --- /dev/null +++ b/modules/calib3d/misc/objc/test/Calib3dTest.swift @@ -0,0 +1,465 @@ +// +// Calib3dTest.swift +// +// Created by Giles Payne on 2020/05/26. +// + +import XCTest +import OpenCV + +class Calib3dTest: OpenCVTestCase { + + var size = Size() + + override func setUp() { + super.setUp() + size = Size(width: 3, height: 3) + } + + override func tearDown() { + super.tearDown() + } + + func testComposeRTMatMatMatMatMatMat() throws { + let rvec1 = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try rvec1.put(row: 0, col: 0, data: [0.5302828, 0.19925919, 0.40105945] as [Float]) + let tvec1 = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try tvec1.put(row: 0, col: 0, data: [0.81438506, 0.43713298, 0.2487897] as [Float]) + let rvec2 = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try rvec2.put(row: 0, col: 0, data: [0.77310503, 0.76209372, 0.30779448] as [Float]) + let tvec2 = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try tvec2.put(row: 0, col: 0, data: [0.70243168, 0.4784472, 0.79219002] as [Float]) + + let rvec3 = Mat() + let tvec3 = Mat() + + let outRvec = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try outRvec.put(row: 0, col: 0, data: [1.418641, 0.88665926, 0.56020796]) + let outTvec = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try outTvec.put(row: 0, col: 0, data: [1.4560841, 1.0680628, 0.81598103]) + + Calib3d.composeRT(rvec1: rvec1, tvec1: tvec1, rvec2: rvec2, tvec2: tvec2, rvec3: rvec3, tvec3: tvec3) + + try assertMatEqual(outRvec, rvec3, OpenCVTestCase.EPS) + try assertMatEqual(outTvec, tvec3, OpenCVTestCase.EPS) + } + + func testFilterSpecklesMatDoubleIntDouble() throws { + gray_16s_1024.copy(to: dst) + let center = Point(x: gray_16s_1024.rows() / 2, y: gray_16s_1024.cols() / 2) + Imgproc.circle(img: dst, center: center, radius: 1, color: Scalar.all(4096)) + + try assertMatNotEqual(gray_16s_1024, dst) + Calib3d.filterSpeckles(img: dst, newVal: 1024.0, maxSpeckleSize: 100, maxDiff: 0.0) + try assertMatEqual(gray_16s_1024, dst) + } + + func testFindChessboardCornersMatSizeMat() { + let patternSize = Size(width: 9, height: 6) + let corners = MatOfPoint2f() + Calib3d.findChessboardCorners(image: grayChess, patternSize: patternSize, corners: corners) + XCTAssertFalse(corners.empty()) + } + + func testFindChessboardCornersMatSizeMatInt() { + let patternSize = Size(width: 9, height: 6) + let corners = MatOfPoint2f() + Calib3d.findChessboardCorners(image: grayChess, patternSize: patternSize, corners: corners, flags: Calib3d.CALIB_CB_ADAPTIVE_THRESH + Calib3d.CALIB_CB_NORMALIZE_IMAGE + Calib3d.CALIB_CB_FAST_CHECK) + XCTAssertFalse(corners.empty()) + } + + func testFind4QuadCornerSubpix() { + let patternSize = Size(width: 9, height: 6) + let corners = MatOfPoint2f() + let region_size = Size(width: 5, height: 5) + Calib3d.findChessboardCorners(image: grayChess, patternSize: patternSize, corners: corners) + Calib3d.find4QuadCornerSubpix(img: grayChess, corners: corners, region_size: region_size) + XCTAssertFalse(corners.empty()) + } + + func testFindCirclesGridMatSizeMat() { + let size = 300 + let img = Mat(rows:Int32(size), cols:Int32(size), type:CvType.CV_8U) + img.setTo(scalar: Scalar(255)) + let centers = Mat() + + XCTAssertFalse(Calib3d.findCirclesGrid(image: img, patternSize: Size(width: 5, height: 5), centers: centers)) + + for i in 0..<5 { + for j in 0..<5 { + let x = Int32(size * (2 * i + 1) / 10) + let y = Int32(size * (2 * j + 1) / 10) + let pt = Point(x: x, y: y) + Imgproc.circle(img: img, center: pt, radius: 10, color: Scalar(0), thickness: -1) + } + } + + XCTAssert(Calib3d.findCirclesGrid(image: img, patternSize:Size(width:5, height:5), centers:centers)) + + XCTAssertEqual(25, centers.rows()) + XCTAssertEqual(1, centers.cols()) + XCTAssertEqual(CvType.CV_32FC2, centers.type()) + } + + func testFindCirclesGridMatSizeMatInt() { + let size:Int32 = 300 + let img = Mat(rows:size, cols: size, type: CvType.CV_8U) + img.setTo(scalar: Scalar(255)) + let centers = Mat() + + XCTAssertFalse(Calib3d.findCirclesGrid(image: img, patternSize: Size(width: 3, height: 5), centers: centers, flags: Calib3d.CALIB_CB_CLUSTERING | Calib3d.CALIB_CB_ASYMMETRIC_GRID)) + + let step = size * 2 / 15 + let offsetx = size / 6 + let offsety = (size - 4 * step) / 2 + for i:Int32 in 0...2 { + for j:Int32 in 0...4 { + let pt = Point(x: offsetx + (2 * i + j % 2) * step, y: offsety + step * j) + Imgproc.circle(img: img, center: pt, radius: 10, color: Scalar(0), thickness: -1) + } + } + + XCTAssert(Calib3d.findCirclesGrid(image: img, patternSize: Size(width: 3, height: 5), centers: centers, flags: Calib3d.CALIB_CB_CLUSTERING | Calib3d.CALIB_CB_ASYMMETRIC_GRID)) + + XCTAssertEqual(15, centers.rows()) + XCTAssertEqual(1, centers.cols()) + XCTAssertEqual(CvType.CV_32FC2, centers.type()) + } + + func testFindHomographyListOfPointListOfPoint() throws { + let NUM:Int32 = 20 + + let originalPoints = MatOfPoint2f() + originalPoints.alloc(NUM) + let transformedPoints = MatOfPoint2f() + transformedPoints.alloc(NUM) + + for i:Int32 in 0.. src, List dst, Mat cameraMatrix, Mat distCoeffs) + func testUndistortPointsListOfPointListOfPointMatMat() { + let src = MatOfPoint2f(array: [Point2f(x: 1, y: 2), Point2f(x: 3, y: 4), Point2f(x: -1, y: -1)]) + let dst = MatOfPoint2f() + let cameraMatrix = Mat.eye(rows: 3, cols: 3, type: CvType.CV_64FC1) + let distCoeffs = Mat(rows: 8, cols: 1, type: CvType.CV_64FC1, scalar: Scalar(0)) + + Calib3d.undistortPoints(src: src, dst: dst, cameraMatrix: cameraMatrix, distCoeffs: distCoeffs) + + XCTAssertEqual(src.toArray(), dst.toArray()) + } +} diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index ee2c4b4b43..59273a3a6b 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -19,7 +19,7 @@ ocv_add_dispatched_file_force_all(test_intrin512 TEST AVX512_SKX) ocv_add_module(core OPTIONAL opencv_cudev - WRAP java python js) + WRAP java objc python js) set(extra_libs "") diff --git a/modules/core/misc/objc/common/ArrayUtil.h b/modules/core/misc/objc/common/ArrayUtil.h new file mode 100644 index 0000000000..ceb0c997ee --- /dev/null +++ b/modules/core/misc/objc/common/ArrayUtil.h @@ -0,0 +1,16 @@ +// +// ArrayUtil.h +// +// Created by Giles Payne on 2020/02/09. +// + +#pragma once + +#import + +/** +* Utility function to create and populate an NSMutableArray with a specific size +* @param size Size of array to create +* @param val Value with which to initialize array elements +*/ +NSMutableArray* createArrayWithSize(int size, NSObject* val); diff --git a/modules/core/misc/objc/common/ArrayUtil.mm b/modules/core/misc/objc/common/ArrayUtil.mm new file mode 100644 index 0000000000..a18a8892fe --- /dev/null +++ b/modules/core/misc/objc/common/ArrayUtil.mm @@ -0,0 +1,15 @@ +// +// ArrayUtil.mm +// +// Created by Giles Payne on 2020/02/09. +// + +#import "ArrayUtil.h" + +NSMutableArray* createArrayWithSize(int size, NSObject* val) { + NSMutableArray *array = [NSMutableArray arrayWithCapacity:size]; + for (int i = 0; i < size; i++){ + [array addObject:val]; + } + return array; +} diff --git a/modules/core/misc/objc/common/ByteVector.h b/modules/core/misc/objc/common/ByteVector.h new file mode 100644 index 0000000000..e0cce73877 --- /dev/null +++ b/modules/core/misc/objc/common/ByteVector.h @@ -0,0 +1,88 @@ +// +// ByteVector.h +// +// Created by Giles Payne on 2020/01/04. +// + +#pragma once + +#import +#ifdef __cplusplus +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** +* Utility class to wrap a `std::vector` +*/ +@interface ByteVector : NSObject + +#pragma mark - Constructors + +/** +* Create ByteVector and initialize with the contents of an NSData object +* @param data NSData containing raw byte array +*/ +-(instancetype)initWithData:(NSData*)data; + +/** +* Create ByteVector and initialize with the contents of another ByteVector object +* @param src ByteVector containing data to copy +*/ +-(instancetype)initWithVector:(ByteVector*)src; + +#ifdef __OBJC__ +/** +* Create ByteVector from raw C array +* @param array The raw C array +* @elements elements The number of elements in the array +*/ +-(instancetype)initWithNativeArray:(char*)array elements:(NSInteger)elements; +#endif + +#ifdef __cplusplus +/** +* Create ByteVector from std::vector +* @param src The std::vector object to wrap +*/ +-(instancetype)initWithStdVector:(std::vector&)src; ++(instancetype)fromNative:(std::vector&)src; +#endif + +#pragma mark - Properties + +/** +* Length of the vector +*/ +@property(readonly) NSInteger length; + +#ifdef __OBJC__ +/** +* Raw C array +*/ +@property(readonly) char* nativeArray; +#endif + +#ifdef __cplusplus +/** +* The wrapped std::vector object +*/ +@property(readonly) std::vector& nativeRef; +#endif + +/** +* NSData object containing the raw byte data +*/ +@property(readonly) NSData* data; + +#pragma mark - Accessor method + +/** +* Return array element +* @param index Index of the array element to return +*/ +-(char)get:(NSInteger)index; + +@end +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/ByteVector.mm b/modules/core/misc/objc/common/ByteVector.mm new file mode 100644 index 0000000000..e1f5ae3dba --- /dev/null +++ b/modules/core/misc/objc/common/ByteVector.mm @@ -0,0 +1,76 @@ +// +// ByteVector.m +// +// Created by Giles Payne on 2020/01/04. +// + +#import "ByteVector.h" +#import + +@implementation ByteVector { + std::vector v; +} + +-(instancetype)initWithData:(NSData*)data { + self = [super init]; + if (self) { + if (data.length % sizeof(char) != 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Invalid data length" userInfo:nil]; + } + v.insert(v.begin(), (char*)data.bytes, (char*)data.bytes + data.length/sizeof(char)); + } + return self; +} + +-(instancetype)initWithVector:(ByteVector*)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.nativeRef.begin(), src.nativeRef.end()); + } + return self; +} + +-(NSInteger)length { + return v.size(); +} + +-(char*)nativeArray { + return (char*)v.data(); +} + +-(instancetype)initWithNativeArray:(char*)array elements:(NSInteger)elements { + self = [super init]; + if (self) { + v.insert(v.begin(), array, array + elements); + } + return self; +} + +- (std::vector&)nativeRef { + return v; +} + +-(instancetype)initWithStdVector:(std::vector&)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.begin(), src.end()); + } + return self; +} + ++(instancetype)fromNative:(std::vector&)src { + return [[ByteVector alloc] initWithStdVector:src]; +} + +-(char)get:(NSInteger)index { + if (index < 0 || index >= (long)v.size()) { + @throw [NSException exceptionWithName:NSRangeException reason:@"Invalid data length" userInfo:nil]; + } + return v[index]; +} + +-(NSData*)data { + return [NSData dataWithBytesNoCopy:v.data() length:(v.size() * sizeof(char)) freeWhenDone:NO]; +} + +@end diff --git a/modules/core/misc/objc/common/ByteVectorExt.swift b/modules/core/misc/objc/common/ByteVectorExt.swift new file mode 100644 index 0000000000..e553de0aa4 --- /dev/null +++ b/modules/core/misc/objc/common/ByteVectorExt.swift @@ -0,0 +1,53 @@ +// +// ByteVectorExt.swift +// +// Created by Giles Payne on 2020/01/04. +// + +import Foundation + +public extension ByteVector { + convenience init(_ array:[Int8]) { + let data = array.withUnsafeBufferPointer { Data(buffer: $0) } + self.init(data:data); + } + + subscript(index: Int) -> Int8 { + get { + return self.get(index) + } + } + + var array: [Int8] { + get { + var ret = Array(repeating: 0, count: data.count/MemoryLayout.stride) + _ = ret.withUnsafeMutableBytes { data.copyBytes(to: $0) } + return ret + } + } +} + +extension ByteVector : Sequence { + public typealias Iterator = ByteVectorIterator + public func makeIterator() -> ByteVectorIterator { + return ByteVectorIterator(self) + } +} + +public struct ByteVectorIterator: IteratorProtocol { + public typealias Element = Int8 + let byteVector: ByteVector + var pos = 0 + + init(_ byteVector: ByteVector) { + self.byteVector = byteVector + } + + mutating public func next() -> Int8? { + guard pos >= 0 && pos < byteVector.length + else { return nil } + + pos += 1 + return byteVector.get(pos - 1) + } +} diff --git a/modules/core/misc/objc/common/CVObjcUtil.h b/modules/core/misc/objc/common/CVObjcUtil.h new file mode 100644 index 0000000000..be28f75af7 --- /dev/null +++ b/modules/core/misc/objc/common/CVObjcUtil.h @@ -0,0 +1,85 @@ +// +// CVObjcUtil.h +// +// Created by Giles Payne on 2020/01/02. +// + +#pragma once + +typedef union { double d; int64_t l; } V64; +typedef union { float f; int32_t i; } V32; + +#define DOUBLE_TO_BITS(x) ((V64){ .d = x }).l +#define FLOAT_TO_BITS(x) ((V32){ .f = x }).i + +#ifdef __cplusplus +#import + +#define MAKE_PTR(t) (*((cv::Ptr*)self.nativePtr)) + +template std::vector objc2cv(NSArray* _Nonnull array, CV& (* _Nonnull converter)(OBJC* _Nonnull)) { + std::vector ret; + for (OBJC* obj in array) { + ret.push_back(converter(obj)); + } + return ret; +} + +#define OBJC2CV(CV_CLASS, OBJC_CLASS, v, a) \ + std::vector v = objc2cv(a, [](OBJC_CLASS* objc) -> CV_CLASS& { return objc.nativeRef; }) + +#define OBJC2CV_CUSTOM(CV_CLASS, OBJC_CLASS, v, a, CONV) \ + std::vector v; \ + for (OBJC_CLASS* obj in a) { \ + CV_CLASS tmp = CONV(obj); \ + v.push_back(tmp); \ + } + +template void cv2objc(std::vector& vector, NSMutableArray* _Nonnull array, OBJC* _Nonnull (* _Nonnull converter)(CV&)) { + [array removeAllObjects]; + for (size_t index = 0; index < vector.size(); index++) { + [array addObject:converter(vector[index])]; + } +} + +#define CV2OBJC(CV_CLASS, OBJC_CLASS, v, a) \ + cv2objc(v, a, [](CV_CLASS& cv) -> OBJC_CLASS* { return [OBJC_CLASS fromNative:cv]; }) + +#define CV2OBJC_CUSTOM(CV_CLASS, OBJC_CLASS, v, a, UNCONV) \ + [a removeAllObjects]; \ + for (size_t index = 0; index < v.size(); index++) { \ + OBJC_CLASS *tmp = UNCONV(v[index]); \ + [a addObject:tmp]; \ + } + +template std::vector> objc2cv2(NSArray*>* _Nonnull array, CV& (* _Nonnull converter)(OBJC* _Nonnull)) { + std::vector> ret; + for (NSArray* innerArray in array) { + std::vector innerVector; + for (OBJC* obj in innerArray) { + innerVector.push_back(converter(obj)); + } + ret.push_back(innerVector); + } + return ret; +} + +#define OBJC2CV2(CV_CLASS, OBJC_CLASS, v, a) \ + std::vector> v = objc2cv2(a, [](OBJC_CLASS* objc) -> CV_CLASS& { return objc.nativeRef; }) + +template void cv2objc2(std::vector>& vector, NSMutableArray*>* _Nonnull array, OBJC* _Nonnull (* _Nonnull converter)(CV&)) { + [array removeAllObjects]; + for (size_t index = 0; index < vector.size(); index++) { + std::vector& innerVector = vector[index]; + NSMutableArray* innerArray = [NSMutableArray arrayWithCapacity:innerVector.size()]; + for (size_t index2 = 0; index2 < innerVector.size(); index2++) { + [innerArray addObject:converter(innerVector[index2])]; + } + [array addObject:innerArray]; + } +} + +#define CV2OBJC2(CV_CLASS, OBJC_CLASS, v, a) \ + cv2objc2(v, a, [](CV_CLASS& cv) -> OBJC_CLASS* { return [OBJC_CLASS fromNative:cv]; }) + +#endif diff --git a/modules/core/misc/objc/common/Converters.h b/modules/core/misc/objc/common/Converters.h new file mode 100755 index 0000000000..c73c3fac76 --- /dev/null +++ b/modules/core/misc/objc/common/Converters.h @@ -0,0 +1,98 @@ +// +// Converters.h +// +// Created by Giles Payne on 2020/03/03. +// + +#pragma once + +#ifdef __cplusplus +#import +#endif + +#import +#import "Mat.h" +#import "CvType.h" +#import "Point2i.h" +#import "Point2f.h" +#import "Point2d.h" +#import "Point3i.h" +#import "Point3f.h" +#import "Point3d.h" +#import "Rect2i.h" +#import "Rect2d.h" +#import "KeyPoint.h" +#import "DMatch.h" +#import "RotatedRect.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface Converters : NSObject + ++ (Mat*)vector_Point_to_Mat:(NSArray*)pts NS_SWIFT_NAME(vector_Point_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Point:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Point(_:)); + ++ (Mat*)vector_Point2f_to_Mat:(NSArray*)pts NS_SWIFT_NAME(vector_Point2f_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Point2f:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Point2f(_:)); + ++ (Mat*)vector_Point2d_to_Mat:(NSArray*)pts NS_SWIFT_NAME(vector_Point2d_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Point2d:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Point2d(_:)); + ++ (Mat*)vector_Point3i_to_Mat:(NSArray*)pts NS_SWIFT_NAME(vector_Point3i_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Point3i:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Point3i(_:)); + ++ (Mat*)vector_Point3f_to_Mat:(NSArray*)pts NS_SWIFT_NAME(vector_Point3f_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Point3f:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Point3f(_:)); + ++ (Mat*)vector_Point3d_to_Mat:(NSArray*)pts NS_SWIFT_NAME(vector_Point3d_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Point3d:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Point3d(_:)); + ++ (Mat*)vector_float_to_Mat:(NSArray*)fs NS_SWIFT_NAME(vector_float_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_float:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_float(_:)); + ++ (Mat*)vector_uchar_to_Mat:(NSArray*)us NS_SWIFT_NAME(vector_uchar_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_uchar:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_uchar(_:)); + ++ (Mat*)vector_char_to_Mat:(NSArray*)cs NS_SWIFT_NAME(vector_char_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_char:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_char(_:)); + ++ (Mat*)vector_int_to_Mat:(NSArray*)is NS_SWIFT_NAME(vector_int_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_int:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_int(_:)); + ++ (Mat*)vector_Rect_to_Mat:(NSArray*)rs NS_SWIFT_NAME(vector_Rect_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Rect:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Rect(_:)); + ++ (Mat*)vector_Rect2d_to_Mat:(NSArray*)rs NS_SWIFT_NAME(vector_Rect2d_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_Rect2d:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_Rect2d(_:)); + ++ (Mat*)vector_KeyPoint_to_Mat:(NSArray*)kps NS_SWIFT_NAME(vector_KeyPoint_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_KeyPoint:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_KeyPoint(_:)); + ++ (Mat*)vector_double_to_Mat:(NSArray*)ds NS_SWIFT_NAME(vector_double_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_double:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_double(_:)); + ++ (Mat*)vector_DMatch_to_Mat:(NSArray*)matches NS_SWIFT_NAME(vector_DMatch_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_DMatch:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_DMatch(_:)); + ++ (Mat*)vector_RotatedRect_to_Mat:(NSArray*)rs NS_SWIFT_NAME(vector_RotatedRect_to_Mat(_:)); + ++ (NSArray*)Mat_to_vector_RotatedRect:(Mat*)mat NS_SWIFT_NAME(Mat_to_vector_RotatedRect(_:)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Converters.mm b/modules/core/misc/objc/common/Converters.mm new file mode 100644 index 0000000000..1260cda7e6 --- /dev/null +++ b/modules/core/misc/objc/common/Converters.mm @@ -0,0 +1,205 @@ +// +// Converters.mm +// +// Created by Giles Payne on 31/05/2020. +// + +#import "Converters.h" +#import "ArrayUtil.h" +#import "MatOfPoint2i.h" +#import "MatOfPoint2f.h" +#import "MatOfPoint3.h" +#import "MatOfPoint3f.h" +#import "MatOfFloat.h" +#import "MatOfByte.h" +#import "MatOfInt.h" +#import "MatOfDouble.h" +#import "MatOfRect2i.h" +#import "MatOfRect2d.h" +#import "MatOfKeyPoint.h" +#import "MatOfDMatch.h" +#import "MatOfRotatedRect.h" + +@implementation Converters + ++ (Mat*)vector_Point_to_Mat:(NSArray*)pts { + return [[MatOfPoint2i alloc] initWithArray:pts]; +} + ++ (NSArray*)Mat_to_vector_Point:(Mat*)mat { + return [[[MatOfPoint2i alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_Point2f_to_Mat:(NSArray*)pts { + return [[MatOfPoint2f alloc] initWithArray:pts]; +} + ++ (NSArray*)Mat_to_vector_Point2f:(Mat*)mat { + return [[[MatOfPoint2f alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_Point2d_to_Mat:(NSArray*)pts { + Mat* res = [[Mat alloc] initWithRows:(int)pts.count cols:1 type:CV_64FC2]; + NSMutableArray* buff = [NSMutableArray arrayWithCapacity:pts.count*2]; + for (Point2d* pt in pts) { + [buff addObject:[NSNumber numberWithDouble:pt.x]]; + [buff addObject:[NSNumber numberWithDouble:pt.y]]; + } + [res put:0 col:0 data:buff]; + return res; +} + ++ (NSArray*)Mat_to_vector_Point2d:(Mat*)mat { + if (mat.cols != 1 || mat.type != CV_64FC2) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid Mat. Mat must be of type CV_64FC2 and have 1 column."] + userInfo:nil]; + @throw exception; + } + NSMutableArray* ret = [NSMutableArray new]; + NSMutableArray* buff = createArrayWithSize(mat.rows*2, [NSNumber numberWithInt:0]); + [mat get:0 col:0 data:buff]; + for (int i = 0; i < mat.rows; i++) { + [ret addObject:[[Point2d alloc] initWithX:buff[i * 2].doubleValue y:buff[i * 2 + 1].doubleValue]]; + } + return ret; +} + ++ (Mat*)vector_Point3i_to_Mat:(NSArray*)pts { + return [[MatOfPoint3 alloc] initWithArray:pts]; +} + ++ (NSArray*)Mat_to_vector_Point3i:(Mat*)mat { + return [[[MatOfPoint3 alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_Point3f_to_Mat:(NSArray*)pts { + return [[MatOfPoint3f alloc] initWithArray:pts]; +} + ++ (NSArray*)Mat_to_vector_Point3f:(Mat*)mat { + return [[[MatOfPoint3f alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_Point3d_to_Mat:(NSArray*)pts { + Mat* res = [[Mat alloc] initWithRows:(int)pts.count cols:1 type:CV_64FC3]; + NSMutableArray* buff = [NSMutableArray arrayWithCapacity:pts.count*3]; + for (Point3d* pt in pts) { + [buff addObject:[NSNumber numberWithDouble:pt.x]]; + [buff addObject:[NSNumber numberWithDouble:pt.y]]; + [buff addObject:[NSNumber numberWithDouble:pt.z]]; + } + [res put:0 col:0 data:buff]; + return res; +} + ++ (NSArray*)Mat_to_vector_Point3d:(Mat*)mat { + if (mat.cols != 1 || mat.type != CV_64FC3) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid Mat. Mat must be of type CV_64FC3 and have 1 column."] + userInfo:nil]; + @throw exception; + } + NSMutableArray* ret = [NSMutableArray new]; + NSMutableArray* buff = createArrayWithSize(mat.rows*3, [NSNumber numberWithInt:0]); + [mat get:0 col:0 data:buff]; + for (int i = 0; i < mat.rows; i++) { + [ret addObject:[[Point3d alloc] initWithX:buff[i * 3].doubleValue y:buff[i * 3 + 1].doubleValue z:buff[i * 3 + 2].doubleValue]]; + } + return ret; +} + ++ (Mat*)vector_float_to_Mat:(NSArray*)fs { + return [[MatOfFloat alloc] initWithArray:fs]; +} + ++ (NSArray*)Mat_to_vector_float:(Mat*)mat { + return [[[MatOfFloat alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_uchar_to_Mat:(NSArray*)us { + return [[MatOfByte alloc] initWithArray:us]; +} + ++ (NSArray*)Mat_to_vector_uchar:(Mat*)mat { + return [[[MatOfByte alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_char_to_Mat:(NSArray*)cs { + Mat* res = [[Mat alloc] initWithRows:(int)cs.count cols:1 type:CV_8S]; + [res put:0 col:0 data:cs]; + return res; +} + ++ (NSArray*)Mat_to_vector_char:(Mat*)mat { + if (mat.cols != 1 || mat.type != CV_8S) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid Mat. Mat must be of type CV_8S and have 1 column."] + userInfo:nil]; + @throw exception; + } + NSMutableArray* ret = createArrayWithSize(mat.rows, @0); + [mat get:0 col:0 data:ret]; + return ret; +} + ++ (Mat*)vector_int_to_Mat:(NSArray*)is { + return [[MatOfInt alloc] initWithArray:is]; +} + ++ (NSArray*)Mat_to_vector_int:(Mat*)mat { + return [[[MatOfInt alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_Rect_to_Mat:(NSArray*)rs { + return [[MatOfRect2i alloc] initWithArray:rs]; +} + ++ (NSArray*)Mat_to_vector_Rect:(Mat*)mat { + return [[[MatOfRect2i alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_Rect2d_to_Mat:(NSArray*)rs { + return [[MatOfRect2d alloc] initWithArray:rs]; +} + ++ (NSArray*)Mat_to_vector_Rect2d:(Mat*)mat { + return [[[MatOfRect2d alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_KeyPoint_to_Mat:(NSArray*)kps { + return [[MatOfKeyPoint alloc] initWithArray:kps]; +} + ++ (NSArray*)Mat_to_vector_KeyPoint:(Mat*)mat { + return [[[MatOfKeyPoint alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_double_to_Mat:(NSArray*)ds { + return [[MatOfDouble alloc] initWithArray:ds]; +} + ++ (NSArray*)Mat_to_vector_double:(Mat*)mat { + return [[[MatOfDouble alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_DMatch_to_Mat:(NSArray*)matches { + return [[MatOfDMatch alloc] initWithArray:matches]; +} + ++ (NSArray*)Mat_to_vector_DMatch:(Mat*)mat { + return [[[MatOfDMatch alloc] initWithMat:mat] toArray]; +} + ++ (Mat*)vector_RotatedRect_to_Mat:(NSArray*)rs { + return [[MatOfRotatedRect alloc] initWithArray:rs]; +} + ++ (NSArray*)Mat_to_vector_RotatedRect:(Mat*)mat { + return [[[MatOfRotatedRect alloc] initWithMat:mat] toArray]; +} + +@end diff --git a/modules/core/misc/objc/common/CvType.h b/modules/core/misc/objc/common/CvType.h new file mode 100644 index 0000000000..15d1598da0 --- /dev/null +++ b/modules/core/misc/objc/common/CvType.h @@ -0,0 +1,67 @@ +// +// CvType.h +// +// Created by Giles Payne on 2019/10/13. +// + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Utility functions for handling CvType values +*/ +@interface CvType : NSObject + +#pragma mark - Type Utility functions + +/** +* Create CvType value from depth and channel values +* @param depth Depth value. One of CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F or CV_64F +* @param channels Number of channels (from 1 to (CV_CN_MAX - 1)) +*/ ++ (int)makeType:(int)depth channels:(int)channels; + +/** +* Get number of channels for type +* @param type Type value +*/ ++ (int)channels:(int)type; + +/** +* Get depth for type +* @param type Type value +*/ ++ (int)depth:(int)type; + +/** +* Get raw type size in bytes for type +* @param type Type value +*/ ++ (int)rawTypeSize:(int)type; + +/** +* Returns true if the raw type is an integer type (if depth is CV_8U, CV_8S, CV_16U, CV_16S or CV_32S) +* @param type Type value +*/ ++ (BOOL)isInteger:(int)type; + +/** +* Get element size in bytes for type +* @param type Type value +*/ ++ (int)ELEM_SIZE:(int)type NS_SWIFT_NAME(elemSize(_:)); + +/** +* Get the string name for type +* @param type Type value +*/ ++ (NSString*)typeToString:(int)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/CvType.mm b/modules/core/misc/objc/common/CvType.mm new file mode 100644 index 0000000000..0b8ee34540 --- /dev/null +++ b/modules/core/misc/objc/common/CvType.mm @@ -0,0 +1,105 @@ +// +// CvType.m +// +// Created by Giles Payne on 2019/10/13. +// + +#import "CvType.h" + +@implementation CvType + ++ (int)makeType:(int)depth channels:(int)channels { + if (channels <= 0 || channels >= CV_CN_MAX) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Channels count should be 1..%d", CV_CN_MAX - 1] + userInfo:nil]; + @throw exception; + } + if (depth < 0 || depth >= CV_DEPTH_MAX) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Data type depth should be 0..%d", CV_DEPTH_MAX - 1] + userInfo:nil]; + @throw exception; + } + return (depth & (CV_DEPTH_MAX - 1)) + ((channels - 1) << CV_CN_SHIFT); +} + ++ (int)channels:(int)type { + return (type >> CV_CN_SHIFT) + 1; +} + ++ (int)depth:(int)type { + return type & (CV_DEPTH_MAX - 1); +} + ++ (BOOL)isInteger:(int)type { + return [CvType depth:type] < CV_32F; +} + ++ (int)typeSizeBits:(int)type { + int depth = [CvType depth:type]; + switch (depth) { + case CV_8U: + case CV_8S: + return 8; + case CV_16U: + case CV_16S: + case CV_16F: + return 16; + case CV_32S: + case CV_32F: + return 32; + case CV_64F: + return 64; + default: + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Unsupported CvType value: %d", type] + userInfo:nil]; + @throw exception; + } +} + ++ (int)rawTypeSize:(int)type { + return [CvType typeSizeBits:type] >> 3; +} + ++ (char)typeMnenomic:(int)type { + int depth = [CvType depth:type]; + switch (depth) { + case CV_8U: + case CV_16U: + return 'U'; + case CV_8S: + case CV_16S: + case CV_32S: + return 'S'; + case CV_16F: + case CV_32F: + case CV_64F: + return 'F'; + default: + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Unsupported CvType value: %d", type] + userInfo:nil]; + @throw exception; + } +} + ++ (int)ELEM_SIZE:(int)type { + int typeSizeBytes = [CvType rawTypeSize:type]; + return typeSizeBytes * [CvType channels:type]; +} + ++ (NSString*)typeToString:(int)type { + int typeSizeBits = [CvType typeSizeBits:type]; + char typeMnenomic = [CvType typeMnenomic:type]; + int channels = [CvType channels:type]; + NSString* channelsSuffix = [NSString stringWithFormat:(channels <= 4)?@"%d":@"(%d)", channels]; + return [NSString stringWithFormat:@"CV_%d%cC%@", typeSizeBits, typeMnenomic, channelsSuffix]; +} + +@end diff --git a/modules/core/misc/objc/common/CvTypeExt.swift b/modules/core/misc/objc/common/CvTypeExt.swift new file mode 100644 index 0000000000..de065ea2de --- /dev/null +++ b/modules/core/misc/objc/common/CvTypeExt.swift @@ -0,0 +1,90 @@ +// +// CvTypeExt.swift +// +// Created by Giles Payne on 2020/01/19. +// + +import Foundation + +public extension CvType { + static let CV_8U: Int32 = 0 + static let CV_8S: Int32 = 1 + static let CV_16U: Int32 = 2 + static let CV_16S: Int32 = 3 + static let CV_32S: Int32 = 4 + static let CV_32F: Int32 = 5 + static let CV_64F: Int32 = 6 + static let CV_16F: Int32 = 7 + + static let CV_8UC1: Int32 = CV_8UC(1) + static let CV_8UC2: Int32 = CV_8UC(2) + static let CV_8UC3: Int32 = CV_8UC(3) + static let CV_8UC4: Int32 = CV_8UC(4) + static let CV_8SC1: Int32 = CV_8SC(1) + static let CV_8SC2: Int32 = CV_8SC(2) + static let CV_8SC3: Int32 = CV_8SC(3) + static let CV_8SC4: Int32 = CV_8SC(4) + + static let CV_16UC1: Int32 = CV_16UC(1) + static let CV_16UC2: Int32 = CV_16UC(2) + static let CV_16UC3: Int32 = CV_16UC(3) + static let CV_16UC4: Int32 = CV_16UC(4) + static let CV_16SC1: Int32 = CV_16SC(1) + static let CV_16SC2: Int32 = CV_16SC(2) + static let CV_16SC3: Int32 = CV_16SC(3) + static let CV_16SC4: Int32 = CV_16SC(4) + + static let CV_32SC1: Int32 = CV_32SC(1) + static let CV_32SC2: Int32 = CV_32SC(2) + static let CV_32SC3: Int32 = CV_32SC(3) + static let CV_32SC4: Int32 = CV_32SC(4) + static let CV_32FC1: Int32 = CV_32FC(1) + static let CV_32FC2: Int32 = CV_32FC(2) + static let CV_32FC3: Int32 = CV_32FC(3) + static let CV_32FC4: Int32 = CV_32FC(4) + + static let CV_64FC1: Int32 = CV_64FC(1) + static let CV_64FC2: Int32 = CV_64FC(2) + static let CV_64FC3: Int32 = CV_64FC(3) + static let CV_64FC4: Int32 = CV_64FC(4) + static let CV_16FC1: Int32 = CV_16FC(1) + static let CV_16FC2: Int32 = CV_16FC(2) + static let CV_16FC3: Int32 = CV_16FC(3) + static let CV_16FC4: Int32 = CV_16FC(4) + + static let CV_CN_MAX = 512 + static let CV_CN_SHIFT = 3 + static let CV_DEPTH_MAX = 1 << CV_CN_SHIFT + + static func CV_8UC(_ channels:Int32) -> Int32 { + return make(CV_8U, channels: channels) + } + + static func CV_8SC(_ channels:Int32) -> Int32 { + return make(CV_8S, channels: channels) + } + + static func CV_16UC(_ channels:Int32) -> Int32 { + return make(CV_16U, channels: channels) + } + + static func CV_16SC(_ channels:Int32) -> Int32 { + return make(CV_16S, channels: channels) + } + + static func CV_32SC(_ channels:Int32) -> Int32 { + return make(CV_32S, channels: channels) + } + + static func CV_32FC(_ channels:Int32) -> Int32 { + return make(CV_32F, channels: channels) + } + + static func CV_64FC(_ channels:Int32) -> Int32 { + return make(CV_64F, channels: channels) + } + + static func CV_16FC(_ channels:Int32) -> Int32 { + return make(CV_16F, channels: channels) + } +} diff --git a/modules/core/misc/objc/common/DMatch.h b/modules/core/misc/objc/common/DMatch.h new file mode 100644 index 0000000000..79d9f557d1 --- /dev/null +++ b/modules/core/misc/objc/common/DMatch.h @@ -0,0 +1,82 @@ +// +// DMatch.h +// +// Created by Giles Payne on 2019/12/25. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Structure for matching: query descriptor index, train descriptor index, train +* image index and distance between descriptors. +*/ +@interface DMatch : NSObject + +/** + * Query descriptor index. + */ +@property int queryIdx; + +/** +* Train descriptor index. +*/ +@property int trainIdx; + +/** +* Train image index. +*/ +@property int imgIdx; + +/** +* Distance +*/ +@property float distance; +#ifdef __cplusplus +@property(readonly) cv::DMatch& nativeRef; +#endif + +- (instancetype)init; +- (instancetype)initWithQueryIdx:(int)queryIdx trainIdx:(int)trainIdx distance:(float)distance; +- (instancetype)initWithQueryIdx:(int)queryIdx trainIdx:(int)trainIdx imgIdx:(int)imgIdx distance:(float)distance; +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::DMatch&)dMatch; +#endif + +/** +* Distance comparison +* @param it DMatch object to compare +*/ +- (BOOL)lessThan:(DMatch*)it; + +/** +* Clone object +*/ +- (DMatch*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/DMatch.mm b/modules/core/misc/objc/common/DMatch.mm new file mode 100644 index 0000000000..7d74f254ad --- /dev/null +++ b/modules/core/misc/objc/common/DMatch.mm @@ -0,0 +1,107 @@ +// +// DMatch.m +// +// Created by Giles Payne on 2019/12/25. +// + +#import "DMatch.h" +#import "CVObjcUtil.h" + +@implementation DMatch { + cv::DMatch native; +} + +- (int)queryIdx { + return native.queryIdx; +} + +- (void)setQueryIdx:(int)queryIdx { + native.queryIdx = queryIdx; +} + +- (int)trainIdx { + return native.trainIdx; +} + +- (void)setTrainIdx:(int)trainIdx { + native.trainIdx = trainIdx; +} + +- (int)imgIdx { + return native.imgIdx; +} + +- (void)setImgIdx:(int)imgIdx { + native.imgIdx = imgIdx; +} + +- (float)distance { + return native.distance; +} + +- (void)setDistance:(float)distance { + native.distance = distance; +} + +- (cv::DMatch&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithQueryIdx:-1 trainIdx:-1 distance:FLT_MAX]; +} + +- (instancetype)initWithQueryIdx:(int)queryIdx trainIdx:(int)trainIdx distance:(float)distance { + return [self initWithQueryIdx:queryIdx trainIdx:trainIdx imgIdx:-1 distance:distance]; +} + +- (instancetype)initWithQueryIdx:(int)queryIdx trainIdx:(int)trainIdx imgIdx:(int)imgIdx distance:(float)distance { + self = [super init]; + if (self != nil) { + self.queryIdx = queryIdx; + self.trainIdx = trainIdx; + self.imgIdx = imgIdx; + self.distance = distance; + } + return self; +} + ++ (instancetype)fromNative:(cv::DMatch&)dMatch { + return [[DMatch alloc] initWithQueryIdx:dMatch.queryIdx trainIdx:dMatch.trainIdx imgIdx:dMatch.imgIdx distance:dMatch.distance]; +} + +- (BOOL)lessThan:(DMatch*)it { + return self.distance < it.distance; +} + + +- (DMatch*)clone { + return [[DMatch alloc] initWithQueryIdx:self.queryIdx trainIdx:self.trainIdx imgIdx:self.imgIdx distance:self.distance]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[DMatch class]]) { + return NO; + } else { + DMatch* dMatch = (DMatch*)other; + return self.queryIdx == dMatch.queryIdx && self.trainIdx == dMatch.trainIdx && self.imgIdx == dMatch.imgIdx && self.distance == dMatch.distance; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + self.queryIdx; + result = prime * result + self.trainIdx; + result = prime * result + self.imgIdx; + result = prime * result + FLOAT_TO_BITS(self.distance); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"DMatch { queryIdx: %d, trainIdx: %d, imgIdx: %d, distance: %f}", self.queryIdx, self.trainIdx, self.imgIdx, self.distance]; +} + +@end diff --git a/modules/core/misc/objc/common/Double2.h b/modules/core/misc/objc/common/Double2.h new file mode 100644 index 0000000000..7512e37ee2 --- /dev/null +++ b/modules/core/misc/objc/common/Double2.h @@ -0,0 +1,93 @@ +// +// Double2.h +// +// Created by Giles Payne on 2020/05/22. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Mat; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Simple wrapper for a vector of two `double` +*/ +@interface Double2 : NSObject + +#pragma mark - Properties + +/** +* First vector element +*/ +@property double v0; + +/** +* Second vector element +*/ +@property double v1; + +/** +* Third vector element +*/ +@property double v2; + + +#ifdef __cplusplus +/** +* The wrapped vector +*/ +@property(readonly) cv::Vec2d& nativeRef; +#endif + +#pragma mark - Constructors + +/** +* Create zero-initialize vecior +*/ +-(instancetype)init; + +/** +* Create vector with specified element values +* @param v0 First element +* @param v1 Second element +*/ +-(instancetype)initWithV0:(double)v0 v1:(double)v1; + +/** +* Create vector with specified element values +* @param vals array of element values +*/ +-(instancetype)initWithVals:(NSArray*)vals; +#ifdef __cplusplus ++(instancetype)fromNative:(cv::Vec2d&)vec2d; +#endif + +/** +* Update vector with specified element values +* @param vals array of element values +*/ +-(void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +/** +* Get vector as an array +*/ +-(NSArray*)get; + +#pragma mark - Common Methods + +/** +* Compare for equality +* @param other Object to compare +*/ +-(BOOL)isEqual:(nullable id)other; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Double2.mm b/modules/core/misc/objc/common/Double2.mm new file mode 100644 index 0000000000..92ea02187f --- /dev/null +++ b/modules/core/misc/objc/common/Double2.mm @@ -0,0 +1,75 @@ +// +// Double2.mm +// +// Created by Giles Payne on 2020/05/22. +// + +#import "Double2.h" +#import "Mat.h" + +@implementation Double2 { + cv::Vec2d native; +} + +-(double)v0 { + return native[0]; +} + +-(void)setV0:(double)v { + native[0] = v; +} + +-(double)v1 { + return native[1]; +} + +-(void)setV1:(double)v { + native[1] = v; +} + +-(instancetype)init { + return [self initWithV0:0 v1:0]; +} + +-(instancetype)initWithV0:(double)v0 v1:(double)v1 { + self = [super init]; + if (self) { + self.v0 = v0; + self.v1 = v1; + } + return self; +} + +-(instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++(instancetype)fromNative:(cv::Vec2d&)vec2d { + return [[Double2 alloc] initWithV0:vec2d[0] v1:vec2d[1]]; +} + +-(void)set:(NSArray*)vals { + self.v0 = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0; + self.v1 = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0; +} + +-(NSArray*)get { + return @[[NSNumber numberWithFloat:native[0]], [NSNumber numberWithFloat:native[1]]]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Double2 class]]) { + return NO; + } else { + Double2* d2 = (Double2*)other; + return self.v0 == d2.v0 && self.v1 == d2.v1; + } +} + +@end diff --git a/modules/core/misc/objc/common/Double3.h b/modules/core/misc/objc/common/Double3.h new file mode 100644 index 0000000000..dc361bbc25 --- /dev/null +++ b/modules/core/misc/objc/common/Double3.h @@ -0,0 +1,94 @@ +// +// Double3.h +// +// Created by Giles Payne on 2020/05/22. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Mat; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Simple wrapper for a vector of three `double` +*/ +@interface Double3 : NSObject + +#pragma mark - Properties + +/** +* First vector element +*/ +@property double v0; + +/** +* Second vector element +*/ +@property double v1; + +/** +* Third vector element +*/ +@property double v2; + + +#ifdef __cplusplus +/** +* The wrapped vector +*/ +@property(readonly) cv::Vec3d& nativeRef; +#endif + +#pragma mark - Constructors + +/** +* Create zero-initialize vecior +*/ +-(instancetype)init; + +/** +* Create vector with specified element values +* @param v0 First element +* @param v1 Second element +* @param v2 Third element +*/ +-(instancetype)initWithV0:(double)v0 v1:(double)v1 v2:(double)v2; + +/** +* Create vector with specified element values +* @param vals array of element values +*/ +-(instancetype)initWithVals:(NSArray*)vals; +#ifdef __cplusplus ++(instancetype)fromNative:(cv::Vec3d&)vec3d; +#endif + +/** +* Update vector with specified element values +* @param vals array of element values +*/ +-(void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +/** +* Get vector as an array +*/ +-(NSArray*)get; + +#pragma mark - Common Methods + +/** +* Compare for equality +* @param other Object to compare +*/ +-(BOOL)isEqual:(nullable id)other; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Double3.mm b/modules/core/misc/objc/common/Double3.mm new file mode 100644 index 0000000000..b4c3cf0a20 --- /dev/null +++ b/modules/core/misc/objc/common/Double3.mm @@ -0,0 +1,85 @@ +// +// Double3.mm +// +// Created by Giles Payne on 2020/05/22. +// + +#import "Double3.h" +#import "Mat.h" + +@implementation Double3 { + cv::Vec3d native; +} + +-(double)v0 { + return native[0]; +} + +-(void)setV0:(double)v { + native[0] = v; +} + +-(double)v1 { + return native[1]; +} + +-(void)setV1:(double)v { + native[1] = v; +} + +-(double)v2 { + return native[2]; +} + +-(void)setV2:(double)v { + native[2] = v; +} + +-(instancetype)init { + return [self initWithV0:0 v1:0 v2:0]; +} + +-(instancetype)initWithV0:(double)v0 v1:(double)v1 v2:(double)v2 { + self = [super init]; + if (self) { + self.v0 = v0; + self.v1 = v1; + self.v2 = v2; + } + return self; +} + +-(instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++(instancetype)fromNative:(cv::Vec3d&)vec3d { + return [[Double3 alloc] initWithV0:vec3d[0] v1:vec3d[1] v2:vec3d[2]]; +} + +-(void)set:(NSArray*)vals { + self.v0 = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0; + self.v1 = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0; + self.v2 = (vals != nil && vals.count > 2) ? vals[2].doubleValue : 0; +} + +-(NSArray*)get { + return @[[NSNumber numberWithFloat:native[0]], [NSNumber numberWithFloat:native[1]], [NSNumber numberWithFloat:native[2]]]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Double3 class]]) { + return NO; + } else { + Double3* d3 = (Double3*)other; + return self.v0 == d3.v0 && self.v1 == d3.v1 && self.v2 == d3.v2; + } +} + +@end diff --git a/modules/core/misc/objc/common/DoubleVector.h b/modules/core/misc/objc/common/DoubleVector.h new file mode 100644 index 0000000000..6f338199c9 --- /dev/null +++ b/modules/core/misc/objc/common/DoubleVector.h @@ -0,0 +1,88 @@ +// +// DoubleVector.h +// +// Created by Giles Payne on 2020/01/04. +// + +#pragma once + +#import +#ifdef __cplusplus +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** +* Utility class to wrap a `std::vector` +*/ +@interface DoubleVector : NSObject + +#pragma mark - Constructors + +/** +* Create DoubleVector and initialize with the contents of an NSData object +* @param data NSData containing raw double array +*/ +-(instancetype)initWithData:(NSData*)data; + +/** +* Create DoubleVector and initialize with the contents of another DoubleVector object +* @param src DoubleVector containing data to copy +*/ +-(instancetype)initWithVector:(DoubleVector*)src; + +#ifdef __OBJC__ +/** +* Create DoubleVector from raw C array +* @param array The raw C array +* @elements elements The number of elements in the array +*/ +-(instancetype)initWithNativeArray:(double*)array elements:(int)elements; +#endif + +#ifdef __cplusplus +/** +* Create DoubleVector from std::vector +* @param src The std::vector object to wrap +*/ +-(instancetype)initWithStdVector:(std::vector&)src; ++(instancetype)fromNative:(std::vector&)src; +#endif + +#pragma mark - Properties + +/** +* Length of the vector +*/ +@property(readonly) size_t length; + +#ifdef __OBJC__ +/** +* Raw C array +*/ +@property(readonly) double* nativeArray; +#endif + +#ifdef __cplusplus +/** +* The wrapped std::vector object +*/ +@property(readonly) std::vector& nativeRef; +#endif + +/** +* NSData object containing the raw double data +*/ +@property(readonly) NSData* data; + +#pragma mark - Accessor method + +/** +* Return array element +* @param index Index of the array element to return +*/ +-(double)get:(NSInteger)index; + +@end +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/DoubleVector.mm b/modules/core/misc/objc/common/DoubleVector.mm new file mode 100644 index 0000000000..861e08d449 --- /dev/null +++ b/modules/core/misc/objc/common/DoubleVector.mm @@ -0,0 +1,76 @@ +// +// DoubleVector.m +// +// Created by Giles Payne on 2020/01/04. +// + +#import "DoubleVector.h" +#import + +@implementation DoubleVector { + std::vector v; +} + +-(instancetype)initWithData:(NSData*)data { + self = [super init]; + if (self) { + if (data.length % sizeof(double) != 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Invalid data length" userInfo:nil]; + } + v.insert(v.begin(), (double*)data.bytes, (double*)data.bytes + data.length/sizeof(double)); + } + return self; +} + +-(instancetype)initWithVector:(DoubleVector*)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.nativeRef.begin(), src.nativeRef.end()); + } + return self; +} + +-(size_t)length { + return v.size(); +} + +-(double*)nativeArray { + return (double*)v.data(); +} + +-(instancetype)initWithNativeArray:(double*)array elements:(int)elements { + self = [super init]; + if (self) { + v.insert(v.begin(), array, array + elements); + } + return self; +} + +- (std::vector&)nativeRef { + return v; +} + +-(instancetype)initWithStdVector:(std::vector&)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.begin(), src.end()); + } + return self; +} + ++(instancetype)fromNative:(std::vector&)src { + return [[DoubleVector alloc] initWithStdVector:src]; +} + +-(double)get:(NSInteger)index { + if (index < 0 || index >= (long)v.size()) { + @throw [NSException exceptionWithName:NSRangeException reason:@"Invalid data length" userInfo:nil]; + } + return v[index]; +} + +-(NSData*)data { + return [NSData dataWithBytesNoCopy:v.data() length:(v.size() * sizeof(double)) freeWhenDone:NO]; +} + +@end diff --git a/modules/core/misc/objc/common/DoubleVectorExt.swift b/modules/core/misc/objc/common/DoubleVectorExt.swift new file mode 100644 index 0000000000..61fad46a29 --- /dev/null +++ b/modules/core/misc/objc/common/DoubleVectorExt.swift @@ -0,0 +1,53 @@ +// +// DoubleVectorExt.swift +// +// Created by Giles Payne on 2020/01/04. +// + +import Foundation + +public extension DoubleVector { + convenience init(_ array:[Double]) { + let data = array.withUnsafeBufferPointer { Data(buffer: $0) } + self.init(data:data); + } + + subscript(index: Int) -> Double { + get { + return self.get(index) + } + } + + var array: [Double] { + get { + var ret = Array(repeating: 0, count: data.count/MemoryLayout.stride) + _ = ret.withUnsafeMutableBytes { data.copyBytes(to: $0) } + return ret + } + } +} + +extension DoubleVector : Sequence { + public typealias Iterator = DoubleVectorIterator + public func makeIterator() -> DoubleVectorIterator { + return DoubleVectorIterator(self) + } +} + +public struct DoubleVectorIterator: IteratorProtocol { + public typealias Element = Double + let doubleVector: DoubleVector + var pos = 0 + + init(_ doubleVector: DoubleVector) { + self.doubleVector = doubleVector + } + + mutating public func next() -> Double? { + guard pos >= 0 && pos < doubleVector.length + else { return nil } + + pos += 1 + return doubleVector.get(pos - 1) + } +} diff --git a/modules/core/misc/objc/common/Float4.h b/modules/core/misc/objc/common/Float4.h new file mode 100644 index 0000000000..d0c52dccfe --- /dev/null +++ b/modules/core/misc/objc/common/Float4.h @@ -0,0 +1,99 @@ +// +// Float4.h +// +// Created by Giles Payne on 2020/02/05. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class Mat; + +/** +* Simple wrapper for a vector of four `float` +*/ +@interface Float4 : NSObject + +#pragma mark - Properties + +/** +* First vector element +*/ +@property float v0; + +/** +* Second vector element +*/ +@property float v1; + +/** +* Third vector element +*/ +@property float v2; + +/** +* Fourth vector element +*/ +@property float v3; + +#ifdef __cplusplus +/** +* The wrapped vector +*/ +@property(readonly) cv::Vec4f& nativeRef; +#endif + +#pragma mark - Constructors + +/** +* Create zero-initialize vecior +*/ +-(instancetype)init; + +/** +* Create vector with specified element values +* @param v0 First element +* @param v1 Second element +* @param v2 Third element +* @param v3 Fourth element +*/ +-(instancetype)initWithV0:(float)v0 v1:(float)v1 v2:(float)v2 v3:(float)v3; + +/** +* Create vector with specified element values +* @param vals array of element values +*/ +-(instancetype)initWithVals:(NSArray*)vals; +#ifdef __cplusplus ++(instancetype)fromNative:(cv::Vec4f&)vec4f; +#endif + +/** +* Update vector with specified element values +* @param vals array of element values +*/ +-(void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +/** +* Get vector as an array +*/ +-(NSArray*)get; + +#pragma mark - Common Methods + +/** +* Compare for equality +* @param other Object to compare +*/ +-(BOOL)isEqual:(nullable id)other; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Float4.mm b/modules/core/misc/objc/common/Float4.mm new file mode 100644 index 0000000000..9b3f1c4a86 --- /dev/null +++ b/modules/core/misc/objc/common/Float4.mm @@ -0,0 +1,95 @@ +// +// Float4.mm +// +// Created by Giles Payne on 2020/02/05. +// + +#import "Float4.h" +#import "Mat.h" + +@implementation Float4 { + cv::Vec4f native; +} + +-(float)v0 { + return native[0]; +} + +-(void)setV0:(float)v { + native[0] = v; +} + +-(float)v1 { + return native[1]; +} + +-(void)setV1:(float)v { + native[1] = v; +} + +-(float)v2 { + return native[2]; +} + +-(void)setV2:(float)v { + native[2] = v; +} + +-(float)v3 { + return native[3]; +} + +-(void)setV3:(float)v { + native[3] = v; +} + +-(instancetype)init { + return [self initWithV0:0.0 v1:0.0 v2:0.0 v3:0.0]; +} + +-(instancetype)initWithV0:(float)v0 v1:(float)v1 v2:(float)v2 v3:(float)v3 { + self = [super init]; + if (self) { + self.v0 = v0; + self.v1 = v1; + self.v2 = v2; + self.v3 = v3; + } + return self; +} + +-(instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++(instancetype)fromNative:(cv::Vec4f&)vec4f { + return [[Float4 alloc] initWithV0:vec4f[0] v1:vec4f[1] v2:vec4f[2] v3:vec4f[3]]; +} + +-(void)set:(NSArray*)vals { + self.v0 = (vals != nil && vals.count > 0) ? vals[0].floatValue : 0; + self.v1 = (vals != nil && vals.count > 1) ? vals[1].floatValue : 0; + self.v2 = (vals != nil && vals.count > 2) ? vals[2].floatValue : 0; + self.v3 = (vals != nil && vals.count > 3) ? vals[3].floatValue : 0; +} + +-(NSArray*)get { + return @[[NSNumber numberWithFloat:native[0]], [NSNumber numberWithFloat:native[1]], [NSNumber numberWithFloat:native[2]], [NSNumber numberWithFloat:native[3]]]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Float4 class]]) { + return NO; + } else { + Float4* point = (Float4*)other; + return self.v0 == point.v0 && self.v1 == point.v1 && self.v2 == point.v2 && self.v3 == point.v3; + } +} + +@end diff --git a/modules/core/misc/objc/common/Float6.h b/modules/core/misc/objc/common/Float6.h new file mode 100644 index 0000000000..c1c0ddd510 --- /dev/null +++ b/modules/core/misc/objc/common/Float6.h @@ -0,0 +1,112 @@ +// +// Float6.h +// +// Created by Giles Payne on 2020/02/05. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Mat; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Simple wrapper for a vector of six `float` +*/ +@interface Float6 : NSObject + +#pragma mark - Properties + +/** +* First vector element +*/ +@property float v0; + +/** +* Second vector element +*/ +@property float v1; + +/** +* Third vector element +*/ +@property float v2; + +/** +* Fourth vector element +*/ +@property float v3; + +/** +* Fifth vector element +*/ +@property float v4; + +/** +* Sixth vector element +*/ +@property float v5; + +#ifdef __cplusplus +/** +* The wrapped vector +*/ +@property(readonly) cv::Vec6f& nativeRef; +#endif + +#pragma mark - Constructors + +/** +* Create zero-initialize vecior +*/ +-(instancetype)init; + +/** +* Create vector with specified element values +* @param v0 First element +* @param v1 Second element +* @param v2 Third element +* @param v3 Fourth element +* @param v4 Fifth element +* @param v5 Sixth element +*/ +-(instancetype)initWithV0:(float)v0 v1:(float)v1 v2:(float)v2 v3:(float)v3 v4:(float)v4 v5:(float)v5; + +/** +* Create vector with specified element values +* @param vals array of element values +*/ +-(instancetype)initWithVals:(NSArray*)vals; +#ifdef __cplusplus ++(instancetype)fromNative:(cv::Vec6f&)vec6f; +#endif + +/** +* Update vector with specified element values +* @param vals array of element values +*/ +-(void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +/** +* Get vector as an array +*/ +-(NSArray*)get; + + +#pragma mark - Common Methods + +/** +* Compare for equality +* @param other Object to compare +*/ +-(BOOL)isEqual:(nullable id)other; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Float6.mm b/modules/core/misc/objc/common/Float6.mm new file mode 100644 index 0000000000..3d9ff803f4 --- /dev/null +++ b/modules/core/misc/objc/common/Float6.mm @@ -0,0 +1,115 @@ +// +// Float6.mm +// +// Created by Giles Payne on 2020/02/05. +// + +#import "Float6.h" +#import "Mat.h" + +@implementation Float6 { + cv::Vec6f native; +} + +-(float)v0 { + return native[0]; +} + +-(void)setV0:(float)v { + native[0] = v; +} + +-(float)v1 { + return native[1]; +} + +-(void)setV1:(float)v { + native[1] = v; +} + +-(float)v2 { + return native[2]; +} + +-(void)setV2:(float)v { + native[2] = v; +} + +-(float)v3 { + return native[3]; +} + +-(void)setV3:(float)v { + native[3] = v; +} + +-(float)v4 { + return native[4]; +} + +-(void)setV4:(float)v { + native[4] = v; +} + +-(float)v5 { + return native[5]; +} + +-(void)setV5:(float)v { + native[5] = v; +} + +-(instancetype)init { + return [self initWithV0:0.0 v1:0.0 v2:0.0 v3:0.0 v4:0.0 v5:0.0]; +} + +-(instancetype)initWithV0:(float)v0 v1:(float)v1 v2:(float)v2 v3:(float)v3 v4:(float)v4 v5:(float)v5 { + self = [super init]; + if (self) { + self.v0 = v0; + self.v1 = v1; + self.v2 = v2; + self.v3 = v3; + self.v4 = v4; + self.v5 = v5; + } + return self; +} + +-(instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++(instancetype)fromNative:(cv::Vec6f&)vec6f { + return [[Float6 alloc] initWithV0:vec6f[0] v1:vec6f[1] v2:vec6f[2] v3:vec6f[3] v4:vec6f[4] v5:vec6f[5]]; +} + +-(void)set:(NSArray*)vals { + self.v0 = (vals != nil && vals.count > 0) ? vals[0].floatValue : 0.0; + self.v1 = (vals != nil && vals.count > 1) ? vals[1].floatValue : 0.0; + self.v2 = (vals != nil && vals.count > 2) ? vals[2].floatValue : 0.0; + self.v3 = (vals != nil && vals.count > 3) ? vals[3].floatValue : 0.0; + self.v4 = (vals != nil && vals.count > 4) ? vals[4].floatValue : 0.0; + self.v5 = (vals != nil && vals.count > 5) ? vals[5].floatValue : 0.0; +} + +-(NSArray*)get { + return @[[NSNumber numberWithFloat:native[0]], [NSNumber numberWithFloat:native[1]], [NSNumber numberWithFloat:native[2]], [NSNumber numberWithFloat:native[3]]]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Float6 class]]) { + return NO; + } else { + Float6* point = (Float6*)other; + return self.v0 == point.v0 && self.v1 == point.v1 && self.v2 == point.v2 && self.v3 == point.v3 && self.v4 == point.v4 && self.v5 == point.v5; + } +} + +@end diff --git a/modules/core/misc/objc/common/FloatVector.h b/modules/core/misc/objc/common/FloatVector.h new file mode 100644 index 0000000000..9aa25927c7 --- /dev/null +++ b/modules/core/misc/objc/common/FloatVector.h @@ -0,0 +1,88 @@ +// +// FloatVector.h +// +// Created by Giles Payne on 2020/01/04. +// + +#pragma once + +#import +#ifdef __cplusplus +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** +* Utility class to wrap a `std::vector` +*/ +@interface FloatVector : NSObject + +#pragma mark - Constructors + +/** +* Create FloatVector and initialize with the contents of an NSData object +* @param data NSData containing raw float array +*/ +-(instancetype)initWithData:(NSData*)data; + +/** +* Create FloatVector and initialize with the contents of another FloatVector object +* @param src FloatVector containing data to copy +*/ +-(instancetype)initWithVector:(FloatVector*)src; + +#ifdef __OBJC__ +/** +* Create FloatVector from raw C array +* @param array The raw C array +* @elements elements The number of elements in the array +*/ +-(instancetype)initWithNativeArray:(float*)array elements:(NSInteger)elements; +#endif + +#ifdef __cplusplus +/** +* Create FloatVector from std::vector +* @param src The std::vector object to wrap +*/ +-(instancetype)initWithStdVector:(std::vector&)src; ++(instancetype)fromNative:(std::vector&)src; +#endif + +#pragma mark - Properties + +/** +* Length of the vector +*/ +@property(readonly) NSInteger length; + +#ifdef __OBJC__ +/** +* Raw C array +*/ +@property(readonly) float* nativeArray; +#endif + +#ifdef __cplusplus +/** +* The wrapped std::vector object +*/ +@property(readonly) std::vector& nativeRef; +#endif + +/** +* NSData object containing the raw float data +*/ +@property(readonly) NSData* data; + +#pragma mark - Accessor method + +/** +* Return array element +* @param index Index of the array element to return +*/ +-(float)get:(NSInteger)index; + +@end +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/FloatVector.mm b/modules/core/misc/objc/common/FloatVector.mm new file mode 100644 index 0000000000..626c465b9f --- /dev/null +++ b/modules/core/misc/objc/common/FloatVector.mm @@ -0,0 +1,76 @@ +// +// FloatVector.m +// +// Created by Giles Payne on 2020/01/04. +// + +#import "FloatVector.h" +#import + +@implementation FloatVector { + std::vector v; +} + +-(instancetype)initWithData:(NSData*)data { + self = [super init]; + if (self) { + if (data.length % sizeof(float) != 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Invalid data length" userInfo:nil]; + } + v.insert(v.begin(), (float*)data.bytes, (float*)data.bytes + data.length/sizeof(float)); + } + return self; +} + +-(instancetype)initWithVector:(FloatVector *)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.nativeRef.begin(), src.nativeRef.end()); + } + return self; +} + +-(NSInteger)length { + return v.size(); +} + +-(float*)nativeArray { + return (float*)v.data(); +} + +-(instancetype)initWithNativeArray:(float*)array elements:(NSInteger)elements { + self = [super init]; + if (self) { + v.insert(v.begin(), array, array + elements); + } + return self; +} + +- (std::vector&)nativeRef { + return v; +} + +-(instancetype)initWithStdVector:(std::vector&)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.begin(), src.end()); + } + return self; +} + ++(instancetype)fromNative:(std::vector&)src { + return [[FloatVector alloc] initWithStdVector:src]; +} + +-(float)get:(NSInteger)index { + if (index < 0 || index >= (long)v.size()) { + @throw [NSException exceptionWithName:NSRangeException reason:@"Invalid data length" userInfo:nil]; + } + return v[index]; +} + +-(NSData*)data { + return [NSData dataWithBytesNoCopy:v.data() length:(v.size() * sizeof(float)) freeWhenDone:NO]; +} + +@end diff --git a/modules/core/misc/objc/common/FloatVectorExt.swift b/modules/core/misc/objc/common/FloatVectorExt.swift new file mode 100644 index 0000000000..9dbe8a58eb --- /dev/null +++ b/modules/core/misc/objc/common/FloatVectorExt.swift @@ -0,0 +1,53 @@ +// +// FloatVectorExt.swift +// +// Created by Giles Payne on 2020/01/04. +// + +import Foundation + +public extension FloatVector { + convenience init(_ array:[Float]) { + let data = array.withUnsafeBufferPointer { Data(buffer: $0) } + self.init(data:data); + } + + subscript(index: Int) -> Float { + get { + return self.get(index) + } + } + + var array: [Float] { + get { + var ret = Array(repeating: 0, count: data.count/MemoryLayout.stride) + _ = ret.withUnsafeMutableBytes { data.copyBytes(to: $0) } + return ret + } + } +} + +extension FloatVector : Sequence { + public typealias Iterator = FloatVectorIterator + public func makeIterator() -> FloatVectorIterator { + return FloatVectorIterator(self) + } +} + +public struct FloatVectorIterator: IteratorProtocol { + public typealias Element = Float + let floatVector: FloatVector + var pos = 0 + + init(_ floatVector: FloatVector) { + self.floatVector = floatVector + } + + mutating public func next() -> Float? { + guard pos >= 0 && pos < floatVector.length + else { return nil } + + pos += 1 + return floatVector.get(pos - 1) + } +} diff --git a/modules/core/misc/objc/common/Int4.h b/modules/core/misc/objc/common/Int4.h new file mode 100644 index 0000000000..583feef0ad --- /dev/null +++ b/modules/core/misc/objc/common/Int4.h @@ -0,0 +1,99 @@ +// +// Int4.h +// +// Created by Giles Payne on 2020/02/05. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Mat; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Simple wrapper for a vector of four `int` +*/ +@interface Int4 : NSObject + +#pragma mark - Properties + +/** +* First vector element +*/ +@property int v0; + +/** +* Second vector element +*/ +@property int v1; + +/** +* Third vector element +*/ +@property int v2; + +/** +* Fourth vector element +*/ +@property int v3; + +#ifdef __cplusplus +/** +* The wrapped vector +*/ +@property(readonly) cv::Vec4i& nativeRef; +#endif + +#pragma mark - Constructors + +/** +* Create zero-initialize vecior +*/ +-(instancetype)init; + +/** +* Create vector with specified element values +* @param v0 First element +* @param v1 Second element +* @param v2 Third element +* @param v3 Fourth element +*/ +-(instancetype)initWithV0:(int)v0 v1:(int)v1 v2:(int)v2 v3:(int)v3; + +/** +* Create vector with specified element values +* @param vals array of element values +*/ +-(instancetype)initWithVals:(NSArray*)vals; +#ifdef __cplusplus ++(instancetype)fromNative:(cv::Vec4i&)vec4i; +#endif + +/** +* Update vector with specified element values +* @param vals array of element values +*/ +-(void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +/** +* Get vector as an array +*/ +-(NSArray*)get; + +#pragma mark - Common Methods + +/** +* Compare for equality +* @param other Object to compare +*/ +-(BOOL)isEqual:(nullable id)other; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Int4.mm b/modules/core/misc/objc/common/Int4.mm new file mode 100644 index 0000000000..d3a2a3a4c9 --- /dev/null +++ b/modules/core/misc/objc/common/Int4.mm @@ -0,0 +1,95 @@ +// +// Int4.mm +// +// Created by Giles Payne on 2020/02/05. +// + +#import "Int4.h" +#import "Mat.h" + +@implementation Int4 { + cv::Vec4i native; +} + +-(int)v0 { + return native[0]; +} + +-(void)setV0:(int)v { + native[0] = v; +} + +-(int)v1 { + return native[1]; +} + +-(void)setV1:(int)v { + native[1] = v; +} + +-(int)v2 { + return native[2]; +} + +-(void)setV2:(int)v { + native[2] = v; +} + +-(int)v3 { + return native[3]; +} + +-(void)setV3:(int)v { + native[3] = v; +} + +-(instancetype)init { + return [self initWithV0:0 v1:0 v2:0 v3:0]; +} + +-(instancetype)initWithV0:(int)v0 v1:(int)v1 v2:(int)v2 v3:(int)v3 { + self = [super init]; + if (self) { + self.v0 = v0; + self.v1 = v1; + self.v2 = v2; + self.v3 = v3; + } + return self; +} + +-(instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++(instancetype)fromNative:(cv::Vec4i&)vec4i { + return [[Int4 alloc] initWithV0:vec4i[0] v1:vec4i[1] v2:vec4i[2] v3:vec4i[3]]; +} + +-(void)set:(NSArray*)vals { + self.v0 = (vals != nil && vals.count > 0) ? vals[0].intValue : 0; + self.v1 = (vals != nil && vals.count > 1) ? vals[1].intValue : 0; + self.v2 = (vals != nil && vals.count > 2) ? vals[2].intValue : 0; + self.v3 = (vals != nil && vals.count > 3) ? vals[3].intValue : 0; +} + +-(NSArray*)get { + return @[[NSNumber numberWithFloat:native[0]], [NSNumber numberWithFloat:native[1]], [NSNumber numberWithFloat:native[2]], [NSNumber numberWithFloat:native[3]]]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Int4 class]]) { + return NO; + } else { + Int4* point = (Int4*)other; + return self.v0 == point.v0 && self.v1 == point.v1 && self.v2 == point.v2 && self.v3 == point.v3; + } +} + +@end diff --git a/modules/core/misc/objc/common/IntVector.h b/modules/core/misc/objc/common/IntVector.h new file mode 100644 index 0000000000..e8c4acc45f --- /dev/null +++ b/modules/core/misc/objc/common/IntVector.h @@ -0,0 +1,88 @@ +// +// IntVector.h +// +// Created by Giles Payne on 2020/01/04. +// + +#pragma once + +#import +#ifdef __cplusplus +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** +* Utility class to wrap a `std::vector` +*/ +@interface IntVector : NSObject + +#pragma mark - Constructors + +/** +* Create IntVector and initialize with the contents of an NSData object +* @param data NSData containing raw int array +*/ +-(instancetype)initWithData:(NSData*)data; + +/** +* Create IntVector and initialize with the contents of another IntVector object +* @param src IntVector containing data to copy +*/ +-(instancetype)initWithVector:(IntVector*)src; + +#ifdef __OBJC__ +/** +* Create IntVector from raw C array +* @param array The raw C array +* @elements elements The number of elements in the array +*/ +-(instancetype)initWithNativeArray:(int*)array elements:(NSInteger)elements; +#endif + +#ifdef __cplusplus +/** +* Create IntVector from std::vector +* @param src The std::vector object to wrap +*/ +-(instancetype)initWithStdVector:(std::vector&)src; ++(instancetype)fromNative:(std::vector&)src; +#endif + +#pragma mark - Properties + +/** +* Length of the vector +*/ +@property(readonly) NSInteger length; + +#ifdef __OBJC__ +/** +* Raw C array +*/ +@property(readonly) int* nativeArray; +#endif + +#ifdef __cplusplus +/** +* The wrapped std::vector object +*/ +@property(readonly) std::vector& nativeRef; +#endif + +/** +* NSData object containing the raw int data +*/ +@property(readonly) NSData* data; + +#pragma mark - Accessor method + +/** +* Return array element +* @param index Index of the array element to return +*/ +-(int)get:(NSInteger)index; + +@end +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/IntVector.mm b/modules/core/misc/objc/common/IntVector.mm new file mode 100644 index 0000000000..112a5dcbc1 --- /dev/null +++ b/modules/core/misc/objc/common/IntVector.mm @@ -0,0 +1,76 @@ +// +// IntVector.m +// +// Created by Giles Payne on 2020/01/04. +// + +#import "IntVector.h" +#import + +@implementation IntVector { + std::vector v; +} + +-(instancetype)initWithData:(NSData*)data { + self = [super init]; + if (self) { + if (data.length % sizeof(int) != 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Invalid data length" userInfo:nil]; + } + v.insert(v.begin(), (int*)data.bytes, (int*)data.bytes + data.length/sizeof(int)); + } + return self; +} + +-(instancetype)initWithVector:(IntVector*)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.nativeRef.begin(), src.nativeRef.end()); + } + return self; +} + +-(NSInteger)length { + return v.size(); +} + +-(int*)nativeArray { + return (int*)v.data(); +} + +-(instancetype)initWithNativeArray:(int*)array elements:(NSInteger)elements { + self = [super init]; + if (self) { + v.insert(v.begin(), array, array + elements); + } + return self; +} + +- (std::vector&)nativeRef { + return v; +} + +-(instancetype)initWithStdVector:(std::vector&)src { + self = [super init]; + if (self) { + v.insert(v.begin(), src.begin(), src.end()); + } + return self; +} + ++(instancetype)fromNative:(std::vector&)src { + return [[IntVector alloc] initWithStdVector:src]; +} + +-(int)get:(NSInteger)index { + if (index < 0 || index >= (long)v.size()) { + @throw [NSException exceptionWithName:NSRangeException reason:@"Invalid data length" userInfo:nil]; + } + return v[index]; +} + +-(NSData*)data { + return [NSData dataWithBytesNoCopy:v.data() length:(v.size() * sizeof(int)) freeWhenDone:NO]; +} + +@end diff --git a/modules/core/misc/objc/common/IntVectorExt.swift b/modules/core/misc/objc/common/IntVectorExt.swift new file mode 100644 index 0000000000..4d5c53886a --- /dev/null +++ b/modules/core/misc/objc/common/IntVectorExt.swift @@ -0,0 +1,53 @@ +// +// IntVectorExt.swift +// +// Created by Giles Payne on 2020/01/04. +// + +import Foundation + +public extension IntVector { + convenience init(_ array:[Int32]) { + let data = array.withUnsafeBufferPointer { Data(buffer: $0) } + self.init(data:data); + } + + subscript(index: Int) -> Int32 { + get { + return self.get(index) + } + } + + var array: [Int32] { + get { + var ret = Array(repeating: 0, count: data.count/MemoryLayout.stride) + _ = ret.withUnsafeMutableBytes { data.copyBytes(to: $0) } + return ret + } + } +} + +extension IntVector : Sequence { + public typealias Iterator = IntVectorIterator + public func makeIterator() -> IntVectorIterator { + return IntVectorIterator(self) + } +} + +public struct IntVectorIterator: IteratorProtocol { + public typealias Element = Int32 + let intVector: IntVector + var pos = 0 + + init(_ intVector: IntVector) { + self.intVector = intVector + } + + mutating public func next() -> Int32? { + guard pos >= 0 && pos < intVector.length + else { return nil } + + pos += 1 + return intVector.get(pos - 1) + } +} diff --git a/modules/core/misc/objc/common/KeyPoint.h b/modules/core/misc/objc/common/KeyPoint.h new file mode 100644 index 0000000000..680aafa72e --- /dev/null +++ b/modules/core/misc/objc/common/KeyPoint.h @@ -0,0 +1,98 @@ +// +// KeyPoint.h +// +// Created by Giles Payne on 2019/10/08. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Point2f; + +NS_ASSUME_NONNULL_BEGIN +/** +* Object representing a point feature found by one of many available keypoint detectors, such as Harris corner detector, FAST, StarDetector, SURF, SIFT etc. +*/ +@interface KeyPoint : NSObject + +#pragma mark - Properties + +/** +* Coordinates of the keypoint. +*/ +@property Point2f* pt; + +/** +* Diameter of the useful keypoint adjacent area. +*/ +@property float size; + +/** +* Computed orientation of the keypoint (-1 if not applicable). +*/ +@property float angle; + +/** +* The response, by which the strongest keypoints have been selected. Can +* be used for further sorting or subsampling. +*/ +@property float response; + +/** +* Octave (pyramid layer), from which the keypoint has been extracted. +*/ +@property int octave; + +/** +* Object ID, that can be used to cluster keypoints by an object they +* belong to. +*/ +@property int classId; + +#ifdef __cplusplus +@property(readonly) cv::KeyPoint& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle response:(float)response octave:(int)octave classId:(int)classId; +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle response:(float)response octave:(int)octave; +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle response:(float)response; +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle; +- (instancetype)initWithX:(float)x y:(float)y size:(float)size; +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::KeyPoint&)keyPoint; +#endif + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (KeyPoint*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/KeyPoint.mm b/modules/core/misc/objc/common/KeyPoint.mm new file mode 100644 index 0000000000..8117910516 --- /dev/null +++ b/modules/core/misc/objc/common/KeyPoint.mm @@ -0,0 +1,95 @@ +// +// KeyPoint.m +// +// Created by Giles Payne on 2019/12/25. +// + +#import "KeyPoint.h" +#import "Point2f.h" +#import "CVObjcUtil.h" + +@implementation KeyPoint { + cv::KeyPoint native; +} + +- (cv::KeyPoint&)nativeRef { + native.pt.x = self.pt.x; + native.pt.y = self.pt.y; + native.size = self.size; + native.angle = self.angle; + native.response = self.response; + native.octave = self.octave; + native.class_id = self.classId; + return native; +} + +- (instancetype)init { + return [self initWithX:0 y:0 size:0]; +} + +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle response:(float)response octave:(int)octave classId:(int)classId { + self = [super init]; + if (self != nil) { + self.pt = [[Point2f alloc] initWithX:x y:y]; + self.size = size; + self.angle = angle; + self.response = response; + self.octave = octave; + self.classId = classId; + } + return self; +} + +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle response:(float)response octave:(int)octave { + return [self initWithX:x y:y size:size angle:angle response:response octave:octave classId:-1]; +} + +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle response:(float)response { + return [self initWithX:x y:y size:size angle:angle response:response octave:0]; +} + +- (instancetype)initWithX:(float)x y:(float)y size:(float)size angle:(float)angle { + return [self initWithX:x y:y size:size angle:angle response:0]; +} + +- (instancetype)initWithX:(float)x y:(float)y size:(float)size { + return [self initWithX:x y:y size:size angle:-1]; +} + ++ (instancetype)fromNative:(cv::KeyPoint&)keyPoint { + return [[KeyPoint alloc] initWithX:keyPoint.pt.x y:keyPoint.pt.y size:keyPoint.size angle:keyPoint.angle response:keyPoint.response octave:keyPoint.octave classId:keyPoint.class_id]; +} + +- (KeyPoint*)clone { + return [[KeyPoint alloc] initWithX:self.pt.x y:self.pt.y size:self.size angle:self.angle response:self.response octave:self.octave classId:self.classId]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[KeyPoint class]]) { + return NO; + } else { + KeyPoint* keyPoint = (KeyPoint*)other; + return [self.pt isEqual:keyPoint.pt] && self.size == keyPoint.size && self.angle == keyPoint.angle && self.response == keyPoint.response && self.octave == keyPoint.octave && self.classId == keyPoint.classId; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + FLOAT_TO_BITS(self.pt.x); + result = prime * result + FLOAT_TO_BITS(self.pt.y); + result = prime * result + FLOAT_TO_BITS(self.size); + result = prime * result + FLOAT_TO_BITS(self.angle); + result = prime * result + FLOAT_TO_BITS(self.response); + result = prime * result + self.octave; + result = prime * result + self.classId; + return result; +} + +- (NSString*)description { + return [NSString stringWithFormat:@"KeyPoint { pt: %@, size: %f, angle: %f, response: %f, octave: %d, classId: %d}", self.pt.description, self.size, self.angle, self.response, self.octave, self.classId]; +} + +@end diff --git a/modules/core/misc/objc/common/Mat.h b/modules/core/misc/objc/common/Mat.h new file mode 100644 index 0000000000..e1b249e87d --- /dev/null +++ b/modules/core/misc/objc/common/Mat.h @@ -0,0 +1,157 @@ +// +// Mat.h +// +// Created by Giles Payne on 2019/10/06. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Size2i; +@class Scalar; +@class Range; +@class Rect2i; +@class Point2i; + +NS_ASSUME_NONNULL_BEGIN + +/** +* The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. +*/ +@interface Mat : NSObject + +#ifdef __cplusplus +@property(readonly) cv::Mat* nativePtr; +@property(readonly) cv::Mat& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (void)dealloc; +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; ++ (instancetype)fromNativePtr:(cv::Mat*)nativePtr; ++ (instancetype)fromNative:(cv::Mat&)nativeRef; +#endif +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type; +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data; +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data step:(long)step; +- (instancetype)initWithSize:(Size2i*)size type:(int)type; +- (instancetype)initWithSizes:(NSArray*)sizes type:(int)type; +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type scalar:(Scalar*)scalar; +- (instancetype)initWithSize:(Size2i*)size type:(int)type scalar:(Scalar*)scalar; +- (instancetype)initWithSizes:(NSArray*)sizes type:(int)type scalar:(Scalar*)scalar; +- (instancetype)initWithMat:(Mat*)mat rowRange:(Range*)rowRange colRange:(Range*)colRange; +- (instancetype)initWithMat:(Mat*)mat rowRange:(Range*)rowRange; +- (instancetype)initWithMat:(Mat*)mat ranges:(NSArray*)ranges; +- (instancetype)initWithMat:(Mat*)mat rect:(Rect2i*)roi; + +#pragma mark - Mat operations + +- (Mat*)adjustRoiTop:(int)dtop bottom:(int)dbottom left:(int)dleft right:(int)dright NS_SWIFT_NAME(adjustRoi(top:bottom:left:right:)); +- (void)assignTo:(Mat*)mat type:(int)type; +- (void)assignTo:(Mat*)mat; +- (BOOL)isSameMat:(Mat*)mat; +- (int)channels; +- (int)checkVector:(int)elemChannels depth:(int)depth requireContinuous:(BOOL) requireContinuous NS_SWIFT_NAME(checkVector(elemChannels:depth:requireContinuous:)); +- (int)checkVector:(int)elemChannels depth:(int)depth NS_SWIFT_NAME(checkVector(elemChannels:depth:)); +- (int)checkVector:(int)elemChannels NS_SWIFT_NAME(checkVector(elemChannels:)); +- (Mat*)clone; +- (Mat*)col:(int)x; +- (Mat*)colRange:(int)start end:(int)end NS_SWIFT_NAME(colRange(start:end:)); +- (Mat*)colRange:(Range*)range; +- (int)dims; +- (int)cols; +- (void)convertTo:(Mat*)mat rtype:(int)rtype alpha:(double)alpha beta:(double)beta; +- (void)convertTo:(Mat*)mat rtype:(int)rtype alpha:(double)alpha; +- (void)convertTo:(Mat*)mat rtype:(int)rtype; +- (void)copyTo:(Mat*)mat; +- (void)copyTo:(Mat*)mat mask:(Mat*)mask; +- (void)create:(int)rows cols:(int)cols type:(int)type NS_SWIFT_NAME(create(rows:cols:type:)); +- (void)create:(Size2i*)size type:(int)type NS_SWIFT_NAME(create(size:type:)); +- (void)createEx:(NSArray*)sizes type:(int)type NS_SWIFT_NAME(create(sizes:type:)); +- (void)copySize:(Mat*)mat; +- (Mat*)cross:(Mat*)mat; +- (int)depth; +- (Mat*)diag:(int)diagonal; +- (Mat*)diag; ++ (Mat*)diag:(Mat*)diagonal; +- (double)dot:(Mat*)mat; +- (long)elemSize; +- (long)elemSize1; +- (BOOL)empty; ++ (Mat*)eye:(int)rows cols:(int)cols type:(int)type NS_SWIFT_NAME(eye(rows:cols:type:)); ++ (Mat*)eye:(Size2i*)size type:(int)type NS_SWIFT_NAME(eye(size:type:)); +- (Mat*)inv:(int)method; +- (Mat*)inv; +- (BOOL)isContinuous; +- (BOOL)isSubmatrix; +- (void)locateROI:(Size2i*)wholeSize ofs:(Point2i*)offset NS_SWIFT_NAME(locateROI(wholeSize:offset:)); +- (Mat*)mul:(Mat*)mat scale:(double)scale; +- (Mat*)mul:(Mat*)mat; ++ (Mat*)ones:(int)rows cols:(int)cols type:(int)type NS_SWIFT_NAME(ones(rows:cols:type:)); ++ (Mat*)ones:(Size2i*)size type:(int)type NS_SWIFT_NAME(ones(size:type:)); ++ (Mat*)onesEx:(NSArray*)sizes type:(int)type NS_SWIFT_NAME(ones(sizes:type:)); +- (void)push_back:(Mat*)mat; +- (Mat*)reshape:(int)channels rows:(int)rows NS_SWIFT_NAME(reshape(channels:rows:)); +- (Mat*)reshape:(int)channels NS_SWIFT_NAME(reshape(channels:)); +- (Mat*)reshape:(int)channels newshape:(NSArray*)newshape NS_SWIFT_NAME(reshape(channels:newshape:)); +- (Mat*)row:(int)y; +- (Mat*)rowRange:(int)start end:(int)end NS_SWIFT_NAME(rowRange(start:end:)); +- (Mat*)rowRange:(Range*)range; +- (int)rows; +- (Mat*)setToScalar:(Scalar*)scalar NS_SWIFT_NAME(setTo(scalar:)); +- (Mat*)setToScalar:(Scalar*)scalar mask:(Mat*)mask NS_SWIFT_NAME(setTo(scalar:mask:)); +- (Mat*)setToValue:(Mat*)value mask:(Mat*)mask NS_SWIFT_NAME(setTo(value:mask:)); +- (Mat*)setToValue:(Mat*)value NS_SWIFT_NAME(setTo(value:)); +- (Size2i*)size; +- (int)size:(int)dim; +- (long)step1:(int)dim; +- (long)step1; +- (Mat*)submat:(int)rowStart rowEnd:(int)rowEnd colStart:(int)colStart colEnd:(int)colEnd NS_SWIFT_NAME(submat(rowStart:rowEnd:colStart:colEnd:)); +- (Mat*)submat:(Range*)rowRange colRange:(Range*)colRange NS_SWIFT_NAME(submat(rowRange:colRange:)); +- (Mat*)submat:(NSArray*)ranges NS_SWIFT_NAME(submat(ranges:)); +- (Mat*)submatRoi:(Rect2i*)roi NS_SWIFT_NAME(submat(roi:)); +- (Mat*)t; +- (long)total; +- (int)type; ++ (Mat*)zeros:(int)rows cols:(int)cols type:(int)type; ++ (Mat*)zeros:(Size2i*)size type:(int)type; ++ (Mat*)zerosEx:(NSArray*)sizes type:(int)type NS_SWIFT_NAME(zeros(sizes:type:)); +- (NSString*)description; +- (NSString*)dump; +- (int)height; +- (int)width; + +#pragma mark - Accessors + +- (int)put:(int)row col:(int)col data:(NSArray*)data NS_REFINED_FOR_SWIFT; +- (int)put:(NSArray*)indices data:(NSArray*)data NS_REFINED_FOR_SWIFT; +- (int)get:(int)row col:(int)col data:(NSMutableArray*)data NS_REFINED_FOR_SWIFT; +- (int)get:(NSArray*)indices data:(NSMutableArray*)data NS_REFINED_FOR_SWIFT; + +- (NSArray*)get:(int)row col:(int)col NS_REFINED_FOR_SWIFT; +- (NSArray*)get:(NSArray*)indices NS_REFINED_FOR_SWIFT; + +- (int)get:(NSArray*)indices count:(int)count byteBuffer:(char*)buffer NS_REFINED_FOR_SWIFT; +- (int)get:(NSArray*)indices count:(int)count doubleBuffer:(double*)buffer NS_REFINED_FOR_SWIFT; +- (int)get:(NSArray*)indices count:(int)count floatBuffer:(float*)buffer NS_REFINED_FOR_SWIFT; +- (int)get:(NSArray*)indices count:(int)count intBuffer:(int*)buffer NS_REFINED_FOR_SWIFT; +- (int)get:(NSArray*)indices count:(int)count shortBuffer:(short*)buffer NS_REFINED_FOR_SWIFT; + +- (int)put:(NSArray*)indices count:(int)count byteBuffer:(const char*)buffer NS_REFINED_FOR_SWIFT; +- (int)put:(NSArray*)indices count:(int)count doubleBuffer:(const double*)buffer NS_REFINED_FOR_SWIFT; +- (int)put:(NSArray*)indices count:(int)count floatBuffer:(const float*)buffer NS_REFINED_FOR_SWIFT; +- (int)put:(NSArray*)indices count:(int)count intBuffer:(const int*)buffer NS_REFINED_FOR_SWIFT; +- (int)put:(NSArray*)indices count:(int)count shortBuffer:(const short*)buffer NS_REFINED_FOR_SWIFT; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Mat.mm b/modules/core/misc/objc/common/Mat.mm new file mode 100644 index 0000000000..c75b7d8a94 --- /dev/null +++ b/modules/core/misc/objc/common/Mat.mm @@ -0,0 +1,913 @@ +// +// Mat.m +// +// Created by Giles Payne on 2019/10/06. +// + +#import "Mat.h" +#import "Size2i.h" +#import "Scalar.h" +#import "Range.h" +#import "Rect2i.h" +#import "Point2i.h" +#import "CvType.h" +#import "CVObjcUtil.h" + +// return true if we have reached the final index +static bool incIdx(cv::Mat* mat, std::vector& indices) { + for (int dim = mat->dims-1; dim>=0; dim--) { + indices[dim] = (indices[dim] + 1) % mat->size[dim]; + if (indices[dim] != 0) { + return false; + } + } + return true; +} + +// returns true if final index was reached +static bool updateIdx(cv::Mat* mat, std::vector& indices, int inc) { + for (int index = 0; index < inc; index++) { + if (incIdx(mat, indices)) { + return true; + } + } + return false; +} + +@implementation Mat { + NSData* _nsdata; +} + +- (cv::Mat&)nativeRef { + return *(cv::Mat*)_nativePtr; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _nativePtr = new cv::Mat(); + } + return self; +} + +- (void)dealloc { + if (_nativePtr != NULL) { + _nativePtr->release(); + delete _nativePtr; + } + _nsdata = NULL; +} + +- (instancetype)initWithNativeMat:(cv::Mat*)nativePtr { + self = [super init]; + if (self) { + _nativePtr = new cv::Mat(*nativePtr); + } + return self; +} + ++ (instancetype)fromNativePtr:(cv::Mat*)nativePtr { + return [[Mat alloc] initWithNativeMat:nativePtr]; +} + ++ (instancetype)fromNative:(cv::Mat&)nativeRef { + return [[Mat alloc] initWithNativeMat:&nativeRef]; +} + +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type { + self = [super init]; + if (self) { + _nativePtr = new cv::Mat(rows, cols, type); + } + return self; +} + +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data { + self = [super init]; + if (self) { + _nativePtr = new cv::Mat(rows, cols, type, (void*)data.bytes); + _nsdata = data; // hold onto a reference otherwise this object might be deallocated + } + return self; +} + +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type data:(NSData*)data step:(long)step { + self = [super init]; + if (self) { + _nativePtr = new cv::Mat(rows, cols, type, (void*)data.bytes, step); + _nsdata = data; // hold onto a reference otherwise this object might be deallocated + } + return self; +} + +- (instancetype)initWithSize:(Size2i*)size type:(int)type { + self = [super init]; + if (self) { + _nativePtr = new cv::Mat(size.width, size.height, type); + } + return self; +} + +- (instancetype)initWithSizes:(NSArray*)sizes type:(int)type { + self = [super init]; + if (self) { + std::vector vSizes; + for (NSNumber* size in sizes) { + vSizes.push_back(size.intValue); + } + _nativePtr = new cv::Mat((int)sizes.count, vSizes.data(), type); + } + return self; +} + +- (instancetype)initWithRows:(int)rows cols:(int)cols type:(int)type scalar:(Scalar*)scalar { + self = [super init]; + if (self) { + cv::Scalar scalerTemp(scalar.val[0].doubleValue, scalar.val[1].doubleValue, scalar.val[2].doubleValue, scalar.val[3].doubleValue); + _nativePtr = new cv::Mat(rows, cols, type, scalerTemp); + } + return self; +} + +- (instancetype)initWithSize:(Size2i*)size type:(int)type scalar:(Scalar *)scalar { + self = [super init]; + if (self) { + cv::Scalar scalerTemp(scalar.val[0].doubleValue, scalar.val[1].doubleValue, scalar.val[2].doubleValue, scalar.val[3].doubleValue); + _nativePtr = new cv::Mat(size.width, size.height, type, scalerTemp); + } + return self; +} + +- (instancetype)initWithSizes:(NSArray*)sizes type:(int)type scalar:(Scalar *)scalar { + self = [super init]; + if (self) { + cv::Scalar scalerTemp(scalar.val[0].doubleValue, scalar.val[1].doubleValue, scalar.val[2].doubleValue, scalar.val[3].doubleValue); + std::vector vSizes; + for (NSNumber* size in sizes) { + vSizes.push_back(size.intValue); + } + _nativePtr = new cv::Mat((int)sizes.count, vSizes.data(), type, scalerTemp); + } + return self; +} + +- (instancetype)initWithMat:(Mat*)mat rowRange:(Range*)rowRange colRange:(Range*)colRange { + self = [super init]; + if (self) { + cv::Range rows(rowRange.start, rowRange.end); + cv::Range cols(colRange.start, colRange.end); + _nativePtr = new cv::Mat(*(cv::Mat*)mat.nativePtr, rows, cols); + } + return self; +} + +- (instancetype)initWithMat:(Mat*)mat rowRange:(Range*)rowRange { + self = [super init]; + if (self) { + cv::Range rows(rowRange.start, rowRange.end); + _nativePtr = new cv::Mat(*(cv::Mat*)mat.nativePtr, rows); + } + return self; +} + +- (instancetype)initWithMat:(Mat*)mat ranges:(NSArray*)ranges { + self = [super init]; + if (self) { + std::vector tempRanges; + for (Range* range in ranges) { + tempRanges.push_back(cv::Range(range.start, range.end)); + } + _nativePtr = new cv::Mat(mat.nativePtr->operator()(tempRanges)); + } + return self; +} + +- (instancetype)initWithMat:(Mat*)mat rect:(Rect2i*)roi { + self = [super init]; + if (self) { + cv::Range rows(roi.y, roi.y + roi.height); + cv::Range cols(roi.x, roi.x + roi.width); + _nativePtr = new cv::Mat(*(cv::Mat*)mat.nativePtr, rows, cols); + } + return self; +} + +- (BOOL)isSameMat:(Mat*)mat { + return self.nativePtr == mat.nativePtr; +} + +- (Mat*)adjustRoiTop:(int)dtop bottom:(int)dbottom left:(int)dleft right:(int)dright { + cv::Mat adjusted = _nativePtr->adjustROI(dtop, dbottom, dleft, dright); + return [[Mat alloc] initWithNativeMat:new cv::Mat(adjusted)]; +} + +- (void)assignTo:(Mat*)mat type:(int)type { + _nativePtr->assignTo(*(cv::Mat*)mat.nativePtr, type); +} + +- (void)assignTo:(Mat*)mat { + _nativePtr->assignTo(*(cv::Mat*)mat.nativePtr); +} + +- (int)channels { + return _nativePtr->channels(); +} + +- (int)checkVector:(int)elemChannels depth:(int)depth requireContinuous:(BOOL) requireContinuous { + return _nativePtr->checkVector(elemChannels, depth, requireContinuous); +} + +- (int)checkVector:(int)elemChannels depth:(int)depth { + return _nativePtr->checkVector(elemChannels, depth); +} + +- (int)checkVector:(int)elemChannels { + return _nativePtr->checkVector(elemChannels); +} + +- (Mat*)clone { + return [[Mat alloc] initWithNativeMat:(new cv::Mat(_nativePtr->clone()))]; +} + +- (Mat*)col:(int)x { + return [[Mat alloc] initWithNativeMat:(new cv::Mat(_nativePtr->col(x)))]; +} + +- (Mat*)colRange:(int)start end:(int)end { + return [[Mat alloc] initWithNativeMat:(new cv::Mat(_nativePtr->colRange(start, end)))]; +} + +- (Mat*)colRange:(Range*)range { + return [[Mat alloc] initWithNativeMat:(new cv::Mat(_nativePtr->colRange(range.start, range.end)))]; +} + +- (int)dims { + return _nativePtr->dims; +} + +- (int)cols { + return _nativePtr->cols; +} + +- (void)convertTo:(Mat*)mat rtype:(int)rtype alpha:(double)alpha beta:(double)beta { + _nativePtr->convertTo(*(cv::Mat*)mat->_nativePtr, rtype, alpha, beta); +} + +- (void)convertTo:(Mat*)mat rtype:(int)rtype alpha:(double)alpha { + _nativePtr->convertTo(*(cv::Mat*)mat->_nativePtr, rtype, alpha); +} + +- (void)convertTo:(Mat*)mat rtype:(int)rtype { + _nativePtr->convertTo(*(cv::Mat*)mat->_nativePtr, rtype); +} + +- (void)copyTo:(Mat*)mat { + _nativePtr->copyTo(*(cv::Mat*)mat->_nativePtr); +} + +- (void)copyTo:(Mat*)mat mask:(Mat*)mask { + _nativePtr->copyTo(*(cv::Mat*)mat->_nativePtr, *(cv::Mat*)mask->_nativePtr); +} + +- (void)create:(int)rows cols:(int)cols type:(int)type { + _nativePtr->create(rows, cols, type); +} + +- (void)create:(Size2i*)size type:(int)type { + cv::Size tempSize(size.width, size.height); + _nativePtr->create(tempSize, type); +} + +- (void)createEx:(NSArray*)sizes type:(int)type { + std::vector tempSizes; + for (NSNumber* size in sizes) { + tempSizes.push_back(size.intValue); + } + _nativePtr->create((int)tempSizes.size(), tempSizes.data(), type); +} + +- (void)copySize:(Mat*)mat { + _nativePtr->copySize(*(cv::Mat*)mat.nativePtr); +} + +- (Mat*)cross:(Mat*)mat { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->cross(*(cv::Mat*)mat.nativePtr))]; +} + +- (int)depth { + return _nativePtr->depth(); +} + +- (Mat*)diag:(int)diagonal { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->diag(diagonal))]; +} + +- (Mat*)diag { + return [self diag:0]; +} + ++ (Mat*)diag:(Mat*)diagonal { + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::diag(*(cv::Mat*)diagonal.nativePtr))]; +} + +- (double)dot:(Mat*)mat { + return _nativePtr->dot(*(cv::Mat*)mat.nativePtr); +} + +- (long)elemSize { + return _nativePtr->elemSize(); +} + +- (long)elemSize1 { + return _nativePtr->elemSize1(); +} + +- (BOOL)empty { + return _nativePtr->empty(); +} + ++ (Mat*)eye:(int)rows cols:(int)cols type:(int)type { + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::eye(rows, cols, type))]; +} + ++ (Mat*)eye:(Size2i*)size type:(int)type { + cv::Size tempSize(size.width, size.height); + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::eye(tempSize, type))]; +} + +- (Mat*)inv:(int)method { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->inv(method))]; +} + +- (Mat*)inv { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->inv())]; +} + +- (BOOL)isContinuous { + return _nativePtr->isContinuous(); +} + +- (BOOL)isSubmatrix { + return _nativePtr->isSubmatrix(); +} + +- (void)locateROI:(Size2i*)wholeSize ofs:(Point2i*)ofs { + cv::Size tempWholeSize; + cv::Point tempOfs; + _nativePtr->locateROI(tempWholeSize, tempOfs); + if (wholeSize != nil) { + wholeSize.width = tempWholeSize.width; + wholeSize.height = tempWholeSize.height; + } + if (ofs != nil) { + ofs.x = tempOfs.x; + ofs.y = tempOfs.y; + } +} + +- (Mat*)mul:(Mat*)mat scale:(double)scale { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->mul(*(cv::Mat*)mat.nativePtr, scale))]; +} + +- (Mat*)mul:(Mat*)mat { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->mul(*(cv::Mat*)mat.nativePtr))]; +} + ++ (Mat*)ones:(int)rows cols:(int)cols type:(int)type { + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::ones(rows, cols, type))]; +} + ++ (Mat*)ones:(Size2i*)size type:(int)type { + cv::Size tempSize(size.width, size.height); + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::ones(tempSize, type))]; +} + ++ (Mat*)onesEx:(NSArray*)sizes type:(int)type { + std::vector tempSizes; + for (NSNumber* size in sizes) { + tempSizes.push_back(size.intValue); + } + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::ones((int)tempSizes.size(), tempSizes.data(), type))]; +} + +- (void)push_back:(Mat*)mat { + _nativePtr->push_back(*(cv::Mat*)mat.nativePtr); +} + +- (Mat*)reshape:(int)channels rows:(int)rows { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->reshape(channels, rows))]; +} + +- (Mat*)reshape:(int)channels { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->reshape(channels))]; +} + +- (Mat*)reshape:(int)channels newshape:(NSArray*)newshape { + std::vector tempNewshape; + for (NSNumber* size in newshape) { + tempNewshape.push_back(size.intValue); + } + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->reshape(channels, tempNewshape))]; +} + +- (Mat*)row:(int)y { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->row(y))]; +} + +- (Mat*)rowRange:(int)start end:(int)end { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->rowRange(start, end))]; +} + +- (Mat*)rowRange:(Range*)range { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->rowRange(range.start, range.end))]; +} + +- (int)rows { + return _nativePtr->rows; +} + +- (Mat*)setToScalar:(Scalar*)scalar { + cv::Scalar tempScalar(scalar.val[0].doubleValue, scalar.val[1].doubleValue, scalar.val[2].doubleValue, scalar.val[3].doubleValue); + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->operator=(tempScalar))]; +} + +- (Mat*)setToScalar:(Scalar*)scalar mask:(Mat*)mask { + cv::Scalar tempScalar(scalar.val[0].doubleValue, scalar.val[1].doubleValue, scalar.val[2].doubleValue, scalar.val[3].doubleValue); + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->setTo(tempScalar, *(cv::Mat*)mask.nativePtr))]; +} + +- (Mat*)setToValue:(Mat*)value mask:(Mat*)mask { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->setTo(*(cv::Mat*)value.nativePtr, *(cv::Mat*)mask.nativePtr))]; +} + +- (Mat*)setToValue:(Mat*)value { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->setTo(*(cv::Mat*)value.nativePtr))]; +} + +- (Size2i*)size { + return [[Size2i alloc] initWithWidth:_nativePtr->size().width height:_nativePtr->size().height]; +} + +- (int)size:(int)dimIndex { + return _nativePtr->size[dimIndex]; +} + +- (long)step1:(int)dimIndex { + return _nativePtr->step1(dimIndex); +} + +- (long)step1 { + return _nativePtr->step1(); +} + +- (Mat*)submat:(int)rowStart rowEnd:(int)rowEnd colStart:(int)colStart colEnd:(int)colEnd { + Range* rowRange = [[Range alloc] initWithStart:rowStart end:rowEnd]; + Range* colRange = [[Range alloc] initWithStart:colStart end:colEnd]; + return [self submat:rowRange colRange:colRange]; +} + +- (Mat*)submat:(Range*)rowRange colRange:(Range*)colRange { + cv::Range tempRowRange(rowRange.start, rowRange.end); + cv::Range tempColRange(colRange.start, colRange.end); + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->operator()(tempRowRange, tempColRange))]; +} + +- (Mat*)submat:(NSArray*)ranges { + std::vector tempRanges; + for (Range* range in ranges) { + tempRanges.push_back(cv::Range(range.start, range.end)); + } + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->operator()(tempRanges))]; +} + +- (Mat*)submatRoi:(Rect2i*)roi { + cv::Rect tempRoi(roi.x, roi.y, roi.width, roi.height); + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->operator()(tempRoi))]; +} + +- (Mat*)t { + return [[Mat alloc] initWithNativeMat:new cv::Mat(_nativePtr->t())]; +} + +- (long)total { + return _nativePtr->total(); +} + +- (int)type { + return _nativePtr->type(); +} + ++ (Mat*)zeros:(int)rows cols:(int)cols type:(int)type { + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::zeros(rows, cols, type))]; +} + ++ (Mat*)zeros:(Size2i*)size type:(int)type { + cv::Size tempSize(size.width, size.height); + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::zeros(tempSize, type))]; +} + ++ (Mat*)zerosEx:(NSArray*)sizes type:(int)type { + std::vector tempSizes; + for (NSNumber* size in sizes) { + tempSizes.push_back(size.intValue); + } + return [[Mat alloc] initWithNativeMat:new cv::Mat(cv::Mat::zeros((int)tempSizes.size(), tempSizes.data(), type))]; +} + +- (NSString*)dimsDescription { + if (_nativePtr->dims <= 0) { + return @"-1*-1*"; + } else { + NSMutableString* ret = [NSMutableString string]; + for (int index=0; index<_nativePtr->dims; index++) { + [ret appendFormat:@"%d*", _nativePtr->size[index]]; + } + return ret; + } +} + +- (NSString*)description { + NSString* dimDesc = [self dimsDescription]; + return [NSString stringWithFormat:@"Mat [ %@%@, isCont=%s, isSubmat=%s, nativeObj=0x%p, dataAddr=0x%p ]", dimDesc, [CvType typeToString:_nativePtr->type()], _nativePtr->isContinuous()?"YES":"NO", _nativePtr->isSubmatrix()?"YES":"NO", (void*)_nativePtr, (void*)_nativePtr->data]; +} + +- (NSString*)dump { + NSMutableString* ret = [NSMutableString string]; + cv::Ptr formatted = cv::Formatter::get()->format(*_nativePtr); + for(const char* format = formatted->next(); format; format = formatted->next()) { + [ret appendFormat:@"%s", format]; + } + return ret; +} + +template void putData(uchar* dataDest, int count, T (^readData)(int)) { + T* tDataDest = (T*)dataDest; + for (int index = 0; index < count; index++) { + tDataDest[index] = readData(index); + } +} + +- (void)put:(uchar*)dest data:(NSArray*)data offset:(int)offset count:(int)count { + int depth = _nativePtr->depth(); + if (depth == CV_8U) { + putData(dest, count, ^uchar (int index) { return cv::saturate_cast(data[offset + index].doubleValue);} ); + } else if (depth == CV_8S) { + putData(dest, count, ^char (int index) { return cv::saturate_cast(data[offset + index].doubleValue);} ); + } else if (depth == CV_16U) { + putData(dest, count, ^ushort (int index) { return cv::saturate_cast(data[offset + index].doubleValue);} ); + } else if (depth == CV_16S) { + putData(dest, count, ^short (int index) { return cv::saturate_cast(data[offset + index].doubleValue);} ); + } else if (depth == CV_32S) { + putData(dest, count, ^int32_t (int index) { return cv::saturate_cast(data[offset + index].doubleValue);} ); + } else if (depth == CV_32F) { + putData(dest, count, ^float (int index) { return cv::saturate_cast(data[offset + index].doubleValue);} ); + } else if (depth == CV_64F) { + putData(dest, count, ^double (int index) { return data[offset + index].doubleValue;} ); + } +} + +- (int)put:(NSArray*)indices data:(NSArray*)data { + cv::Mat* mat = _nativePtr; + int type = mat->type(); + int rawValueSize = (int)(mat->elemSize() / mat->channels()); + if (data == nil || data.count % [CvType channels:type] != 0) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Provided data element number (%lu) should be multiple of the Mat channels count (%d)", (unsigned long)(data == nil ? 0 : data.count), [CvType channels:type]] + userInfo:nil]; + @throw exception; + } + std::vector tempIndices; + for (NSNumber* index in indices) { + tempIndices.push_back(index.intValue); + } + for (int index = 0; index < mat->dims; index++) { + if (mat->size[index]<=tempIndices[index]) { + return 0; // indexes out of range + } + } + + int arrayAvailable = (int)data.count; + int matAvailable = getMatAvailable(mat, tempIndices); + int available = MIN(arrayAvailable, matAvailable); + int copyOffset = 0; + int copyCount = MIN((mat->size[mat->dims - 1] - tempIndices[mat->dims - 1]) * mat->channels(), available); + int result = (int)(available * rawValueSize); + + while (available > 0) { + [self put:mat->ptr(tempIndices.data()) data:data offset:(int)copyOffset count:copyCount]; + if (updateIdx(mat, tempIndices, copyCount / mat->channels())) { + break; + } + available -= copyCount; + copyOffset += copyCount; + copyCount = MIN(mat->size[mat->dims-1] * mat->channels(), available); + } + return result; +} + +- (int)put:(int)row col:(int)col data:(NSArray*)data { + NSArray* indices = @[[NSNumber numberWithInt:row], [NSNumber numberWithInt:col]]; + return [self put:indices data:data]; +} + +template void getData(uchar* dataSource, int count, void (^writeData)(int,T)) { + T* tDataSource = (T*)dataSource; + for (int index = 0; index < count; index++) { + writeData(index, tDataSource[index]); + } +} + +- (void)get:(uchar*)source data:(NSMutableArray*)data offset:(int)offset count:(int)count { + int depth = _nativePtr->depth(); + if (depth == CV_8U) { + getData(source, count, ^void (int index, uchar value) { data[offset + index] = [[NSNumber alloc] initWithUnsignedChar:value]; } ); + } else if (depth == CV_8S) { + getData(source, count, ^void (int index, char value) { data[offset + index] = [[NSNumber alloc] initWithChar:value]; } ); + } else if (depth == CV_16U) { + getData(source, count, ^void (int index, ushort value) { data[offset + index] = [[NSNumber alloc] initWithUnsignedShort:value]; } ); + } else if (depth == CV_16S) { + getData(source, count, ^void (int index, short value) { data[offset + index] = [[NSNumber alloc] initWithShort:value]; } ); + } else if (depth == CV_32S) { + getData(source, count, ^void (int index, int32_t value) { data[offset + index] = [[NSNumber alloc] initWithInt:value]; } ); + } else if (depth == CV_32F) { + getData(source, count, ^void (int index, float value) { data[offset + index] = [[NSNumber alloc] initWithFloat:value]; } ); + } else if (depth == CV_64F) { + getData(source, count, ^void (int index, double value) { data[offset + index] = [[NSNumber alloc] initWithDouble:value]; } ); + } +} + +- (int)get:(NSArray*)indices data:(NSMutableArray*)data { + cv::Mat* mat = _nativePtr; + int type = mat->type(); + if (data == nil || data.count % [CvType channels:type] != 0) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Provided data element number (%lu) should be multiple of the Mat channels count (%d)", (unsigned long)(data == nil ? 0 : data.count), [CvType channels:type]] + userInfo:nil]; + @throw exception; + } + std::vector tempIndices; + for (NSNumber* index in indices) { + tempIndices.push_back(index.intValue); + } + for (int index = 0; index < mat->dims; index++) { + if (mat->size[index]<=tempIndices[index]) { + return 0; // indexes out of range + } + } + + int arrayAvailable = (int)data.count; + int copyOffset = 0; + int matAvailable = getMatAvailable(mat, tempIndices); + int available = MIN(arrayAvailable, matAvailable); + int copyCount = MIN((mat->size[mat->dims - 1] - tempIndices[mat->dims - 1]) * mat->channels(), available); + int result = (int)(available * mat->elemSize() / mat->channels()); + + while (available > 0) { + [self get:mat->ptr(tempIndices.data()) data:data offset:(int)copyOffset count:copyCount]; + if (updateIdx(mat, tempIndices, copyCount / mat->channels())) { + break; + } + available -= copyCount; + copyOffset += copyCount; + copyCount = MIN(mat->size[mat->dims-1] * mat->channels(), available); + } + return result; +} + +- (int)get:(int)row col:(int)col data:(NSMutableArray*)data { + NSArray* indices = @[[NSNumber numberWithInt:row], [NSNumber numberWithInt:col]]; + return [self get:indices data:data]; +} + +- (NSArray*)get:(int)row col:(int)col { + NSMutableArray* result = [NSMutableArray new]; + for (int index = 0; index<_nativePtr->channels(); index++) { + [result addObject:@0]; + } + [self get:row col:col data:result]; + return result; +} + +- (NSArray*)get:(NSArray*)indices { + NSMutableArray* result = [NSMutableArray new]; + for (int index = 0; index<_nativePtr->channels(); index++) { + [result addObject:@0]; + } + [self get:indices data:result]; + return result; +} + +template void getData(uchar* source, void (^writeData)(int,T), int dataOffset, int dataLength) { + T* tSource = (T*)source; + for (int index = 0; index < dataLength; index++) { + writeData(dataOffset+index, tSource[index]); + } +} + +int getMatAvailable(cv::Mat* mat, std::vector& indices) { + int blockSize = 1; + int unavailableCount = 0; + for (int index = mat->dims - 1; index >= 0; index--) { + unavailableCount += blockSize * indices[index]; + blockSize *= mat->size[index]; + } + return (int)(mat->channels() * (mat->total() - unavailableCount)); +} + +template int getData(NSArray* indices, cv::Mat* mat, int count, T* tBuffer) { + std::vector tempIndices; + for (NSNumber* index in indices) { + tempIndices.push_back(index.intValue); + } + for (int index = 0; index < mat->dims; index++) { + if (mat->size[index]<=tempIndices[index]) { + return 0; // indexes out of range + } + } + + int arrayAvailable = count; + int matAvailable = getMatAvailable(mat, tempIndices); + int available = MIN(arrayAvailable, matAvailable); + int result = (int)(available * mat->elemSize() / mat->channels()); + if (mat->isContinuous()) { + memcpy(tBuffer, mat->ptr(tempIndices.data()), available * sizeof(T)); + } else { + int copyOffset = 0; + int copyCount = MIN((mat->size[mat->dims - 1] - tempIndices[mat->dims - 1]) * mat->channels(), available); + while (available > 0) { + memcpy(tBuffer + copyOffset, mat->ptr(tempIndices.data()), copyCount * sizeof(T)); + if (updateIdx(mat, tempIndices, copyCount / mat->channels())) { + break; + } + available -= copyCount; + copyOffset += copyCount * sizeof(T); + copyCount = MIN(mat->size[mat->dims-1] * mat->channels(), available); + } + } + return result; +} + +- (int)get:(NSArray*)indices count:(int)count byteBuffer:(char*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_8U && depth != CV_8S) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depths for this call are CV_8U or CV_8S.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return getData(indices, _nativePtr, count, buffer); +} + +- (int)get:(NSArray*)indices count:(int)count doubleBuffer:(double*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_64F) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depth for this call is CV_64F.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return getData(indices, _nativePtr, count, buffer); +} + +- (int)get:(NSArray*)indices count:(int)count floatBuffer:(float*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_32F) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depth for this call is CV_32F.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return getData(indices, _nativePtr, count, buffer); +} + +- (int)get:(NSArray*)indices count:(int)count intBuffer:(int*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_32S) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depth for this call is CV_32S.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return getData(indices, _nativePtr, count, buffer); +} + +- (int)get:(NSArray*)indices count:(int)count shortBuffer:(short*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_16S && depth != CV_16U) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depths for this call are CV_16S and CV_16U.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return getData(indices, _nativePtr, count, buffer); +} + +template int putData(NSArray* indices, cv::Mat* mat, int count, const T* tBuffer) { + std::vector tempIndices; + for (NSNumber* index in indices) { + tempIndices.push_back(index.intValue); + } + for (int index = 0; index < mat->dims; index++) { + if (mat->size[index]<=tempIndices[index]) { + return 0; // indexes out of range + } + } + + int arrayAvailable = count; + int matAvailable = getMatAvailable(mat, tempIndices); + int available = MIN(arrayAvailable, matAvailable); + int result = (int)(available * mat->elemSize() / mat->channels()); + if (mat->isContinuous()) { + memcpy(mat->ptr(tempIndices.data()), tBuffer, available * sizeof(T)); + } else { + int copyOffset = 0; + int copyCount = MIN((mat->size[mat->dims - 1] - tempIndices[mat->dims - 1]) * mat->channels(), available); + while (available > 0) { + memcpy(mat->ptr(tempIndices.data()), tBuffer + copyOffset, copyCount * sizeof(T)); + if (updateIdx(mat, tempIndices, copyCount / mat->channels())) { + break; + } + available -= copyCount; + copyOffset += copyCount * sizeof(T); + copyCount = MIN(mat->size[mat->dims-1] * (int)mat->channels(), available); + } + } + return result; +} + +- (int)put:(NSArray*)indices count:(int)count byteBuffer:(const char*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_8U && depth != CV_8S) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depths for this call are CV_8U or CV_8S.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return putData(indices, _nativePtr, count, buffer); +} + +- (int)put:(NSArray*)indices count:(int)count doubleBuffer:(const double*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_64F) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depth for this call is CV_64F.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return putData(indices, _nativePtr, count, buffer); +} + +- (int)put:(NSArray*)indices count:(int)count floatBuffer:(const float*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_32F) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depth for this call is CV_32F.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return putData(indices, _nativePtr, count, buffer); +} + +- (int)put:(NSArray*)indices count:(int)count intBuffer:(const int*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_32S) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depth for this call is CV_32S.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return putData(indices, _nativePtr, count, buffer); +} + +- (int)put:(NSArray*)indices count:(int)count shortBuffer:(const short*)buffer { + int depth = _nativePtr->depth(); + if (depth != CV_16S && depth != CV_16U) { + NSException* exception = [NSException + exceptionWithName:@"UnsupportedOperationException" + reason:[NSString stringWithFormat:@"Invalid depth (%@). Valid depths for this call are CV_16S and CV_16U.", [CvType typeToString:depth]] + userInfo:nil]; + @throw exception; + } + return putData(indices, _nativePtr, count, buffer); +} + +- (int)height { + return [self rows]; +} + +- (int)width { + return [self cols]; +} + +@end diff --git a/modules/core/misc/objc/common/MatExt.swift b/modules/core/misc/objc/common/MatExt.swift new file mode 100644 index 0000000000..f6b3072345 --- /dev/null +++ b/modules/core/misc/objc/common/MatExt.swift @@ -0,0 +1,244 @@ +// +// MatExt.swift +// +// Created by Giles Payne on 2020/01/19. +// + +import Foundation + +let OpenCVErrorDomain = "OpenCVErrorDomain" + +enum OpenCVError : Int { + case IncompatibleDataType = 10001 + case IncompatibleBufferSize +} + +func throwIncompatibleDataType(typeName: String) throws { + throw NSError( + domain: OpenCVErrorDomain, + code: OpenCVError.IncompatibleDataType.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Incompatible Mat type \(typeName)" + ] + ) +} + +func throwIncompatibleBufferSize(count: Int, channels: Int32) throws { + throw NSError( + domain: OpenCVErrorDomain, + code: OpenCVError.IncompatibleBufferSize.rawValue, + userInfo: [ + NSLocalizedDescriptionKey: "Provided data element number \(count) should be multiple of the Mat channels count \(channels)" + ] + ) +} + +public extension Mat { + + convenience init(rows:Int32, cols:Int32, type:Int32, data:[Int8]) { + let dataObject = data.withUnsafeBufferPointer { Data(buffer: $0) } + self.init(rows: rows, cols: cols, type: type, data: dataObject) + } + + convenience init(rows:Int32, cols:Int32, type:Int32, data:[Int8], step:Int) { + let dataObject = data.withUnsafeBufferPointer { Data(buffer: $0) } + self.init(rows: rows, cols: cols, type: type, data: dataObject, step:step) + } + + @discardableResult func get(indices:[Int32], data:inout [Int8]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_8U && depth() != CvType.CV_8S { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeMutableBufferPointer { body in + return __get(indices as [NSNumber], count: count, byteBuffer: body.baseAddress!) + } + } + + @discardableResult func get(indices:[Int32], data:inout [Double]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_64F { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeMutableBufferPointer { body in + return __get(indices as [NSNumber], count: count, doubleBuffer: body.baseAddress!) + } + } + + @discardableResult func get(indices:[Int32], data:inout [Float]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_32F { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeMutableBufferPointer { body in + return __get(indices as [NSNumber], count: count, floatBuffer: body.baseAddress!) + } + } + + @discardableResult func get(indices:[Int32], data:inout [Int32]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_32S { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeMutableBufferPointer { body in + return __get(indices as [NSNumber], count: count, intBuffer: body.baseAddress!) + } + } + + @discardableResult func get(indices:[Int32], data:inout [Int16]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_16U && depth() != CvType.CV_16S { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeMutableBufferPointer { body in + return __get(indices as [NSNumber], count: count, shortBuffer: body.baseAddress!) + } + } + + @discardableResult func get(row: Int32, col: Int32, data:inout [Int8]) throws -> Int32 { + return try get(indices: [row, col], data: &data) + } + + @discardableResult func get(row: Int32, col: Int32, data:inout [Double]) throws -> Int32 { + return try get(indices: [row, col], data: &data) + } + + @discardableResult func get(row: Int32, col: Int32, data:inout [Float]) throws -> Int32 { + return try get(indices: [row, col], data: &data) + } + + @discardableResult func get(row: Int32, col: Int32, data:inout [Int32]) throws -> Int32 { + return try get(indices: [row, col], data: &data) + } + + @discardableResult func get(row: Int32, col: Int32, data:inout [Int16]) throws -> Int32 { + return try get(indices: [row, col], data: &data) + } + + @discardableResult func put(indices:[Int32], data:[Int8]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_8U && depth() != CvType.CV_8S { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeBufferPointer { body in + return __put(indices as [NSNumber], count: count, byteBuffer: body.baseAddress!) + } + } + + @discardableResult func put(indices:[Int32], data:[Int8], offset: Int, length: Int32) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_8U && depth() != CvType.CV_8S { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + return data.withUnsafeBufferPointer { body in + return __put(indices as [NSNumber], count: length, byteBuffer: body.baseAddress! + offset) + } + } + + // unlike other put:indices:data functions this one (with [Double]) should convert input values to correct type + @discardableResult func put(indices:[Int32], data:[Double]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } + if depth() == CvType.CV_64F { + let count = Int32(data.count) + return data.withUnsafeBufferPointer { body in + return __put(indices as [NSNumber], count: count, doubleBuffer: body.baseAddress!) + } + } else { + return __put(indices as [NSNumber], data: data as [NSNumber]) + } + } + + @discardableResult func put(indices:[Int32], data:[Float]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_32F { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeBufferPointer { body in + return __put(indices as [NSNumber], count: count, floatBuffer: body.baseAddress!) + } + } + + @discardableResult func put(indices:[Int32], data:[Int32]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_32S { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeBufferPointer { body in + return __put(indices as [NSNumber], count: count, intBuffer: body.baseAddress!) + } + } + + @discardableResult func put(indices:[Int32], data:[Int16]) throws -> Int32 { + let channels = CvType.channels(Int32(type())) + if Int32(data.count) % channels != 0 { + try throwIncompatibleBufferSize(count: data.count, channels: channels) + } else if depth() != CvType.CV_16U && depth() != CvType.CV_16S { + try throwIncompatibleDataType(typeName: CvType.type(toString: type())) + } + let count = Int32(data.count) + return data.withUnsafeBufferPointer { body in + return __put(indices as [NSNumber], count: count, shortBuffer: body.baseAddress!) + } + } + + @discardableResult func put(row: Int32, col: Int32, data:[Int8]) throws -> Int32 { + return try put(indices: [row, col], data: data) + } + + @discardableResult func put(row: Int32, col: Int32, data: [Int8], offset: Int, length: Int32) throws -> Int32 { + return try put(indices: [row, col], data: data, offset: offset, length: length) + } + + @discardableResult func put(row: Int32, col: Int32, data: [Double]) throws -> Int32 { + return try put(indices: [row, col], data: data) + } + + @discardableResult func put(row: Int32, col: Int32, data: [Float]) throws -> Int32 { + return try put(indices: [row, col], data: data) + } + + @discardableResult func put(row: Int32, col: Int32, data: [Int32]) throws -> Int32 { + return try put(indices: [row, col], data: data) + } + + @discardableResult func put(row: Int32, col: Int32, data: [Int16]) throws -> Int32 { + return try put(indices: [row, col], data: data) + } + + @discardableResult func get(row: Int32, col: Int32) -> [Double] { + return get(indices: [row, col]) + } + + @discardableResult func get(indices: [Int32]) -> [Double] { + return __get(indices as [NSNumber]) as! [Double] + } +} diff --git a/modules/core/misc/objc/common/MatOfByte.h b/modules/core/misc/objc/common/MatOfByte.h new file mode 100644 index 0000000000..27d360ee36 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfByte.h @@ -0,0 +1,62 @@ +// +// MatOfByte.h +// +// Created by Giles Payne on 2019/12/26. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of bytes +*/ +@interface MatOfByte : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfByte from Mat object +* @param mat Mat object from which to create MatOfByte +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfByte from array +* @param array Array from which to create MatOfByte +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfByte.mm b/modules/core/misc/objc/common/MatOfByte.mm new file mode 100644 index 0000000000..3ed03ae707 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfByte.mm @@ -0,0 +1,69 @@ +// +// MatOfByte.mm +// +// Created by Giles Payne on 2019/12/26. +// + +#import "MatOfByte.h" +#import "Range.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfByte + +static const int _depth = CV_8U; +static const int _channels = 1; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + [self alloc:(int)array.count / _channels]; + [self put:0 col:0 data:array]; +} + +- (NSArray*)toArray { + int length = [self length]; + NSMutableArray* data = createArrayWithSize(length, @0.0); + [self get:0 col:0 data:data]; + return data; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfDMatch.h b/modules/core/misc/objc/common/MatOfDMatch.h new file mode 100644 index 0000000000..97507103ee --- /dev/null +++ b/modules/core/misc/objc/common/MatOfDMatch.h @@ -0,0 +1,64 @@ +// +// MatOfDMatch.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class DMatch; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of DMatch objects +*/ +@interface MatOfDMatch : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfDMatch from Mat object +* @param mat Mat object from which to create MatOfDMatch +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfDMatch from array +* @param array Array from which to create MatOfDMatch +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of DMatch objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfDMatch.mm b/modules/core/misc/objc/common/MatOfDMatch.mm new file mode 100644 index 0000000000..0ba7ab9184 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfDMatch.mm @@ -0,0 +1,83 @@ +// +// MatOfDMatch.m +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfDMatch.h" +#import "Range.h" +#import "DMatch.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfDMatch + +static const int _depth = CV_32F; +static const int _channels = 4; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithFloat:array[index].queryIdx]; + data[_channels * index + 1] = [NSNumber numberWithFloat:array[index].trainIdx]; + data[_channels * index + 2] = [NSNumber numberWithFloat:array[index].imgIdx]; + data[_channels * index + 3] = [NSNumber numberWithFloat:array[index].distance]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [DMatch new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[DMatch alloc] initWithQueryIdx:data[index * _channels].intValue trainIdx:data[index * _channels + 1].intValue imgIdx:data[index * _channels + 2].intValue distance:data[index * _channels + 3].floatValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfDouble.h b/modules/core/misc/objc/common/MatOfDouble.h new file mode 100644 index 0000000000..16a925a982 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfDouble.h @@ -0,0 +1,63 @@ +// +// MatOfDouble.h +// +// Created by Giles Payne on 2019/12/26. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of doubles +*/ +@interface MatOfDouble : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfDouble from Mat object +* @param mat Mat object from which to create MatOfDouble +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfDouble from array +* @param array Array from which to create MatOfDouble +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array +*/ +- (NSArray*)toArray; + + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfDouble.mm b/modules/core/misc/objc/common/MatOfDouble.mm new file mode 100644 index 0000000000..ac6e0bac3f --- /dev/null +++ b/modules/core/misc/objc/common/MatOfDouble.mm @@ -0,0 +1,69 @@ +// +// MatOfDouble.mm +// +// Created by Giles Payne on 2019/12/26. +// + +#import "MatOfDouble.h" +#import "Range.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfDouble + +static const int _depth = CV_64F; +static const int _channels = 1; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + [self alloc:(int)array.count / _channels]; + [self put:0 col:0 data:array]; +} + +- (NSArray*)toArray { + int length = [self length]; + NSMutableArray* data = createArrayWithSize(length, @0.0); + [self get:0 col:0 data:data]; + return data; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfFloat.h b/modules/core/misc/objc/common/MatOfFloat.h new file mode 100644 index 0000000000..110819d85f --- /dev/null +++ b/modules/core/misc/objc/common/MatOfFloat.h @@ -0,0 +1,60 @@ +// +// MatOfFloat.h +// +// Created by Giles Payne on 2019/12/26. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of floats +*/ +@interface MatOfFloat : Mat + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfFloat from Mat object +* @param mat Mat object from which to create MatOfFloat +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfFloat from array +* @param array Array from which to create MatOfFloat +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfFloat.mm b/modules/core/misc/objc/common/MatOfFloat.mm new file mode 100644 index 0000000000..bce4f25e99 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfFloat.mm @@ -0,0 +1,69 @@ +// +// MatOfFloat.mm +// +// Created by Giles Payne on 2019/12/26. +// + +#import "MatOfFloat.h" +#import "Range.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfFloat + +static const int _depth = CV_32F; +static const int _channels = 1; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + [self alloc:(int)array.count / _channels]; + [self put:0 col:0 data:array]; +} + +- (NSArray*)toArray { + int length = [self length]; + NSMutableArray* data = createArrayWithSize(length, @0.0); + [self get:0 col:0 data:data]; + return data; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfFloat4.h b/modules/core/misc/objc/common/MatOfFloat4.h new file mode 100644 index 0000000000..852e04bfd1 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfFloat4.h @@ -0,0 +1,62 @@ +// +// MatOfFloat4.h +// +// Created by Giles Payne on 2019/12/26. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of vectors of four floats +*/ +@interface MatOfFloat4 : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfFloat4 from Mat object +* @param mat Mat object from which to create MatOfFloat4 +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfFloat4 from array +* @param array Array from which to create MatOfFloat4 +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfFloat4.mm b/modules/core/misc/objc/common/MatOfFloat4.mm new file mode 100644 index 0000000000..331767126b --- /dev/null +++ b/modules/core/misc/objc/common/MatOfFloat4.mm @@ -0,0 +1,69 @@ +// +// MatOfFloat4.mm +// +// Created by Giles Payne on 2019/12/26. +// + +#import "MatOfFloat4.h" +#import "Range.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfFloat4 + +static const int _depth = CV_32F; +static const int _channels = 4; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + [self alloc:(int)array.count / _channels]; + [self put:0 col:0 data:array]; +} + +- (NSArray*)toArray { + int length = [self length]; + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:length]; + [self get:0 col:0 data:data]; + return data; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfFloat6.h b/modules/core/misc/objc/common/MatOfFloat6.h new file mode 100644 index 0000000000..c602f48d9e --- /dev/null +++ b/modules/core/misc/objc/common/MatOfFloat6.h @@ -0,0 +1,62 @@ +// +// MatOfFloat6.h +// +// Created by Giles Payne on 2019/12/26. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of vectors of six floats +*/ +@interface MatOfFloat6 : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfFloat6 from Mat object +* @param mat Mat object from which to create MatOfFloat6 +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfFloat6 from array +* @param array Array from which to create MatOfFloat6 +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfFloat6.mm b/modules/core/misc/objc/common/MatOfFloat6.mm new file mode 100644 index 0000000000..80f8e32d35 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfFloat6.mm @@ -0,0 +1,69 @@ +// +// MatOfFloat6.mm +// +// Created by Giles Payne on 2019/12/26. +// + +#import "MatOfFloat6.h" +#import "Range.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfFloat6 + +static const int _depth = CV_32F; +static const int _channels = 6; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + [self alloc:(int)array.count / _channels]; + [self put:0 col:0 data:array]; +} + +- (NSArray*)toArray { + int length = [self length]; + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:length]; + [self get:0 col:0 data:data]; + return data; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfInt.h b/modules/core/misc/objc/common/MatOfInt.h new file mode 100644 index 0000000000..b7e218353a --- /dev/null +++ b/modules/core/misc/objc/common/MatOfInt.h @@ -0,0 +1,62 @@ +// +// MatOfInt.h +// +// Created by Giles Payne on 2019/12/26. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of ints +*/ +@interface MatOfInt : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfInt from Mat object +* @param mat Mat object from which to create MatOfInt +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfInt from array +* @param array Array from which to create MatOfInt +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfInt.mm b/modules/core/misc/objc/common/MatOfInt.mm new file mode 100644 index 0000000000..4777b9c7e2 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfInt.mm @@ -0,0 +1,69 @@ +// +// MatOfInt.mm +// +// Created by Giles Payne on 2019/12/26. +// + +#import "MatOfInt.h" +#import "Range.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfInt + +static const int _depth = CV_32S; +static const int _channels = 1; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + [self alloc:(int)array.count / _channels]; + [self put:0 col:0 data:array]; +} + +- (NSArray*)toArray { + int length = [self length]; + NSMutableArray* data = createArrayWithSize(length, @0.0); + [self get:0 col:0 data:data]; + return data; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfInt4.h b/modules/core/misc/objc/common/MatOfInt4.h new file mode 100644 index 0000000000..c1fb40bb4b --- /dev/null +++ b/modules/core/misc/objc/common/MatOfInt4.h @@ -0,0 +1,62 @@ +// +// MatOfInt4.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of vectors of four ints +*/ +@interface MatOfInt4 : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfInt4 from Mat object +* @param mat Mat object from which to create MatOfInt4 +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfInt4 from array +* @param array Array from which to create MatOfInt4 +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfInt4.mm b/modules/core/misc/objc/common/MatOfInt4.mm new file mode 100644 index 0000000000..b5d81fe251 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfInt4.mm @@ -0,0 +1,69 @@ +// +// MatOfInt4.mm +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfInt4.h" +#import "Range.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfInt4 + +static const int _depth = CV_32S; +static const int _channels = 4; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + [self alloc:(int)array.count / _channels]; + [self put:0 col:0 data:array]; +} + +- (NSArray*)toArray { + int length = [self length]; + NSMutableArray* data = createArrayWithSize(length, @0.0); + [self get:0 col:0 data:data]; + return data; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfKeyPoint.h b/modules/core/misc/objc/common/MatOfKeyPoint.h new file mode 100644 index 0000000000..769e836e83 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfKeyPoint.h @@ -0,0 +1,64 @@ +// +// MatOfKeyPoint.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class KeyPoint; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of KeyPoint objects +*/ +@interface MatOfKeyPoint : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfKeyPoint from Mat object +* @param mat Mat object from which to create MatOfKeyPoint +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfKeyPoint from array +* @param array Array from which to create MatOfKeyPoint +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of KeyPoint objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfKeyPoint.mm b/modules/core/misc/objc/common/MatOfKeyPoint.mm new file mode 100644 index 0000000000..c6d92faea9 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfKeyPoint.mm @@ -0,0 +1,87 @@ +// +// MatOfKeyPoint.m +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfKeyPoint.h" +#import "Range.h" +#import "Point2f.h" +#import "KeyPoint.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfKeyPoint + +static const int _depth = CV_32F; +static const int _channels = 7; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithFloat:array[index].pt.x]; + data[_channels * index + 1] = [NSNumber numberWithFloat:array[index].pt.y]; + data[_channels * index + 2] = [NSNumber numberWithFloat:array[index].size]; + data[_channels * index + 3] = [NSNumber numberWithFloat:array[index].angle]; + data[_channels * index + 4] = [NSNumber numberWithFloat:array[index].response]; + data[_channels * index + 5] = [NSNumber numberWithFloat:array[index].octave]; + data[_channels * index + 6] = [NSNumber numberWithFloat:array[index].classId]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [KeyPoint new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[KeyPoint alloc] initWithX:data[index * _channels].floatValue y:data[index * _channels + 1].floatValue size:data[index * _channels + 2].floatValue angle:data[index * _channels + 3].floatValue response:data[index * _channels + 4].floatValue octave:data[index * _channels + 5].intValue classId:data[index * _channels + 6].intValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfPoint2f.h b/modules/core/misc/objc/common/MatOfPoint2f.h new file mode 100644 index 0000000000..e6b6a2df99 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint2f.h @@ -0,0 +1,64 @@ +// +// MatOfPoint2f.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +@class Point2f; + +/** +* Mat representation of an array of Point2f objects +*/ +@interface MatOfPoint2f : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfPoint2f from Mat object +* @param mat Mat object from which to create MatOfPoint2f +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfPoint2f from array +* @param array Array from which to create MatOfPoint2f +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of Point2f objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfPoint2f.mm b/modules/core/misc/objc/common/MatOfPoint2f.mm new file mode 100644 index 0000000000..c1c082b5fe --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint2f.mm @@ -0,0 +1,81 @@ +// +// MatOfPoint2f.mm +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfPoint2f.h" +#import "Range.h" +#import "Point2f.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfPoint2f + +static const int _depth = CV_32F; +static const int _channels = 2; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithFloat:array[index].x]; + data[_channels * index + 1] = [NSNumber numberWithFloat:array[index].y]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [Point2f new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[Point2f alloc] initWithX:data[index * _channels].floatValue y:data[index * _channels + 1].floatValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfPoint2i.h b/modules/core/misc/objc/common/MatOfPoint2i.h new file mode 100644 index 0000000000..dea9710e31 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint2i.h @@ -0,0 +1,65 @@ +// +// MatOfPoint2i.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class Point2i; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of Point objects +*/ +NS_SWIFT_NAME(MatOfPoint) +@interface MatOfPoint2i : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfPoint from Mat object +* @param mat Mat object from which to create MatOfPoint +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfPoint from array +* @param array Array from which to create MatOfPoint +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of Point objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfPoint2i.mm b/modules/core/misc/objc/common/MatOfPoint2i.mm new file mode 100644 index 0000000000..7c202f6d42 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint2i.mm @@ -0,0 +1,81 @@ +// +// MatOfPoint2i.mm +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfPoint2i.h" +#import "Range.h" +#import "Point2i.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfPoint2i + +static const int _depth = CV_32S; +static const int _channels = 2; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithInt:array[index].x]; + data[_channels * index + 1] = [NSNumber numberWithInt:array[index].y]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [Point2i new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[Point2i alloc] initWithX:data[index * _channels].intValue y:data[index * _channels + 1].intValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfPoint3.h b/modules/core/misc/objc/common/MatOfPoint3.h new file mode 100644 index 0000000000..d1ab9b87c7 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint3.h @@ -0,0 +1,64 @@ +// +// MatOfPoint3.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class Point3i; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of Point3i objects +*/ +@interface MatOfPoint3 : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfPoint3 from Mat object +* @param mat Mat object from which to create MatOfPoint3 +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfPoint3 from array +* @param array Array from which to create MatOfPoint3 +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of Point3i objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfPoint3.mm b/modules/core/misc/objc/common/MatOfPoint3.mm new file mode 100644 index 0000000000..546194b8df --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint3.mm @@ -0,0 +1,82 @@ +// +// MatOfPoint3.mm +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfPoint3.h" +#import "Range.h" +#import "Point3i.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfPoint3 + +static const int _depth = CV_32S; +static const int _channels = 3; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithInt:array[index].x]; + data[_channels * index + 1] = [NSNumber numberWithInt:array[index].y]; + data[_channels * index + 2] = [NSNumber numberWithInt:array[index].z]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [Point3i new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[Point3i alloc] initWithX:data[index * _channels].intValue y:data[index * _channels + 1].intValue z:data[index * _channels + 2].intValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfPoint3f.h b/modules/core/misc/objc/common/MatOfPoint3f.h new file mode 100644 index 0000000000..e9ba7b37f7 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint3f.h @@ -0,0 +1,64 @@ +// +// MatOfPoint3f.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class Point3f; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of Point3f objects +*/ +@interface MatOfPoint3f : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfPoint3f from Mat object +* @param mat Mat object from which to create MatOfPoint3f +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfPoint3f from array +* @param array Array from which to create MatOfPoint3f +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of Point3f objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfPoint3f.mm b/modules/core/misc/objc/common/MatOfPoint3f.mm new file mode 100644 index 0000000000..ea34210cd1 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfPoint3f.mm @@ -0,0 +1,82 @@ +// +// MatOfPoint3f.mm +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfPoint3f.h" +#import "Range.h" +#import "Point3f.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfPoint3f + +static const int _depth = CV_32F; +static const int _channels = 3; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithFloat:array[index].x]; + data[_channels * index + 1] = [NSNumber numberWithFloat:array[index].y]; + data[_channels * index + 2] = [NSNumber numberWithFloat:array[index].z]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [Point3f new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[Point3f alloc] initWithX:data[index * _channels].floatValue y:data[index * _channels + 1].floatValue z:data[index * _channels + 2].floatValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfRect2d.h b/modules/core/misc/objc/common/MatOfRect2d.h new file mode 100644 index 0000000000..4388fcfa11 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfRect2d.h @@ -0,0 +1,65 @@ +// +// MatOfRect2d.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class Rect2d; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of Rect2d objects +*/ +@interface MatOfRect2d : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; ++ (instancetype)fromNative:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfRect2d from Mat object +* @param mat Mat object from which to create MatOfRect2d +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfRect2d from array +* @param array Array from which to create MatOfRect2d +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of Rect2d objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfRect2d.mm b/modules/core/misc/objc/common/MatOfRect2d.mm new file mode 100644 index 0000000000..61490c3003 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfRect2d.mm @@ -0,0 +1,88 @@ +// +// MatOfRect2d.mm +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfRect2d.h" +#import "Range.h" +#import "Rect2d.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfRect2d + +static const int _depth = CV_64F; +static const int _channels = 4; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Mat*)nativeMat { + return [[MatOfRect2d alloc] initWithNativeMat:nativeMat]; +} + +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithDouble:array[index].x]; + data[_channels * index + 1] = [NSNumber numberWithDouble:array[index].y]; + data[_channels * index + 2] = [NSNumber numberWithDouble:array[index].width]; + data[_channels * index + 3] = [NSNumber numberWithDouble:array[index].height]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [Rect2d new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[Rect2d alloc] initWithX:data[index * _channels].doubleValue y:data[index * _channels + 1].doubleValue width:data[index * _channels + 2].doubleValue height:data[index * _channels + 3].doubleValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfRect2i.h b/modules/core/misc/objc/common/MatOfRect2i.h new file mode 100644 index 0000000000..fa23338172 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfRect2i.h @@ -0,0 +1,65 @@ +// +// MatOfRect2i.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class Rect2i; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of Rect objects +*/ +NS_SWIFT_NAME(MatOfRect) +@interface MatOfRect2i : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfRect from Mat object +* @param mat Mat object from which to create MatOfRect +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfRect from array +* @param array Array from which to create MatOfRect +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of Rect objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfRect2i.mm b/modules/core/misc/objc/common/MatOfRect2i.mm new file mode 100644 index 0000000000..00e8b824d0 --- /dev/null +++ b/modules/core/misc/objc/common/MatOfRect2i.mm @@ -0,0 +1,83 @@ +// +// MatOfRect2i.m +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfRect2i.h" +#import "Range.h" +#import "Rect2i.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfRect2i + +static const int _depth = CV_32S; +static const int _channels = 4; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithInt:array[index].x]; + data[_channels * index + 1] = [NSNumber numberWithInt:array[index].y]; + data[_channels * index + 2] = [NSNumber numberWithInt:array[index].width]; + data[_channels * index + 3] = [NSNumber numberWithInt:array[index].height]; + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [Rect2i new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[Rect2i alloc] initWithX:data[index * _channels].intValue y:data[index * _channels + 1].intValue width:data[index * _channels + 2].intValue height:data[index * _channels + 3].intValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MatOfRotatedRect.h b/modules/core/misc/objc/common/MatOfRotatedRect.h new file mode 100644 index 0000000000..a9108e030e --- /dev/null +++ b/modules/core/misc/objc/common/MatOfRotatedRect.h @@ -0,0 +1,64 @@ +// +// MatOfRotatedRect.h +// +// Created by Giles Payne on 2019/12/27. +// + +#pragma once + +#import "Mat.h" + +@class RotatedRect; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Mat representation of an array of RotatedRect objects +*/ +@interface MatOfRotatedRect : Mat + +#pragma mark - Constructors + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat; +#endif + +/** +* Create MatOfRotatedRect from Mat object +* @param mat Mat object from which to create MatOfRotatedRect +*/ +- (instancetype)initWithMat:(Mat*)mat; + +/** +* Create MatOfRotatedRect from array +* @param array Array from which to create MatOfRotatedRect +*/ +- (instancetype)initWithArray:(NSArray*)array; + +#pragma mark - Methods + +/** +* Allocate specified number of elements +* @param elemNumber Number of elements +*/ +- (void)alloc:(int)elemNumber; + +/** +* Populate Mat with elements of an array +* @param array Array with which to populate the Mat +*/ +- (void)fromArray:(NSArray*)array; + +/** +* Output Mat elements as an array of RotatedRect objects +*/ +- (NSArray*)toArray; + +/** +* Total number of values in Mat +*/ +- (int)length; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MatOfRotatedRect.mm b/modules/core/misc/objc/common/MatOfRotatedRect.mm new file mode 100644 index 0000000000..ff88f3e6dd --- /dev/null +++ b/modules/core/misc/objc/common/MatOfRotatedRect.mm @@ -0,0 +1,87 @@ +// +// MatOfRotatedRect.mm +// +// Created by Giles Payne on 2019/12/27. +// + +#import "MatOfRotatedRect.h" +#import "Range.h" +#import "RotatedRect.h" +#import "Point2f.h" +#import "Size2f.h" +#import "CvType.h" +#import "ArrayUtil.h" + +@implementation MatOfRotatedRect + +static const int _depth = CV_32F; +static const int _channels = 5; + +#ifdef __cplusplus +- (instancetype)initWithNativeMat:(cv::Mat*)nativeMat { + self = [super initWithNativeMat:nativeMat]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} +#endif + +- (instancetype)initWithMat:(Mat*)mat { + self = [super initWithMat:mat rowRange:[Range all]]; + if (self && ![self empty] && [self checkVector:_channels depth:_depth] < 0) { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Incompatible Mat" userInfo:nil]; + } + return self; +} + +- (instancetype)initWithArray:(NSArray*)array { + self = [super init]; + if (self) { + [self fromArray:array]; + } + return self; +} + +- (void)alloc:(int)elemNumber { + if (elemNumber>0) { + [super create:elemNumber cols:1 type:[CvType makeType:_depth channels:_channels]]; + } +} + +- (void)fromArray:(NSArray*)array { + NSMutableArray* data = [[NSMutableArray alloc] initWithCapacity:array.count * _channels]; + for (int index = 0; index < (int)array.count; index++) { + data[_channels * index] = [NSNumber numberWithFloat:array[index].center.x]; + data[_channels * index + 1] = [NSNumber numberWithFloat:array[index].center.y]; + data[_channels * index + 2] = [NSNumber numberWithFloat:array[index].size.width]; + data[_channels * index + 3] = [NSNumber numberWithFloat:array[index].size.height]; + data[_channels * index + 4] = [NSNumber numberWithFloat:array[index].angle]; + + } + [self alloc:(int)array.count]; + [self put:0 col:0 data:data]; +} + +- (NSArray*)toArray { + int length = [self length] / _channels; + NSMutableArray* ret = createArrayWithSize(length, [RotatedRect new]); + if (length > 0) { + NSMutableArray* data = createArrayWithSize([self length], @0.0); + [self get:0 col:0 data:data]; + for (int index = 0; index < length; index++) { + ret[index] = [[RotatedRect alloc] initWithCenter:[[Point2f alloc] initWithX:data[index * _channels].floatValue y:data[index * _channels + 1].floatValue] size:[[Size2f alloc] initWithWidth:data[index * _channels + 2].floatValue height:data[index * _channels + 3].floatValue] angle:data[index * _channels + 4].floatValue]; + } + } + return ret; +} + +- (int)length { + int num = [self checkVector:_channels depth:_depth]; + if (num < 0) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Incompatible Mat" userInfo:nil]; + } + return num * _channels; +} + +@end diff --git a/modules/core/misc/objc/common/MinMaxLocResult.h b/modules/core/misc/objc/common/MinMaxLocResult.h new file mode 100644 index 0000000000..87593f2b18 --- /dev/null +++ b/modules/core/misc/objc/common/MinMaxLocResult.h @@ -0,0 +1,38 @@ +// +// MinMaxLocResult.h +// +// Created by Giles Payne on 2019/12/28. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Point2i; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Result of operation to determine global minimum and maximum of an array +*/ +@interface MinMaxLocResult : NSObject + +#pragma mark - Properties + +@property double minVal; +@property double maxVal; +@property Point2i* minLoc; +@property Point2i* maxLoc; + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithMinval:(double)minVal maxVal:(double)maxVal minLoc:(Point2i*)minLoc maxLoc:(Point2i*)maxLoc; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/MinMaxLocResult.mm b/modules/core/misc/objc/common/MinMaxLocResult.mm new file mode 100644 index 0000000000..1e4215bd3d --- /dev/null +++ b/modules/core/misc/objc/common/MinMaxLocResult.mm @@ -0,0 +1,27 @@ +// +// MinMaxLocResult.m +// +// Created by Giles Payne on 2019/12/28. +// + +#import "MinMaxLocResult.h" +#import "Point2i.h" + +@implementation MinMaxLocResult + +- (instancetype)init { + return [self initWithMinval:0 maxVal:0 minLoc:[Point2i new] maxLoc:[Point2i new]]; +} + +- (instancetype)initWithMinval:(double)minVal maxVal:(double)maxVal minLoc:(Point2i*)minLoc maxLoc:(Point2i*)maxLoc { + self = [super init]; + if (self) { + self.minVal = minVal; + self.maxVal = maxVal; + self.minLoc = minLoc; + self.maxLoc = maxLoc; + } + return self; +} + +@end diff --git a/modules/core/misc/objc/common/Point2d.h b/modules/core/misc/objc/common/Point2d.h new file mode 100644 index 0000000000..8187c074f8 --- /dev/null +++ b/modules/core/misc/objc/common/Point2d.h @@ -0,0 +1,87 @@ +// +// Point2d.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Rect2d; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a two dimensional point the coordinate values of which are of type `double` +*/ +@interface Point2d : NSObject + +# pragma mark - Properties + +@property double x; +@property double y; +#ifdef __cplusplus +@property(readonly) cv::Point2d& nativeRef; +#endif + +# pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(double)x y:(double)y; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Point2d&)point; +- (void)update:(cv::Point2d&)point; +#endif + +# pragma mark - Methods + +/** +* Calculate the dot product of this point and another point +* @param point The other point +*/ +- (double)dot:(Point2d*)point; + +/** +* Determine if the point lies with a specified rectangle +* @param rect The rectangle +*/ +- (BOOL)inside:(Rect2d*)rect; + +/** +* Set the point coordinates from the values of an array +* @param vals The array of values from which to set the coordinates +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +# pragma mark - Common Methods + +/** +* Clone object +*/ +- (Point2d*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Point2d.mm b/modules/core/misc/objc/common/Point2d.mm new file mode 100644 index 0000000000..6c75cd233b --- /dev/null +++ b/modules/core/misc/objc/common/Point2d.mm @@ -0,0 +1,107 @@ +// +// Point2d.m +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Point2d.h" +#import "Rect2d.h" +#import "CVObjcUtil.h" + +@implementation Point2d { + cv::Point2d native; +} + +- (double)x { + return native.x; +} + +- (void)setX:(double)val { + native.x = val; +} + +- (double)y { + return native.y; +} + +- (void)setY:(double)val { + native.y = val; +} + +- (cv::Point2d&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithX:0 y:0]; +} + +- (instancetype)initWithX:(double)x y:(double)y { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + } + return self; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Point2d&)point { + return [[Point2d alloc] initWithX:point.x y:point.y]; +} + +- (void)update:(cv::Point2d&)point { + self.x = point.x; + self.y = point.y; +} + +- (Point2d*) clone { + return [[Point2d alloc] initWithX:self.x y:self.y]; +} + +- (double)dot:(Point2d*)point { + return self.x * point.x + self.y * point.y; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0; + self.y = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Point2d class]]) { + return NO; + } else { + Point2d* point = (Point2d*)other; + return self.x == point.x && self.y == point.y; + } +} + +- (BOOL)inside:(Rect2d*)rect { + return [rect contains:self]; +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + int64_t temp = DOUBLE_TO_BITS(self.x); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.y); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Point2d {%lf,%lf}", self.x, self.y]; +} + +@end diff --git a/modules/core/misc/objc/common/Point2f.h b/modules/core/misc/objc/common/Point2f.h new file mode 100644 index 0000000000..6d7d0f9732 --- /dev/null +++ b/modules/core/misc/objc/common/Point2f.h @@ -0,0 +1,87 @@ +// +// Point2f.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Rect2f; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a two dimensional point the coordinate values of which are of type `float` +*/ +@interface Point2f : NSObject + +# pragma mark - Properties + +@property float x; +@property float y; +#ifdef __cplusplus +@property(readonly) cv::Point2f& nativeRef; +#endif + +# pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(float)x y:(float)y; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Point2f&)point; +- (void)update:(cv::Point2f&)point; +#endif + +# pragma mark - Methods + +/** +* Calculate the dot product of this point and another point +* @param point The other point +*/ +- (double)dot:(Point2f*)point; + +/** +* Determine if the point lies with a specified rectangle +* @param rect The rectangle +*/ +- (BOOL)inside:(Rect2f*)rect; + +/** +* Set the point coordinates from the values of an array +* @param vals The array of values from which to set the coordinates +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +# pragma mark - Common Methods + +/** +* Clone object +*/ +- (Point2f*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Point2f.mm b/modules/core/misc/objc/common/Point2f.mm new file mode 100644 index 0000000000..a43ed805b0 --- /dev/null +++ b/modules/core/misc/objc/common/Point2f.mm @@ -0,0 +1,105 @@ +// +// Point2f.m +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Point2f.h" +#import "Rect2f.h" +#import "CVObjcUtil.h" + +@implementation Point2f { + cv::Point2f native; +} + +- (float)x { + return native.x; +} + +- (void)setX:(float)val { + native.x = val; +} + +- (float)y { + return native.y; +} + +- (void)setY:(float)val { + native.y = val; +} + +- (cv::Point2f&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithX:0 y:0]; +} + +- (instancetype)initWithX:(float)x y:(float)y { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + } + return self; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Point2f&)point { + return [[Point2f alloc] initWithX:point.x y:point.y]; +} + +- (void)update:(cv::Point2f&)point { + self.x = point.x; + self.y = point.y; +} + +- (Point2f*) clone { + return [[Point2f alloc] initWithX:self.x y:self.y]; +} + +- (double)dot:(Point2f*)point { + return self.x * point.x + self.y * point.y; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0; + self.y = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Point2f class]]) { + return NO; + } else { + Point2f* point = (Point2f*)other; + return self.x == point.x && self.y == point.y; + } +} + +- (BOOL)inside:(Rect2f *)rect { + return [rect contains:self]; +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + FLOAT_TO_BITS(self.x); + result = prime * result + FLOAT_TO_BITS(self.x); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Point2f {%f,%f}", self.x, self.y]; +} + +@end diff --git a/modules/core/misc/objc/common/Point2i.h b/modules/core/misc/objc/common/Point2i.h new file mode 100644 index 0000000000..ac545d4ecc --- /dev/null +++ b/modules/core/misc/objc/common/Point2i.h @@ -0,0 +1,88 @@ +// +// Point2i.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Rect2i; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a two dimensional point the coordinate values of which are of type `int` +*/ +NS_SWIFT_NAME(Point) +@interface Point2i : NSObject + +# pragma mark - Properties + +@property int x; +@property int y; +#ifdef __cplusplus +@property(readonly) cv::Point2i& nativeRef; +#endif + +# pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(int)x y:(int)y; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Point2i&)point; +- (void)update:(cv::Point2i&)point; +#endif + +# pragma mark - Methods + +/** +* Calculate the dot product of this point and another point +* @param point The other point +*/ +- (double)dot:(Point2i*)point; + +/** +* Determine if the point lies with a specified rectangle +* @param rect The rectangle +*/ +- (BOOL)inside:(Rect2i*)rect; + +/** +* Set the point coordinates from the values of an array +* @param vals The array of values from which to set the coordinates +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +# pragma mark - Common Methods + +/** +* Clone object +*/ +- (Point2i*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Point2i.mm b/modules/core/misc/objc/common/Point2i.mm new file mode 100644 index 0000000000..bfbbc15e3a --- /dev/null +++ b/modules/core/misc/objc/common/Point2i.mm @@ -0,0 +1,105 @@ +// +// Point2i.m +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Point2i.h" +#import "Rect2i.h" +#import "CVObjcUtil.h" + +@implementation Point2i { + cv::Point2i native; +} + +- (int)x { + return native.x; +} + +- (void)setX:(int)val { + native.x = val; +} + +- (int)y { + return native.y; +} + +- (void)setY:(int)val { + native.y = val; +} + +- (cv::Point2i&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithX:0 y:0]; +} + +- (instancetype)initWithX:(int)x y:(int)y { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + } + return self; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Point2i&)point { + return [[Point2i alloc] initWithX:point.x y:point.y]; +} + +- (void)update:(cv::Point2i&)point { + self.x = point.x; + self.y = point.y; +} + +- (Point2i*) clone { + return [[Point2i alloc] initWithX:self.x y:self.y]; +} + +- (double)dot:(Point2i*)point { + return self.x * point.x + self.y * point.y; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0; + self.y = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Point2i class]]) { + return NO; + } else { + Point2i* point = (Point2i*)other; + return self.x == point.x && self.y == point.y; + } +} + +- (BOOL)inside:(Rect2i*)rect { + return [rect contains:self]; +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + self.x; + result = prime * result + self.y; + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Point2i {%d,%d}", self.x, self.y]; +} + +@end diff --git a/modules/core/misc/objc/common/Point3d.h b/modules/core/misc/objc/common/Point3d.h new file mode 100644 index 0000000000..271fbb1bd0 --- /dev/null +++ b/modules/core/misc/objc/common/Point3d.h @@ -0,0 +1,84 @@ +// +// Point3d.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Point2d; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a three dimensional point the coordinate values of which are of type `double` +*/ +@interface Point3d : NSObject + +# pragma mark - Properties + +@property double x; +@property double y; +@property double z; +#ifdef __cplusplus +@property(readonly) cv::Point3d& nativeRef; +#endif + +# pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(double)x y:(double)y z:(double)z; +- (instancetype)initWithPoint:(Point2d*)point; +- (instancetype)initWithVals:(NSArray*)vals; + +# pragma mark - Methods + +/** +* Calculate the dot product of this point and another point +* @param point The other point +*/ +- (double)dot:(Point3d*)point; + +/** +* Calculate the cross product of this point and another point +* @param point The other point +*/ +- (Point3d*)cross:(Point3d*)point; + +/** +* Set the point coordinates from the values of an array +* @param vals The array of values from which to set the coordinates +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +# pragma mark - Common Methods + +/** +* Clone object +*/ +- (Point3d*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString *)description; +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Point3d.mm b/modules/core/misc/objc/common/Point3d.mm new file mode 100644 index 0000000000..a4770ddec4 --- /dev/null +++ b/modules/core/misc/objc/common/Point3d.mm @@ -0,0 +1,114 @@ +// +// Point3d.mm +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Point3d.h" +#import "Point2d.h" +#import "CVObjcUtil.h" + +@implementation Point3d { + cv::Point3d native; +} + +- (double)x { + return native.x; +} + +- (void)setX:(double)val { + native.x = val; +} + +- (double)y { + return native.y; +} + +- (void)setY:(double)val { + native.y = val; +} + +- (double)z { + return native.z; +} + +- (void)setZ:(double)val { + native.z = val; +} + +- (cv::Point3d&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithX:0 y:0 z:0]; +} + +- (instancetype)initWithX:(double)x y:(double)y z:(double)z { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + self.z = z; + } + return self; +} + +- (instancetype)initWithPoint:(Point2d*)point { + return [self initWithX:point.x y:point.y z:0]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0.0; + self.y = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0.0; + self.z = (vals != nil && vals.count > 2) ? vals[2].doubleValue : 0.0; +} + +- (Point3d*) clone { + return [[Point3d alloc] initWithX:self.x y:self.y z:self.z]; +} + +- (double)dot:(Point3d*)point { + return self.x * point.x + self.y * point.y + self.z * point.z; +} + +- (Point3d*)cross:(Point3d*)point { + return [[Point3d alloc] initWithX:(self.y * point.z - self.z * point.y) y:(self.z * point.x - self.x * point.z) z:(self.x * point.y - self.y * point.x)]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Point3d class]]) { + return NO; + } else { + Point3d* point = (Point3d*)other; + return self.x == point.x && self.y == point.y && self.z == point.z; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + int64_t temp = DOUBLE_TO_BITS(self.x); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.y); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.z); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Point3 {%lf,%lf,%lf}", self.x, self.y, self.z]; +} + +@end diff --git a/modules/core/misc/objc/common/Point3f.h b/modules/core/misc/objc/common/Point3f.h new file mode 100644 index 0000000000..8f02aaeb4f --- /dev/null +++ b/modules/core/misc/objc/common/Point3f.h @@ -0,0 +1,85 @@ +// +// Point3f.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Point2f; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a three dimensional point the coordinate values of which are of type `float` +*/ +@interface Point3f : NSObject + +# pragma mark - Properties + +@property float x; +@property float y; +@property float z; +#ifdef __cplusplus +@property(readonly) cv::Point3f& nativeRef; +#endif + +# pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(float)x y:(float)y z:(float)z; +- (instancetype)initWithPoint:(Point2f*)point; +- (instancetype)initWithVals:(NSArray*)vals; + + +# pragma mark - Methods + +/** +* Calculate the dot product of this point and another point +* @param point The other point +*/ +- (double)dot:(Point3f*)point; + +/** +* Calculate the cross product of this point and another point +* @param point The other point +*/ +- (Point3f*)cross:(Point3f*)point; + +/** +* Set the point coordinates from the values of an array +* @param vals The array of values from which to set the coordinates +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +# pragma mark - Common Methods + +/** +* Clone object +*/ +- (Point3f*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString *)description; +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Point3f.mm b/modules/core/misc/objc/common/Point3f.mm new file mode 100644 index 0000000000..bd03230f4b --- /dev/null +++ b/modules/core/misc/objc/common/Point3f.mm @@ -0,0 +1,111 @@ +// +// Point3f.mm +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Point3f.h" +#import "Point2f.h" +#import "CVObjcUtil.h" + +@implementation Point3f { + cv::Point3f native; +} + +- (float)x { + return native.x; +} + +- (void)setX:(float)val { + native.x = val; +} + +- (float)y { + return native.y; +} + +- (void)setY:(float)val { + native.y = val; +} + +- (float)z { + return native.z; +} + +- (void)setZ:(float)val { + native.z = val; +} + +- (cv::Point3f&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithX:0 y:0 z:0]; +} + +- (instancetype)initWithX:(float)x y:(float)y z:(float)z { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + self.z = z; + } + return self; +} + +- (instancetype)initWithPoint:(Point2f*)point { + return [self initWithX:point.x y:point.y z:0]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].floatValue : 0.0; + self.y = (vals != nil && vals.count > 1) ? vals[1].floatValue : 0.0; + self.z = (vals != nil && vals.count > 2) ? vals[2].floatValue : 0.0; +} + +- (Point3f*) clone { + return [[Point3f alloc] initWithX:self.x y:self.y z:self.z]; +} + +- (double)dot:(Point3f*)point { + return self.x * point.x + self.y * point.y + self.z * point.z; +} + +- (Point3f*)cross:(Point3f*)point { + return [[Point3f alloc] initWithX:(self.y * point.z - self.z * point.y) y:(self.z * point.x - self.x * point.z) z:(self.x * point.y - self.y * point.x)]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Point3f class]]) { + return NO; + } else { + Point3f* point = (Point3f*)other; + return self.x == point.x && self.y == point.y && self.z == point.z; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + FLOAT_TO_BITS(self.x); + result = prime * result + FLOAT_TO_BITS(self.y); + result = prime * result + FLOAT_TO_BITS(self.z); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Point3f {%f,%f,%f}", self.x, self.y, self.z]; +} + +@end diff --git a/modules/core/misc/objc/common/Point3i.h b/modules/core/misc/objc/common/Point3i.h new file mode 100644 index 0000000000..5323cb8029 --- /dev/null +++ b/modules/core/misc/objc/common/Point3i.h @@ -0,0 +1,84 @@ +// +// Point3i.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +@class Point2i; + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a three dimensional point the coordinate values of which are of type `int` +*/ +@interface Point3i : NSObject + +# pragma mark - Properties + +@property int x; +@property int y; +@property int z; +#ifdef __cplusplus +@property(readonly) cv::Point3i& nativeRef; +#endif + +# pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(int)x y:(int)y z:(int)z; +- (instancetype)initWithPoint:(Point2i*)point; +- (instancetype)initWithVals:(NSArray*)vals; + +# pragma mark - Methods + +/** +* Calculate the dot product of this point and another point +* @param point The other point +*/ +- (double)dot:(Point3i*)point; + +/** +* Calculate the cross product of this point and another point +* @param point The other point +*/ +- (Point3i*)cross:(Point3i*)point; + +/** +* Set the point coordinates from the values of an array +* @param vals The array of values from which to set the coordinates +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +# pragma mark - Common Methods + +/** +* Clone object +*/ +- (Point3i*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)other; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString *)description; +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Point3i.mm b/modules/core/misc/objc/common/Point3i.mm new file mode 100644 index 0000000000..8218f2b3c5 --- /dev/null +++ b/modules/core/misc/objc/common/Point3i.mm @@ -0,0 +1,111 @@ +// +// Point3i.mm +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Point3i.h" +#import "Point2i.h" +#import "CVObjcUtil.h" + +@implementation Point3i { + cv::Point3i native; +} + +- (int)x { + return native.x; +} + +- (void)setX:(int)val { + native.x = val; +} + +- (int)y { + return native.y; +} + +- (void)setY:(int)val { + native.y = val; +} + +- (int)z { + return native.z; +} + +- (void)setZ:(int)val { + native.z = val; +} + +- (cv::Point3i&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithX:0 y:0 z:0]; +} + +- (instancetype)initWithX:(int)x y:(int)y z:(int)z { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + self.z = z; + } + return self; +} + +- (instancetype)initWithPoint:(Point2i*)point { + return [self initWithX:point.x y:point.y z:0]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].intValue : 0; + self.y = (vals != nil && vals.count > 1) ? vals[1].intValue : 0; + self.z = (vals != nil && vals.count > 2) ? vals[2].intValue : 0; +} + +- (Point3i*) clone { + return [[Point3i alloc] initWithX:self.x y:self.y z:self.z]; +} + +- (double)dot:(Point3i*)point { + return self.x * point.x + self.y * point.y + self.z * point.z; +} + +- (Point3i*)cross:(Point3i*)point { + return [[Point3i alloc] initWithX:(self.y * point.z - self.z * point.y) y:(self.z * point.x - self.x * point.z) z:(self.x * point.y - self.y * point.x)]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Point3i class]]) { + return NO; + } else { + Point3i* point = (Point3i*)other; + return self.x == point.x && self.y == point.y && self.z == point.z; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + self.x; + result = prime * result + self.y; + result = prime * result + self.z; + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Point3i {%d,%d,%d}", self.x, self.y, self.z]; +} + +@end diff --git a/modules/core/misc/objc/common/Range.h b/modules/core/misc/objc/common/Range.h new file mode 100644 index 0000000000..ba0aa549e3 --- /dev/null +++ b/modules/core/misc/objc/common/Range.h @@ -0,0 +1,93 @@ +// +// Range.h +// +// Created by Giles Payne on 2019/10/08. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a range of dimension indices +*/ +@interface Range : NSObject + +#pragma mark - Properties + +@property int start; +@property int end; + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithStart:(int)start end:(int)end; +- (instancetype)initWithVals:(NSArray*)vals; + +#pragma mark - Methods + +/** +* The size of the range +*/ +- (int)size; + +/** +* Determines if the range is empty +*/ +- (BOOL)empty; + +/** +* Creates a range representing all possible indices for a particular dimension +*/ ++ (Range*)all; + +/** +* Calculates the intersection of the range with another range +* @param r1 The other range +*/ +- (Range*)intersection:(Range*)r1; + +/** +* Adjusts each of the range limts +* @param delta The amount of the adjustment +*/ +- (Range*)shift:(int)delta; + +/** +* Set the range limits from the values of an array +* @param vals The array of values from which to set the range limits +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +# pragma mark - Common Methods + +/** +* Clone object +*/ +- (Range*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Range.m b/modules/core/misc/objc/common/Range.m new file mode 100644 index 0000000000..a4e155214e --- /dev/null +++ b/modules/core/misc/objc/common/Range.m @@ -0,0 +1,86 @@ +// +// Range.m +// +// Created by Giles Payne on 2019/10/08. +// + +#import "Range.h" + +@implementation Range + +- (instancetype)init { + return [self initWithStart:0 end: 0]; +} + +- (instancetype)initWithStart:(int)start end:(int)end { + self = [super init]; + if (self != nil) { + self.start = start; + self.end = end; + } + return self; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [self init]; + if (self != nil) { + [self set:vals]; + } + return self; +} + +- (void)set:(NSArray*)vals { + self.start = (vals != nil && vals.count > 0) ? vals[0].intValue : 0; + self.end = (vals != nil && vals.count > 1 ) ? vals[1].intValue : 0; +} + +- (int)size { + return [self empty] ? 0 : self.end - self.start; +} + +- (BOOL)empty { + return self.end <= self.start; +} + ++ (Range*)all { + return [[Range alloc] initWithStart:INT_MIN end:INT_MAX]; +} + +- (Range*)intersection:(Range*)r1 { + Range* out = [[Range alloc] initWithStart:MAX(r1.start, self.start) end:MIN(r1.end, self.end)]; + out.end = MAX(out.end, out.start); + return out; +} + +- (Range*)shift:(int)delta { + return [[Range alloc] initWithStart:self.start + delta end:self.end + delta]; +} + +- (Range*)clone { + return [[Range alloc] initWithStart:self.start end:self.end]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Range class]]) { + return NO; + } else { + Range* it = (Range*)other; + return self.start == it.start && self.end == it.end; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + self.start; + result = prime * result + self.end; + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Range {%d, %d}", self.start, self.end]; +} + +@end diff --git a/modules/core/misc/objc/common/Rect2d.h b/modules/core/misc/objc/common/Rect2d.h new file mode 100644 index 0000000000..c1d944247e --- /dev/null +++ b/modules/core/misc/objc/common/Rect2d.h @@ -0,0 +1,111 @@ +// +// Rect.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +@class Point2d; +@class Size2d; + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a rectange the coordinate and dimension values of which are of type `double` +*/ +@interface Rect2d : NSObject + +#pragma mark - Properties + +@property double x; +@property double y; +@property double width; +@property double height; +#ifdef __cplusplus +@property(readonly) cv::Rect2d& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(double)x y:(double)y width:(double)width height:(double)height; +- (instancetype)initWithPoint:(Point2d*)point1 point:(Point2d*)point2; +- (instancetype)initWithPoint:(Point2d*)point size:(Size2d*)size; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Rect2d&)point; +#endif + +#pragma mark - Methods + +/** +* Returns the top left coordinate of the rectangle +*/ +- (Point2d*)tl; + +/** +* Returns the bottom right coordinate of the rectangle +*/ +- (Point2d*)br; + +/** +* Returns the size of the rectangle +*/ +- (Size2d*)size; + +/** +* Returns the area of the rectangle +*/ +- (double)area; + +/** +* Determines if the rectangle is empty +*/ +- (BOOL)empty; + +/** +* Determines if the rectangle contains a given point +* @param point The point +*/ +- (BOOL)contains:(Point2d*)point; + +/** +* Set the rectangle coordinates and dimensions from the values of an array +* @param vals The array of values from which to set the rectangle coordinates and dimensions +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (Rect2d*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Rect2d.mm b/modules/core/misc/objc/common/Rect2d.mm new file mode 100644 index 0000000000..20bd830ba5 --- /dev/null +++ b/modules/core/misc/objc/common/Rect2d.mm @@ -0,0 +1,155 @@ +// +// Rect2d.mm +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Rect2d.h" +#import "Point2d.h" +#import "Size2d.h" +#import "CVObjcUtil.h" + +@implementation Rect2d { + cv::Rect2d native; +} + +- (double)x { + return native.x; +} + +- (void)setX:(double)val { + native.x = val; +} + +- (double)y { + return native.y; +} + +- (void)setY:(double)val { + native.y = val; +} + +- (double)width { + return native.width; +} + +- (void)setWidth:(double)val { + native.width = val; +} + +- (double)height { + return native.height; +} + +- (void)setHeight:(double)val { + native.height = val; +} + +- (cv::Rect2d&)nativeRef { + return native; +} + +- (instancetype)initWithX:(double)x y:(double)y width:(double)width height:(double)height { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + self.width = width; + self.height = height; + } + return self; +} + +- (instancetype)init { + return [self initWithX:0 y:0 width:0 height:0]; +} + +- (instancetype)initWithPoint:(Point2d*)point1 point:(Point2d*)point2 { + int x = (point1.x < point2.x ? point1.x : point2.x); + int y = (point1.y < point2.y ? point1.y : point2.y); + int width = (point1.x > point2.x ? point1.x : point2.x) - x; + int height = (point1.y > point2.y ? point1.y : point2.y) - y; + return [self initWithX:x y:y width:width height:height]; +} + +- (instancetype)initWithPoint:(Point2d*)point size:(Size2d*)size { + return [self initWithX:point.x y:point.y width:size.width height:size.height]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Rect2d&)rect { + return [[Rect2d alloc] initWithX:rect.x y:rect.y width:rect.width height:rect.height]; +} + +- (Rect2d*)clone { + return [[Rect2d alloc] initWithX:self.x y:self.y width:self.width height:self.height]; +} + +- (Point2d*)tl { + return [[Point2d alloc] initWithX:self.x y:self.y]; +} + +- (Point2d*)br { + return [[Point2d alloc] initWithX:self.x + self.width y:self.y + self.height]; +} + +- (Size2d*)size { + return [[Size2d alloc] initWithWidth:self.width height:self.height]; +} + +- (double)area { + return self.width * self.height; +} + +- (BOOL)empty { + return self.width <= 0 || self.height <= 0; +} + +- (BOOL)contains:(Point2d*)point { + return self.x <= point.x && point.x < self.x + self.width && self.y <= point.y && point.y < self.y + self.height; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].intValue : 0; + self.y = (vals != nil && vals.count > 1) ? vals[1].intValue : 0; + self.width = (vals != nil && vals.count > 2) ? vals[2].intValue : 0; + self.height = (vals != nil && vals.count > 3) ? vals[3].intValue : 0; +} + +- (BOOL)isEqual:(id)other{ + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Rect2d class]]) { + return NO; + } else { + Rect2d* rect = (Rect2d*)other; + return self.x == rect.x && self.y == rect.y && self.width == rect.width && self.height == rect.height; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + int64_t temp = DOUBLE_TO_BITS(self.x); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.y); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.width); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.height); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Rect2d {%lf,%lf,%lf,%lf}", self.x, self.y, self.width, self.height]; +} + +@end diff --git a/modules/core/misc/objc/common/Rect2f.h b/modules/core/misc/objc/common/Rect2f.h new file mode 100644 index 0000000000..9ed5f40981 --- /dev/null +++ b/modules/core/misc/objc/common/Rect2f.h @@ -0,0 +1,111 @@ +// +// Rect.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +@class Point2f; +@class Size2f; + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a rectange the coordinate and dimension values of which are of type `float` +*/ +@interface Rect2f : NSObject + +#pragma mark - Properties + +@property float x; +@property float y; +@property float width; +@property float height; +#ifdef __cplusplus +@property(readonly) cv::Rect2f& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(float)x y:(float)y width:(float)width height:(float)height; +- (instancetype)initWithPoint:(Point2f*)point1 point:(Point2f*)point2; +- (instancetype)initWithPoint:(Point2f*)point size:(Size2f*)size; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Rect2f&)point; +#endif + +#pragma mark - Methods + +/** +* Returns the top left coordinate of the rectangle +*/ +- (Point2f*)tl; + +/** +* Returns the bottom right coordinate of the rectangle +*/ +- (Point2f*)br; + +/** +* Returns the size of the rectangle +*/ +- (Size2f*)size; + +/** +* Returns the area of the rectangle +*/ +- (double)area; + +/** +* Determines if the rectangle is empty +*/ +- (BOOL)empty; + +/** +* Determines if the rectangle contains a given point +* @param point The point +*/ +- (BOOL)contains:(Point2f*)point; + +/** +* Set the rectangle coordinates and dimensions from the values of an array +* @param vals The array of values from which to set the rectangle coordinates and dimensions +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (Rect2f*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Rect2f.mm b/modules/core/misc/objc/common/Rect2f.mm new file mode 100644 index 0000000000..6f8b7c6fc4 --- /dev/null +++ b/modules/core/misc/objc/common/Rect2f.mm @@ -0,0 +1,151 @@ +// +// Rect2d.mm +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Rect2f.h" +#import "Point2f.h" +#import "Size2f.h" +#import "CVObjcUtil.h" + +@implementation Rect2f { + cv::Rect2f native; +} + +- (float)x { + return native.x; +} + +- (void)setX:(float)val { + native.x = val; +} + +- (float)y { + return native.y; +} + +- (void)setY:(float)val { + native.y = val; +} + +- (float)width { + return native.width; +} + +- (void)setWidth:(float)val { + native.width = val; +} + +- (float)height { + return native.height; +} + +- (void)setHeight:(float)val { + native.height = val; +} + +- (cv::Rect2f&)nativeRef { + return native; +} + +- (instancetype)initWithX:(float)x y:(float)y width:(float)width height:(float)height { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + self.width = width; + self.height = height; + } + return self; +} + +- (instancetype)init { + return [self initWithX:0 y:0 width:0 height:0]; +} + +- (instancetype)initWithPoint:(Point2f*)point1 point:(Point2f*)point2 { + int x = (point1.x < point2.x ? point1.x : point2.x); + int y = (point1.y < point2.y ? point1.y : point2.y); + int width = (point1.x > point2.x ? point1.x : point2.x) - x; + int height = (point1.y > point2.y ? point1.y : point2.y) - y; + return [self initWithX:x y:y width:width height:height]; +} + +- (instancetype)initWithPoint:(Point2f*)point size:(Size2f*)size { + return [self initWithX:point.x y:point.y width:size.width height:size.height]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Rect2f&)rect { + return [[Rect2f alloc] initWithX:rect.x y:rect.y width:rect.width height:rect.height]; +} + +- (Rect2f*)clone { + return [[Rect2f alloc] initWithX:self.x y:self.y width:self.width height:self.height]; +} + +- (Point2f*)tl { + return [[Point2f alloc] initWithX:self.x y:self.y]; +} + +- (Point2f*)br { + return [[Point2f alloc] initWithX:self.x + self.width y:self.y + self.height]; +} + +- (Size2f*)size { + return [[Size2f alloc] initWithWidth:self.width height:self.height]; +} + +- (double)area { + return self.width * self.height; +} + +- (BOOL)empty { + return self.width <= 0 || self.height <= 0; +} + +- (BOOL)contains:(Point2f*)point { + return self.x <= point.x && point.x < self.x + self.width && self.y <= point.y && point.y < self.y + self.height; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].floatValue : 0; + self.y = (vals != nil && vals.count > 1) ? vals[1].floatValue : 0; + self.width = (vals != nil && vals.count > 2) ? vals[2].floatValue : 0; + self.height = (vals != nil && vals.count > 3) ? vals[3].floatValue : 0; +} + +- (BOOL)isEqual:(id)other{ + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Rect2f class]]) { + return NO; + } else { + Rect2f* rect = (Rect2f*)other; + return self.x == rect.x && self.y == rect.y && self.width == rect.width && self.height == rect.height; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + FLOAT_TO_BITS(self.x); + result = prime * result + FLOAT_TO_BITS(self.y); + result = prime * result + FLOAT_TO_BITS(self.width); + result = prime * result + FLOAT_TO_BITS(self.height); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Rect2f {%lf,%lf,%lf,%lf}", self.x, self.y, self.width, self.height]; +} + +@end diff --git a/modules/core/misc/objc/common/Rect2i.h b/modules/core/misc/objc/common/Rect2i.h new file mode 100644 index 0000000000..a94a547226 --- /dev/null +++ b/modules/core/misc/objc/common/Rect2i.h @@ -0,0 +1,112 @@ +// +// Rect2i.h +// +// Created by Giles Payne on 2019/10/09. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +@class Point2i; +@class Size2i; + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a rectange the coordinate and dimension values of which are of type `int` +*/ +NS_SWIFT_NAME(Rect) +@interface Rect2i : NSObject + +#pragma mark - Properties + +@property int x; +@property int y; +@property int width; +@property int height; +#ifdef __cplusplus +@property(readonly) cv::Rect2i& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithX:(int)x y:(int)y width:(int)width height:(int)height; +- (instancetype)initWithPoint:(Point2i*)point1 point:(Point2i*)point2; +- (instancetype)initWithPoint:(Point2i*)point size:(Size2i*)size; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Rect&)point; +#endif + +#pragma mark - Methods + +/** +* Returns the top left coordinate of the rectangle +*/ +- (Point2i*)tl; + +/** +* Returns the bottom right coordinate of the rectangle +*/ +- (Point2i*)br; + +/** +* Returns the size of the rectangle +*/ +- (Size2i*)size; + +/** +* Returns the area of the rectangle +*/ +- (double)area; + +/** +* Determines if the rectangle is empty +*/ +- (BOOL)empty; + +/** +* Determines if the rectangle contains a given point +* @param point The point +*/ +- (BOOL)contains:(Point2i*)point; + +/** +* Set the rectangle coordinates and dimensions from the values of an array +* @param vals The array of values from which to set the rectangle coordinates and dimensions +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (Rect2i*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Rect2i.mm b/modules/core/misc/objc/common/Rect2i.mm new file mode 100644 index 0000000000..75bbd31664 --- /dev/null +++ b/modules/core/misc/objc/common/Rect2i.mm @@ -0,0 +1,150 @@ +// +// Rect2i.m +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Rect2i.h" +#import "Point2i.h" +#import "Size2i.h" + +@implementation Rect2i { + cv::Rect2i native; +} + +- (int)x { + return native.x; +} + +- (void)setX:(int)val { + native.x = val; +} + +- (int)y { + return native.y; +} + +- (void)setY:(int)val { + native.y = val; +} + +- (int)width { + return native.width; +} + +- (void)setWidth:(int)val { + native.width = val; +} + +- (int)height { + return native.height; +} + +- (void)setHeight:(int)val { + native.height = val; +} + +- (cv::Rect&)nativeRef { + return native; +} + +- (instancetype)initWithX:(int)x y:(int)y width:(int)width height:(int)height { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + self.width = width; + self.height = height; + } + return self; +} + +- (instancetype)init { + return [self initWithX:0 y:0 width:0 height:0]; +} + +- (instancetype)initWithPoint:(Point2i*)point1 point:(Point2i*)point2 { + int x = (point1.x < point2.x ? point1.x : point2.x); + int y = (point1.y < point2.y ? point1.y : point2.y); + int width = (point1.x > point2.x ? point1.x : point2.x) - x; + int height = (point1.y > point2.y ? point1.y : point2.y) - y; + return [self initWithX:x y:y width:width height:height]; +} + +- (instancetype)initWithPoint:(Point2i*)point size:(Size2i*)size { + return [self initWithX:point.x y:point.y width:size.width height:size.height]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Rect&)rect { + return [[Rect2i alloc] initWithX:rect.x y:rect.y width:rect.width height:rect.height]; +} + +- (Rect2i*)clone { + return [[Rect2i alloc] initWithX:self.x y:self.y width:self.width height:self.height]; +} + +- (Point2i*)tl { + return [[Point2i alloc] initWithX:self.x y:self.y]; +} + +- (Point2i*)br { + return [[Point2i alloc] initWithX:self.x + self.width y:self.y + self.height]; +} + +- (Size2i*)size { + return [[Size2i alloc] initWithWidth:self.width height:self.height]; +} + +- (double)area { + return self.width * self.height; +} + +- (BOOL)empty { + return self.width <= 0 || self.height <= 0; +} + +- (BOOL)contains:(Point2i*)point { + return self.x <= point.x && point.x < self.x + self.width && self.y <= point.y && point.y < self.y + self.height; +} + +- (void)set:(NSArray*)vals { + self.x = (vals != nil && vals.count > 0) ? vals[0].intValue : 0; + self.y = (vals != nil && vals.count > 1) ? vals[1].intValue : 0; + self.width = (vals != nil && vals.count > 2) ? vals[2].intValue : 0; + self.height = (vals != nil && vals.count > 3) ? vals[3].intValue : 0; +} + +- (BOOL)isEqual:(id)other{ + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Rect2i class]]) { + return NO; + } else { + Rect2i* rect = (Rect2i*)other; + return self.x == rect.x && self.y == rect.y && self.width == rect.width && self.height == rect.height; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + self.x; + result = prime * result + self.y; + result = prime * result + self.width; + result = prime * result + self.height; + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Rect2i {%d,%d,%d,%d}", self.x, self.y, self.width, self.height]; +} + +@end diff --git a/modules/core/misc/objc/common/RotatedRect.h b/modules/core/misc/objc/common/RotatedRect.h new file mode 100644 index 0000000000..5571e739ee --- /dev/null +++ b/modules/core/misc/objc/common/RotatedRect.h @@ -0,0 +1,86 @@ +// +// RotatedRect.h +// +// Created by Giles Payne on 2019/12/26. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +@class Point2f; +@class Size2f; +@class Rect2f; + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a rotated rectangle on a plane +*/ +@interface RotatedRect : NSObject + +#pragma mark - Properties + +@property Point2f* center; +@property Size2f* size; +@property double angle; +#ifdef __cplusplus +@property(readonly) cv::RotatedRect& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithCenter:(Point2f*)center size:(Size2f*)size angle:(double)angle; +- (instancetype)initWithVals:(NSArray*)vals; +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::RotatedRect&)rotatedRect; +#endif + +#pragma mark - Methods +/** +* Returns the corner points of the rotated rectangle as an array +*/ +- (NSArray*)points; + +/** +* Returns the bounding (non-rotated) rectangle of the rotated rectangle +*/ +- (Rect2f*)boundingRect; + +/** +* Set the rotated rectangle coordinates, dimensions and angle of rotation from the values of an array +* @param vals The array of values from which to set the rotated rectangle coordinates, dimensions and angle of rotation +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (RotatedRect*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/RotatedRect.mm b/modules/core/misc/objc/common/RotatedRect.mm new file mode 100644 index 0000000000..e35002e1b3 --- /dev/null +++ b/modules/core/misc/objc/common/RotatedRect.mm @@ -0,0 +1,113 @@ +// +// RotatedRect.m +// +// Created by Giles Payne on 2019/12/26. +// + +#import "RotatedRect.h" +#import "Point2f.h" +#import "Size2f.h" +#import "Rect2f.h" +#import "CVObjcUtil.h" + +#include + +@implementation RotatedRect { + cv::RotatedRect native; +} + +- (cv::RotatedRect&)nativeRef { + native.center.x = self.center.x; + native.center.y = self.center.y; + native.size.width = self.size.width; + native.size.height = self.size.height; + native.angle = self.angle; + return native; +} + +- (instancetype)init { + return [self initWithCenter:[Point2f new] size:[Size2f new] angle:0.0]; +} + +- (instancetype)initWithCenter:(Point2f*)center size:(Size2f*)size angle:(double)angle { + self = [super init]; + if (self) { + self.center = center; + self.size = size; + self.angle = angle; + } + return self; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [self init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::RotatedRect&)rotatedRect { + return [[RotatedRect alloc] initWithCenter:[Point2f fromNative:rotatedRect.center] size:[Size2f fromNative:rotatedRect.size] angle:rotatedRect.angle]; +} + +- (void)set:(NSArray*)vals { + self.center.x = (vals != nil && vals.count > 0) ? vals[0].floatValue : 0.0; + self.center.y = (vals != nil && vals.count > 1) ? vals[1].floatValue : 0.0; + self.size.width = (vals != nil && vals.count > 2) ? vals[2].floatValue : 0.0; + self.size.height = (vals != nil && vals.count > 3) ? vals[3].floatValue : 0.0; + self.angle = (vals != nil && vals.count > 4) ? vals[4].doubleValue : 0.0; +} + +- (NSArray*)points { + double angleRadians = self.angle * M_PI / 180.0; + double b = cos(angleRadians) * 0.5; + double a = sin(angleRadians) * 0.5f; + + Point2f* p0 = [[Point2f alloc] initWithX:self.center.x - a * self.size.height - b * self.size.width y:self.center.y + b * self.size.height - a * self.size.width]; + Point2f* p1 = [[Point2f alloc] initWithX:self.center.x + a * self.size.height - b * self.size.width y:self.center.y - b * self.size.height - a * self.size.width]; + Point2f* p2 = [[Point2f alloc] initWithX:2 * self.center.x - p0.x y:2 * self.center.y - p0.y]; + Point2f* p3 = [[Point2f alloc] initWithX:2 * self.center.x - p1.x y:2 * self.center.y - p1.y]; + return [NSArray arrayWithObjects:p0, p1, p2, p3, nil]; +} + +- (Rect2f*)boundingRect { + NSArray* pts = [self points]; + Rect2f* rect = [[Rect2f alloc] initWithX:(int)floor(MIN(MIN(MIN(pts[0].x, pts[1].x), pts[2].x), pts[3].x)) y:(int)floor(MIN(MIN(MIN(pts[0].y, pts[1].y), pts[2].y), pts[3].y)) width:(int)ceil(MAX(MAX(MAX(pts[0].x, pts[1].x), pts[2].x), pts[3].x)) height:(int)ceil(MAX(MAX(MAX(pts[0].y, pts[1].y), pts[2].y), pts[3].y))]; + rect.width -= rect.x - 1; + rect.height -= rect.y - 1; + return rect; +} + +- (RotatedRect*)clone { + return [[RotatedRect alloc] initWithCenter:[self.center clone] size:[self.size clone] angle:self.angle]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[RotatedRect class]]) { + return NO; + } else { + RotatedRect* rect = (RotatedRect*)other; + return [self.center isEqual:rect.center] && [self.size isEqual:rect.size] && self.angle == rect.angle; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + FLOAT_TO_BITS(self.center.x); + result = prime * result + FLOAT_TO_BITS(self.center.y); + result = prime * result + FLOAT_TO_BITS(self.size.width); + result = prime * result + FLOAT_TO_BITS(self.size.height); + int64_t temp = DOUBLE_TO_BITS(self.angle); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + return result; +} + +- (NSString*)description { + return [NSString stringWithFormat:@"RotatedRect {%@,%@,%lf}", self.center.description, self.size.description, self.angle]; +} + +@end diff --git a/modules/core/misc/objc/common/Scalar.h b/modules/core/misc/objc/common/Scalar.h new file mode 100644 index 0000000000..ebf54c48f6 --- /dev/null +++ b/modules/core/misc/objc/common/Scalar.h @@ -0,0 +1,96 @@ +// +// Scalar.h +// +// Created by Giles Payne on 2019/10/06. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents a four element vector +*/ +@interface Scalar : NSObject + +#pragma mark - Properties + +@property(readonly) NSArray* val; +#ifdef __cplusplus +@property(readonly) cv::Scalar& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)initWithVals:(NSArray*)vals; +- (instancetype)initWithV0:(double)v0 v1:(double)v1 v2:(double)v2 v3:(double)v3 NS_SWIFT_NAME(init(_:_:_:_:)); +- (instancetype)initWithV0:(double)v0 v1:(double)v1 v2:(double)v2 NS_SWIFT_NAME(init(_:_:_:)); +- (instancetype)initWithV0:(double)v0 v1:(double)v1 NS_SWIFT_NAME(init(_:_:)); +- (instancetype)initWithV0:(double)v0 NS_SWIFT_NAME(init(_:)); +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Scalar&)nativeScalar; +#endif + +#pragma mark - Methods + +/** +* Creates a scalar with all elements of the same value +* @param v The value to set each element to +*/ ++ (Scalar*)all:(double)v; + +/** +* Calculates per-element product with another Scalar and a scale factor +* @param it The other Scalar +* @param scale The scale factor +*/ +- (Scalar*)mul:(Scalar*)it scale:(double)scale; + +/** +* Calculates per-element product with another Scalar +* @param it The other Scalar +*/ +- (Scalar*)mul:(Scalar*)it; + +/** +* Returns (v0, -v1, -v2, -v3) +*/ +- (Scalar*)conj; + +/** +* Returns true iff v1 == v2 == v3 == 0 +*/ +- (BOOL)isReal; + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (Scalar*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Scalar.mm b/modules/core/misc/objc/common/Scalar.mm new file mode 100644 index 0000000000..29a18a7991 --- /dev/null +++ b/modules/core/misc/objc/common/Scalar.mm @@ -0,0 +1,117 @@ +// +// Scalar.mm +// +// Created by Giles Payne on 2019/10/06. +// + +#import "Scalar.h" +#import "CVObjcUtil.h" + +double getVal(NSArray* vals, int index) { + return [vals count] > index ? vals[index].doubleValue : 0; +} + +@implementation Scalar { + cv::Scalar native; +} + +- (NSArray*)val { + return @[[NSNumber numberWithDouble:native.val[0]], [NSNumber numberWithDouble:native.val[1]], [NSNumber numberWithDouble:native.val[2]], [NSNumber numberWithDouble:native.val[3]]]; +} + +#ifdef __cplusplus +- (cv::Scalar&)nativeRef { + return native; +} +#endif + +- (instancetype)initWithVals:(NSArray *)vals { + return [self initWithV0:getVal(vals, 0) v1:getVal(vals, 1) v2:getVal(vals, 2) v3:getVal(vals, 3)]; +} + +- (instancetype)initWithV0:(double)v0 v1:(double)v1 v2:(double)v2 v3:(double)v3 { + self = [super init]; + if (self != nil) { + native.val[0] = v0; + native.val[1] = v1; + native.val[2] = v2; + native.val[3] = v3; + } + return self; +} + +- (instancetype)initWithV0:(double)v0 v1:(double)v1 v2:(double)v2 { + return [self initWithV0:v0 v1:v1 v2:v2 v3:0]; +} + +- (instancetype)initWithV0:(double)v0 v1:(double)v1 { + return [self initWithV0:v0 v1:v1 v2:0 v3:0]; +} + +- (instancetype)initWithV0:(double)v0 { + return [self initWithV0:v0 v1:0 v2:0 v3:0]; +} + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Scalar&)nativeScalar { + return [[Scalar alloc] initWithV0:nativeScalar.val[0] v1:nativeScalar.val[1] v2:nativeScalar.val[2] v3:nativeScalar.val[3]]; +} +#endif + ++ (Scalar*)all:(double)v { + return [[Scalar alloc] initWithV0:v v1:v v2:v v3:v]; +} + +- (Scalar*)clone { + return [Scalar fromNative:self.nativeRef]; +} + +- (Scalar*)mul:(Scalar*)it scale:(double)scale { + return [[Scalar alloc] initWithV0:self.nativeRef.val[0]*it.nativeRef.val[0]*scale v1:self.nativeRef.val[1]*it.nativeRef.val[1]*scale v2:self.nativeRef.val[2]*it.nativeRef.val[2]*scale v3:self.nativeRef.val[3]*it.nativeRef.val[3]*scale]; +} + +- (Scalar*)mul:(Scalar*)it { + return [self mul:it scale:1]; +} + +- (Scalar*)conj { + return [[Scalar alloc] initWithV0:self.nativeRef.val[0] v1:-self.nativeRef.val[1] v2:-self.nativeRef.val[2] v3:-self.nativeRef.val[3]]; +} + +- (BOOL)isReal { + return self.nativeRef.val[1] == self.nativeRef.val[2] == self.nativeRef.val[3] == 0; +} + +- (BOOL)isEqual:(id)other +{ + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Scalar class]]) { + return NO; + } else { + Scalar* it = (Scalar*) other; + return it.nativeRef.val[0] == self.nativeRef.val[0] && it.nativeRef.val[1] == self.nativeRef.val[1] && it.nativeRef.val[2] == self.nativeRef.val[2] && it.nativeRef.val[3] == self.nativeRef.val[3]; + } +} + +- (NSUInteger)hash +{ + int prime = 31; + uint32_t result = 1; + int64_t temp = DOUBLE_TO_BITS(self.nativeRef.val[0]); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.nativeRef.val[1]); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.nativeRef.val[2]); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.nativeRef.val[3]); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + return result; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"Scalar [%lf, %lf, %lf, %lf]", self.nativeRef.val[0], self.nativeRef.val[1], self.nativeRef.val[2], self.nativeRef.val[3]]; +} + +@end diff --git a/modules/core/misc/objc/common/Size2d.h b/modules/core/misc/objc/common/Size2d.h new file mode 100644 index 0000000000..233f4c29ae --- /dev/null +++ b/modules/core/misc/objc/common/Size2d.h @@ -0,0 +1,87 @@ +// +// Size2d.h +// +// Created by Giles Payne on 2019/10/06. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +@class Point2d; + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents the dimensions of a rectangle the values of which are of type `double` +*/ +@interface Size2d : NSObject + +#pragma mark - Properties + +@property double width; +@property double height; +#ifdef __cplusplus +@property(readonly) cv::Size2d& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithWidth:(double)width height:(double)height; +- (instancetype)initWithPoint:(Point2d*)point; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Size2d&)size; +#endif ++ (instancetype)width:(double)width height:(double)height; + +#pragma mark - Methods + +/** +* Returns the area of a rectangle with corresponding dimensions +*/ +- (double)area; + +/** +* Determines if a rectangle with corresponding dimensions has area of zero +*/ +- (BOOL)empty; + +/** +* Set the dimensions from the values of an array +* @param vals The array of values from which to set the dimensions +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (Size2d*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Size2d.mm b/modules/core/misc/objc/common/Size2d.mm new file mode 100644 index 0000000000..cb9a8c25ea --- /dev/null +++ b/modules/core/misc/objc/common/Size2d.mm @@ -0,0 +1,110 @@ +// +// Size2d.mm +// +// Created by Giles Payne on 2019/10/06. +// + +#import "Size2d.h" +#import "Point2d.h" +#import "CVObjcUtil.h" + +@implementation Size2d { + cv::Size2d native; +} + +- (double)width { + return native.width; +} + +- (void)setWidth:(double)val { + native.width = val; +} + +- (double)height { + return native.height; +} + +- (void)setHeight:(double)val { + native.height = val; +} + +- (cv::Size2d&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithWidth:0 height:0]; +} + +- (instancetype)initWithWidth:(double)width height:(double)height { + self = [super init]; + if (self) { + self.width = width; + self.height = height; + } + return self; +} + +- (instancetype)initWithPoint:(Point2d*)point { + return [self initWithWidth:point.x height:point.y]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Size2d&)size { + return [[Size2d alloc] initWithWidth:size.width height:size.height]; +} + ++ (instancetype)width:(double)width height:(double)height { + return [[Size2d alloc] initWithWidth:width height:height]; +} + +- (double)area { + return self.width * self.height; +} + +- (BOOL)empty { + return self.width <= 0 || self.height <= 0; +} + +- (void)set:(NSArray*)vals { + self.width = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0; + self.height = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0; +} + +- (Size2d*)clone { + return [[Size2d alloc] initWithWidth:self.width height:self.height]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Size2d class]]) { + return NO; + } else { + Size2d* it = (Size2d*)other; + return self.width == it.width && self.height == it.height; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + int64_t temp = DOUBLE_TO_BITS(self.height); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + temp = DOUBLE_TO_BITS(self.width); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Size2d {%lf,%lf}", self.width, self.height]; +} + +@end diff --git a/modules/core/misc/objc/common/Size2f.h b/modules/core/misc/objc/common/Size2f.h new file mode 100644 index 0000000000..83f1bae016 --- /dev/null +++ b/modules/core/misc/objc/common/Size2f.h @@ -0,0 +1,87 @@ +// +// Size2f.h +// +// Created by Giles Payne on 2019/10/06. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +@class Point2f; + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents the dimensions of a rectangle the values of which are of type `float` +*/ +@interface Size2f : NSObject + +#pragma mark - Properties + +@property float width; +@property float height; +#ifdef __cplusplus +@property(readonly) cv::Size2f& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithWidth:(float)width height:(float)height; +- (instancetype)initWithPoint:(Point2f*)point; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Size2f&)size; +#endif ++ (instancetype)width:(float)width height:(float)height; + +#pragma mark - Methods + +/** +* Returns the area of a rectangle with corresponding dimensions +*/ +- (double)area; + +/** +* Determines if a rectangle with corresponding dimensions has area of zero +*/ +- (BOOL)empty; + +/** +* Set the dimensions from the values of an array +* @param vals The array of values from which to set the dimensions +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (Size2f*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Size2f.mm b/modules/core/misc/objc/common/Size2f.mm new file mode 100644 index 0000000000..00ff5eec9f --- /dev/null +++ b/modules/core/misc/objc/common/Size2f.mm @@ -0,0 +1,108 @@ +// +// Size2f.mm +// +// Created by Giles Payne on 2019/10/06. +// + +#import "Size2f.h" +#import "Point2f.h" +#import "CVObjcUtil.h" + +@implementation Size2f { + cv::Size2f native; +} + +- (float)width { + return native.width; +} + +- (void)setWidth:(float)val { + native.width = val; +} + +- (float)height { + return native.height; +} + +- (void)setHeight:(float)val { + native.height = val; +} + +- (cv::Size2f&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithWidth:0 height:0]; +} + +- (instancetype)initWithWidth:(float)width height:(float)height { + self = [super init]; + if (self) { + self.width = width; + self.height = height; + } + return self; +} + +- (instancetype)initWithPoint:(Point2f*)point { + return [self initWithWidth:point.x height:point.y]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Size2f&)size { + return [[Size2f alloc] initWithWidth:size.width height:size.height]; +} + ++ (instancetype)width:(float)width height:(float)height { + return [[Size2f alloc] initWithWidth:width height:height]; +} + +- (double)area { + return self.width * self.height; +} + +- (BOOL)empty { + return self.width <= 0 || self.height <= 0; +} + +- (void)set:(NSArray*)vals { + self.width = (vals != nil && vals.count > 0) ? vals[0].floatValue : 0; + self.height = (vals != nil && vals.count > 1) ? vals[1].floatValue : 0; +} + +- (Size2f*)clone { + return [[Size2f alloc] initWithWidth:self.width height:self.height]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Size2f class]]) { + return NO; + } else { + Size2f* it = (Size2f*)other; + return self.width == it.width && self.height == it.height; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + FLOAT_TO_BITS(self.height); + result = prime * result + FLOAT_TO_BITS(self.width); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Size2f {%f,%f}", self.width, self.height]; +} + +@end diff --git a/modules/core/misc/objc/common/Size2i.h b/modules/core/misc/objc/common/Size2i.h new file mode 100644 index 0000000000..9a714da13e --- /dev/null +++ b/modules/core/misc/objc/common/Size2i.h @@ -0,0 +1,88 @@ +// +// Size2i.h +// +// Created by Giles Payne on 2019/10/06. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +@class Point2i; + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Represents the dimensions of a rectangle the values of which are of type `int` +*/ +NS_SWIFT_NAME(Size) +@interface Size2i : NSObject + +#pragma mark - Properties + +@property int width; +@property int height; +#ifdef __cplusplus +@property(readonly) cv::Size2i& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithWidth:(int)width height:(int)height; +- (instancetype)initWithPoint:(Point2i*)point; +- (instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::Size2i&)size; +#endif ++ (instancetype)width:(int)width height:(int)height; + +#pragma mark - Methods + +/** +* Returns the area of a rectangle with corresponding dimensions +*/ +- (double)area; + +/** +* Determines if a rectangle with corresponding dimensions has area of zero +*/ +- (BOOL)empty; + +/** +* Set the dimensions from the values of an array +* @param vals The array of values from which to set the dimensions +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (Size2i*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/Size2i.mm b/modules/core/misc/objc/common/Size2i.mm new file mode 100644 index 0000000000..a352c501fa --- /dev/null +++ b/modules/core/misc/objc/common/Size2i.mm @@ -0,0 +1,108 @@ +// +// Size2i.mm +// +// Created by Giles Payne on 2019/10/06. +// + +#import "Size2i.h" +#import "Point2i.h" +#import "CVObjcUtil.h" + +@implementation Size2i { + cv::Size2i native; +} + +- (int)width { + return native.width; +} + +- (void)setWidth:(int)val { + native.width = val; +} + +- (int)height { + return native.height; +} + +- (void)setHeight:(int)val { + native.height = val; +} + +- (cv::Size2i&)nativeRef { + return native; +} + +- (instancetype)init { + return [self initWithWidth:0 height:0]; +} + +- (instancetype)initWithWidth:(int)width height:(int)height { + self = [super init]; + if (self) { + self.width = width; + self.height = height; + } + return self; +} + +- (instancetype)initWithPoint:(Point2i*)point { + return [self initWithWidth:point.x height:point.y]; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++ (instancetype)fromNative:(cv::Size2i&)size { + return [[Size2i alloc] initWithWidth:size.width height:size.height]; +} + ++ (instancetype)width:(int)width height:(int)height { + return [[Size2i alloc] initWithWidth:width height:height]; +} + +- (double)area { + return self.width * self.height; +} + +- (BOOL)empty { + return self.width <= 0 || self.height <= 0; +} + +- (void)set:(NSArray*)vals { + self.width = (vals != nil && vals.count > 0) ? vals[0].intValue : 0; + self.height = (vals != nil && vals.count > 1) ? vals[1].intValue : 0; +} + +- (Size2i*)clone { + return [[Size2i alloc] initWithWidth:self.width height:self.height]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[Size2i class]]) { + return NO; + } else { + Size2i* it = (Size2i*)other; + return self.width == it.width && self.height == it.height; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + self.height; + result = prime * result + self.width; + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Size2i {%d,%d}", self.width, self.height]; +} + +@end diff --git a/modules/core/misc/objc/common/TermCriteria.h b/modules/core/misc/objc/common/TermCriteria.h new file mode 100644 index 0000000000..1e3055dd74 --- /dev/null +++ b/modules/core/misc/objc/common/TermCriteria.h @@ -0,0 +1,77 @@ +// +// TermCriteria.h +// +// Created by Giles Payne on 2019/10/08. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** +* Class representing termination criteria for iterative algorithms. +*/ +@interface TermCriteria : NSObject + +#pragma mark - Properties + +@property(class, readonly) int COUNT; +@property(class, readonly) int EPS; +@property(class, readonly) int MAX_ITER; + +@property int type; +@property int maxCount; +@property double epsilon; +#ifdef __cplusplus +@property(readonly) cv::TermCriteria& nativeRef; +#endif + +#pragma mark - Constructors + +- (instancetype)init; +- (instancetype)initWithType:(int)type maxCount:(int)maxCount epsilon:(double)epsilon; +- (instancetype)initWithVals:(NSArray*)vals; +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::TermCriteria&)nativeTermCriteria; +#endif + +#pragma mark - Methods + +/** +* Set the termination criteria values from the values of an array +* @param vals The array of values from which to set the termination criteria values +*/ +- (void)set:(NSArray*)vals NS_SWIFT_NAME(set(vals:)); + +#pragma mark - Common Methods + +/** +* Clone object +*/ +- (TermCriteria*)clone; + +/** +* Compare for equality +* @param other Object to compare +*/ +- (BOOL)isEqual:(nullable id)object; + +/** +* Calculate hash value for this object +*/ +- (NSUInteger)hash; + +/** +* Returns a string that describes the contents of the object +*/ +- (NSString*)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/core/misc/objc/common/TermCriteria.mm b/modules/core/misc/objc/common/TermCriteria.mm new file mode 100644 index 0000000000..b7339cd523 --- /dev/null +++ b/modules/core/misc/objc/common/TermCriteria.mm @@ -0,0 +1,119 @@ +// +// TermCriteria.m +// +// Created by Giles Payne on 2019/12/25. +// + +#import "TermCriteria.h" +#import "CVObjcUtil.h" + +@implementation TermCriteria { + cv::TermCriteria native; +} + ++ (int)COUNT { + return 1; +} + ++ (int)EPS { + return 2; +} + ++ (int)MAX_ITER { + return 1; +} + +- (int)type { + return native.type; +} + +- (void)setType:(int)val { + native.type = val; +} + +- (int)maxCount { + return native.maxCount; +} + +- (void)setMaxCount:(int)val { + native.maxCount = val; +} + +- (double)epsilon { + return native.epsilon; +} + +- (void)setEpsilon:(double)val { + native.epsilon = val; +} + +#ifdef __cplusplus +- (cv::TermCriteria&)nativeRef { + return native; +} +#endif + +- (instancetype)init { + return [self initWithType:0 maxCount:0 epsilon:0.0]; +} + +- (instancetype)initWithType:(int)type maxCount:(int)maxCount epsilon:(double)epsilon { + self = [super init]; + if (self) { + self.type = type; + self.maxCount = maxCount; + self.epsilon = epsilon; + } + return self; +} + +- (instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + +#ifdef __cplusplus ++ (instancetype)fromNative:(cv::TermCriteria&)nativeTermCriteria { + return [[TermCriteria alloc] initWithType:nativeTermCriteria.type maxCount:nativeTermCriteria.maxCount epsilon:nativeTermCriteria.epsilon]; +} +#endif + +- (void)set:(NSArray*)vals { + self.type = (vals != nil && vals.count > 0) ? vals[0].intValue : 0; + self.maxCount = (vals != nil && vals.count > 1) ? vals[1].intValue : 0; + self.epsilon = (vals != nil && vals.count > 2) ? vals[2].doubleValue : 0.0; +} + +- (TermCriteria*)clone { + return [[TermCriteria alloc] initWithType:self.type maxCount:self.maxCount epsilon:self.epsilon]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[TermCriteria class]]) { + return NO; + } else { + TermCriteria* it = (TermCriteria*)other; + return self.type == it.type && self.maxCount == it.maxCount && self.epsilon == it.epsilon; + } +} + +- (NSUInteger)hash { + int prime = 31; + uint32_t result = 1; + result = prime * result + self.type; + result = prime * result + self.maxCount; + int64_t temp = DOUBLE_TO_BITS(self.epsilon); + result = prime * result + (int32_t) (temp ^ (temp >> 32)); + return result; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"TermCriteria { type: %d, maxCount: %d, epsilon: %lf}", self.type, self.maxCount, self.epsilon]; +} + +@end diff --git a/modules/core/misc/objc/filelist b/modules/core/misc/objc/filelist new file mode 100644 index 0000000000..34ad0374ec --- /dev/null +++ b/modules/core/misc/objc/filelist @@ -0,0 +1,4 @@ +include/opencv2/core/base.hpp +include/opencv2/core.hpp +include/opencv2/core/utility.hpp +misc/objc/manual/core_manual.hpp \ No newline at end of file diff --git a/modules/core/misc/objc/gen_dict.json b/modules/core/misc/objc/gen_dict.json new file mode 100644 index 0000000000..574c334145 --- /dev/null +++ b/modules/core/misc/objc/gen_dict.json @@ -0,0 +1,375 @@ +{ + "module_imports": ["Point2i", "MinMaxLocResult"], + "class_ignore_list" : [ + "FileNode", + "FileStorage", + "KDTree", + "KeyPoint", + "DMatch" + ], + "missing_consts" : { + "Core" : { + "private" : [], + "public" : [ + ["SVD_MODIFY_A", 1], ["SVD_NO_UV", 2], ["SVD_FULL_UV", 4], + ["FILLED", -1], + ["REDUCE_SUM", 0], ["REDUCE_AVG", 1], ["REDUCE_MAX", 2], ["REDUCE_MIN", 3] + ] + } + }, + "ManualFuncs" : { + "Core" : { + "minMaxLoc" : { + "declaration" : [ + "// C++: minMaxLoc(Mat src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, InputArray mask=noArray())", + "+ (MinMaxLocResult*)minMaxLoc:(Mat*)src mask:(nullable Mat*)mask;", + "\n", + "+ (MinMaxLocResult*)minMaxLoc:(Mat*)src;", + "\n" + ], + "implementation" : [ + "// C++: minMaxLoc(Mat src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, InputArray mask=noArray())", + "+ (MinMaxLocResult*)minMaxLoc:(Mat*)src mask:(nullable Mat*)mask {", + " double minVal, maxVal;", + " cv::Point minLoc, maxLoc;", + " cv::Mat& rSrc = src.nativeRef;", + " if (mask != nil) {", + " cv::Mat& rMask = mask.nativeRef;", + " cv::minMaxLoc(rSrc, &minVal, &maxVal, &minLoc, &maxLoc, rMask);", + " } else {", + " cv::minMaxLoc(rSrc, &minVal, &maxVal, &minLoc, &maxLoc);", + " }", + " return [[MinMaxLocResult alloc] initWithMinval:minVal maxVal:maxVal minLoc:[Point2i fromNative:minLoc] maxLoc:[Point2i fromNative:maxLoc]];", + "}", + "\n", + "+ (MinMaxLocResult*)minMaxLoc:(Mat*)src {", + " return [self minMaxLoc:src mask:nil];", + "}", + "\n" + ], + "objc_method_name" : "+minMaxLoc:mask:" + }, + "checkHardwareSupport" : {"declaration" : [""], "implementation" : [""] }, + "setUseOptimized" : {"declaration" : [""], "implementation" : [""] }, + "useOptimized" : {"declaration" : [""], "implementation" : [""] } + } + }, + "func_arg_fix" : { + "randu" : { "low" : {"ctype" : "double"}, + "high" : {"ctype" : "double"} }, + "randn" : { "mean" : {"ctype" : "double"}, + "stddev" : {"ctype" : "double"} }, + "inRange" : { "lowerb" : {"ctype" : "Scalar"}, + "upperb" : {"ctype" : "Scalar"} }, + "boundingRect" : { "points" : {"ctype" : "vector_Point"} }, + "hconcat" : { "src" : {"ctype" : "vector_Mat"} }, + "vconcat" : { "src" : {"ctype" : "vector_Mat"} }, + "checkRange" : {"pos" : {"ctype" : "*"} }, + "meanStdDev" : { "mean" : {"ctype" : "vector_double"}, + "stddev" : {"ctype" : "vector_double"} }, + "mixChannels" : { "dst" : {"attrib" : []} }, + "rotate" : { "rotateCode" : {"ctype" : "RotateFlags"} }, + "norm" : { "normType" : {"ctype" : "NormTypes"} }, + "batchDistance" : { "normType" : {"ctype" : "NormTypes"} }, + "normalize" : { "norm_type" : {"ctype" : "NormTypes"} }, + "compare" : { "cmpop" : {"ctype" : "CmpTypes"} }, + "copyMakeBorder" : { "borderType" : {"ctype" : "BorderTypes"} }, + "borderInterpolate" : { "borderType" : {"ctype" : "BorderTypes"} }, + "(void)divide:(double)scale src2:(Mat*)src2 dst:(Mat*)dst dtype:(int)dtype" : { "src2" : {"name" : "src"} } + }, + "type_dict" : { + "Algorithm": { + "objc_type": "Algorithm*" + }, + "CvSlice": { + "objc_type": "Range*" + }, + "CvTermCriteria": { + "objc_type": "TermCriteria*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[TermCriteria fromNative:%(n)s]" + }, + "DMatch": { + "objc_type": "DMatch*" + }, + "KeyPoint": { + "objc_type": "KeyPoint*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[KeyPoint fromNative:%(n)s]" + }, + "Mat": { + "objc_type": "Mat*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Mat fromNative:%(n)s]", + "from_cpp_ptr": "[Mat fromNativePtr:%(n)s]" + }, + "Moments": { + "objc_type": "Moments*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Moments fromNative:%(n)s]" + }, + "Point": { + "objc_type": "Point2i*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Point2i fromNative:%(n)s]" + }, + "Point2f": { + "objc_type": "Point2f*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Point2f fromNative:%(n)s]" + }, + "Point2d": { + "objc_type": "Point2d*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Point2d fromNative:%(n)s]" + }, + "Point3d": { + "objc_type": "Point3d*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Point3d fromNative:%(n)s]" + }, + "Point3f": { + "objc_type": "Point3f*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Point3f fromNative:%(n)s]" + }, + "Point3": { + "objc_type": "Point3i*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Point3i fromNative:%(n)s]" + }, + "Range": { + "objc_type": "Range*" + }, + "Rect": { + "objc_type": "Rect2i*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Rect2i fromNative:%(n)s]" + }, + "Rect2i": { + "objc_type": "Rect2i*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Rect2i fromNative:%(n)s]" + }, + "Rect2f": { + "objc_type": "Rect2f*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Rect2f fromNative:%(n)s]" + }, + "Rect2d": { + "objc_type": "Rect2d*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Rect2d fromNative:%(n)s]" + }, + "RotatedRect": { + "objc_type": "RotatedRect*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[RotatedRect fromNative:%(n)s]" + }, + "Scalar": { + "objc_type": "Scalar*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Scalar fromNative:%(n)s]" + }, + "Size": { + "objc_type": "Size2i*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Size2i fromNative:%(n)s]" + }, + "Size2f": { + "objc_type": "Size2f*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Size2f fromNative:%(n)s]" + }, + "Size2d": { + "objc_type": "Size2d*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Size2d fromNative:%(n)s]" + }, + "String": { + "objc_type": "NSString*", + "to_cpp": "cv::String(%(n)s.UTF8String)", + "from_cpp": "[NSString stringWithUTF8String:%(n)s.c_str()]" + }, + "std::string": { + "objc_type": "NSString*", + "to_cpp": "std::string(%(n)s.UTF8String)", + "from_cpp": "[NSString stringWithUTF8String:%(n)s.c_str()]" + }, + "TermCriteria": { + "objc_type": "TermCriteria*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[TermCriteria fromNative:%(n)s]" + }, + "Vec4f": { + "objc_type": "Float4*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Float4 fromNative:%(n)s]" + }, + "Vec4i": { + "objc_type": "Int4*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Int4 fromNative:%(n)s]" + }, + "Vec6f": { + "objc_type": "Float6*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Float6 fromNative:%(n)s]" + }, + "Vec2d": { + "objc_type": "Double2*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Double2 fromNative:%(n)s]" + }, + "Vec3d": { + "objc_type": "Double3*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[Double3 fromNative:%(n)s]" + }, + "c_string": { + "objc_type": "NSString*" + }, + "vector_DMatch": { + "objc_type": "DMatch*", + "v_type": "DMatch" + }, + "vector_KeyPoint": { + "objc_type": "KeyPoint*", + "v_type": "KeyPoint" + }, + "vector_Mat": { + "objc_type": "Mat*", + "v_type": "Mat" + }, + "vector_Point": { + "objc_type": "Point2i*", + "v_type": "Point" + }, + "vector_Point2f": { + "objc_type": "Point2f*", + "v_type": "Point2f" + }, + "vector_Point2d": { + "objc_type": "Point2d*", + "v_type": "Point2d" + }, + "vector_Point3": { + "objc_type": "Point3i*", + "v_type": "Point3i" + }, + "vector_Point3f": { + "objc_type": "Point3f*", + "v_type": "Point3f" + }, + "vector_Point3d": { + "objc_type": "Point3d*", + "v_type": "Point3d" + }, + "vector_Rect": { + "objc_type": "Rect2i*", + "v_type": "Rect2i" + }, + "vector_Rect2d": { + "objc_type": "Rect2d*", + "v_type": "Rect2d" + }, + "vector_RotatedRect": { + "objc_type": "RotatedRect*", + "v_type": "RotatedRect" + }, + "vector_String": { + "objc_type": "NSString*", + "v_type": "String" + }, + "vector_string": { + "objc_type": "NSString*", + "v_type": "std::string" + }, + "vector_Vec4f": { + "objc_type": "Float4*", + "v_type": "Vec4f" + }, + "vector_Vec4i": { + "objc_type": "Int4*", + "v_type": "Vec4i" + }, + "vector_Vec6f": { + "objc_type": "Float6*", + "v_type": "Vec6f" + }, + "vector_char": { + "objc_type": "ByteVector*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[ByteVector fromNative:%(n)s]", + "cast_to": "std::vector" + }, + "vector_double": { + "objc_type": "DoubleVector*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[DoubleVector fromNative:%(n)s]", + "cast_to": "std::vector" + }, + "vector_float": { + "objc_type": "FloatVector*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[FloatVector fromNative:%(n)s]", + "cast_to": "std::vector" + }, + "vector_int": { + "objc_type": "IntVector*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[IntVector fromNative:%(n)s]", + "cast_to": "std::vector" + }, + "vector_uchar": { + "objc_type": "ByteVector*", + "to_cpp": "(std::vector&)%(n)s.nativeRef", + "from_cpp": "[ByteVector fromNative:(std::vector&)%(n)s]", + "cast_to": "std::vector" + }, + "vector_vector_Mat": { + "objc_type": "Mat*", + "v_v_type": "Mat" + }, + "vector_vector_DMatch": { + "objc_type": "DMatch*", + "v_v_type": "DMatch" + }, + "vector_vector_KeyPoint": { + "objc_type": "KeyPoint*", + "v_v_type": "KeyPoint" + }, + "vector_vector_Point": { + "objc_type": "Point2i*", + "v_v_type": "Point" + }, + "vector_vector_Point2f": { + "objc_type": "Point2f*", + "v_v_type": "Point2f" + }, + "vector_vector_Point3f": { + "objc_type": "Point3f*", + "v_v_type": "Point3f" + }, + "vector_vector_char": { + "objc_type": "ByteVector*", + "v_type": "ByteVector" + }, + "ByteVector": { + "objc_type": "ByteVector*", + "cast_to": "std::vector" + }, + "IntVector": { + "objc_type": "IntVector*", + "cast_to": "std::vector" + }, + "FloatVector": { + "objc_type": "FloatVector*", + "cast_to": "std::vector" + }, + "DoubleVector": { + "objc_type": "DoubleVector*", + "cast_to": "std::vector" + } + } +} diff --git a/modules/core/misc/objc/manual/core_manual.hpp b/modules/core/misc/objc/manual/core_manual.hpp new file mode 100644 index 0000000000..28ba28d147 --- /dev/null +++ b/modules/core/misc/objc/manual/core_manual.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "opencv2/core.hpp" + +#ifdef OPENCV_BINDINGS_PARSER + +namespace cv +{ +CV_EXPORTS_W void add(InputArray src1, Scalar srcScalar, OutputArray dst, InputArray mask=noArray(), int dtype=-1); + +CV_EXPORTS_W void subtract(InputArray src1, Scalar srcScalar, OutputArray dst, InputArray mask=noArray(), int dtype=-1); + +CV_EXPORTS_W void multiply(InputArray src1, Scalar srcScalar, OutputArray dst, double scale=1, int dtype=-1); + +CV_EXPORTS_W void divide(InputArray src1, Scalar srcScalar, OutputArray dst, double scale=1, int dtype=-1); + +CV_EXPORTS_W void absdiff(InputArray src1, Scalar srcScalar, OutputArray dst); + +CV_EXPORTS_W void compare(InputArray src1, Scalar srcScalar, OutputArray dst, int cmpop); + +CV_EXPORTS_W void min(InputArray src1, Scalar srcScalar, OutputArray dst); + +CV_EXPORTS_W void max(InputArray src1, Scalar srcScalar, OutputArray dst); + +} +#endif diff --git a/modules/core/misc/objc/test/ConvertersTest.swift b/modules/core/misc/objc/test/ConvertersTest.swift new file mode 100755 index 0000000000..5d7efd588d --- /dev/null +++ b/modules/core/misc/objc/test/ConvertersTest.swift @@ -0,0 +1,109 @@ +// +// ConvertersTest.swift +// +// Created by Giles Payne on 2020/06/01. +// + +import XCTest +import OpenCV + +class ConvertersTest: OpenCVTestCase { + + func testPoint2iToMat() { + let pointsIn = [Point(x:3, y:4), Point(x:6, y:7), Point(x:7, y:6), Point(x:-78, y:14), Point(x:-93, y:700)] + let pointsOut = Converters.Mat_to_vector_Point(Converters.vector_Point_to_Mat(pointsIn)) + XCTAssertEqual(pointsIn, pointsOut) + } + + func testPoint2fToMat() { + let pointsIn = [Point2f(x:3.8, y:4.2), Point2f(x:6.01, y:7), Point2f(x:7, y:6), Point2f(x:-78, y:14), Point2f(x:-93, y:700)] + let pointsOut = Converters.Mat_to_vector_Point2f(Converters.vector_Point2f_to_Mat(pointsIn)) + XCTAssertEqual(pointsIn, pointsOut) + } + + func testPoint2dToMat() { + let pointsIn = [Point2d(x:3.80004, y:73.2), Point2d(x:16.01, y:7.1111), Point2d(x:3.14, y:6), Point2d(x:-78, y:14)] + let pointsOut = Converters.Mat_to_vector_Point2d(Converters.vector_Point2d_to_Mat(pointsIn)) + XCTAssertEqual(pointsIn, pointsOut) + } + + func testPoint3iToMat() { + let pointsIn = [Point3i(x:3, y:4, z:2), Point3i(x:6, y:7, z:1), Point3i(x:7, y:6, z:9), Point3i(x:-78, y:14, z:0), Point3i(x:-93, y:700, z:54)] + let pointsOut = Converters.Mat_to_vector_Point3i(Converters.vector_Point3i_to_Mat(pointsIn)) + XCTAssertEqual(pointsIn, pointsOut) + } + + func testPoint3fToMat() { + let pointsIn = [Point3f(x:3.8, y:4.2, z:1200), Point3f(x:6.01, y:7, z: 12), Point3f(x:7, y:6, z:8.88128), Point3f(x:-78, y:14, z:-1), Point3f(x:-93, y:700, z:200)] + let pointsOut = Converters.Mat_to_vector_Point3f(Converters.vector_Point3f_to_Mat(pointsIn)) + XCTAssertEqual(pointsIn, pointsOut) + } + + func testPoint3dToMat() { + let pointsIn = [Point3d(x:3.80004, y:73.2, z:1), Point3d(x:16.01, y:7.1111, z:2), Point3d(x:3.14, y:6, z:3), Point3d(x:-78, y:14, z:4)] + let pointsOut = Converters.Mat_to_vector_Point3d(Converters.vector_Point3d_to_Mat(pointsIn)) + XCTAssertEqual(pointsIn, pointsOut) + } + + func testFloatToMat() { + let floatsIn:[Float] = [23.8, 999.89, 93, 0.9, 12] + let floatsOut = Converters.Mat_to_vector_float(Converters.vector_float_to_Mat(floatsIn as [NSNumber])) as! [Float] + XCTAssertEqual(floatsIn, floatsOut) + } + + func testIntToMat() { + let intsIn:[Int32] = [23, 999, -93, 0, 4412] + let intsOut = Converters.Mat_to_vector_int(Converters.vector_int_to_Mat(intsIn as [NSNumber])) as! [Int32] + XCTAssertEqual(intsIn, intsOut) + } + + func testCharToMat() { + let charsIn:[Int8] = [23, -23, 93, 0, -127] + let charsOut = Converters.Mat_to_vector_char(Converters.vector_char_to_Mat(charsIn as [NSNumber])) as! [Int8] + XCTAssertEqual(charsIn, charsOut) + } + + func testUCharToMat() { + let ucharsIn:[UInt8] = [23, 190, 93, 0, 255] + let ucharsOut = Converters.Mat_to_vector_uchar(Converters.vector_uchar_to_Mat(ucharsIn as [NSNumber])) as! [UInt8] + XCTAssertEqual(ucharsIn, ucharsOut) + } + + func testDoubleToMat() { + let doublesIn:[Double] = [23.8, 999.89, 93, 0.9, 12] + let doublesOut = Converters.Mat_to_vector_double(Converters.vector_double_to_Mat(doublesIn as [NSNumber])) as! [Double] + XCTAssertEqual(doublesIn, doublesOut) + } + + func testRectToMat() { + let rectsIn = [Rect(x: 0, y: 0, width: 3, height: 4), Rect(x: 10, y: 23, width: 7, height: 6), Rect(x: 0, y: 1111110, width: 1, height: 4000)] + let rectsOut = Converters.Mat_to_vector_Rect(Converters.vector_Rect_to_Mat(rectsIn)) + XCTAssertEqual(rectsIn, rectsOut) + } + + func testRect2dToMat() { + let rectsIn = [Rect2d(x: 0.001, y: 0.00001, width: 3.2, height: 4.556555555), Rect2d(x: 10.009, y: -6623, width: 7.9, height: 6), Rect2d(x: 0, y: 1111.33110, width: 0.99999, height: 3999.999)] + let rectsOut = Converters.Mat_to_vector_Rect2d(Converters.vector_Rect2d_to_Mat(rectsIn)) + XCTAssertEqual(rectsIn, rectsOut) + } + + func testKeyPointToMat() { + let keyPointsIn = [KeyPoint(x: 8.99, y: 9.00, size: 3, angle: 3.23, response: 0.001, octave: 3, classId: 5), KeyPoint(x: 58.99, y: 9.488, size: 3.4, angle: 2.223, response: 0.006, octave: 4, classId: 7), KeyPoint(x: 7, y: 9.003, size: 12, angle: -3.23, response: 0.02, octave: 1, classId: 8)] + let keyPointsOut = Converters.Mat_to_vector_KeyPoint(Converters.vector_KeyPoint_to_Mat(keyPointsIn)) + XCTAssertEqual(keyPointsIn, keyPointsOut) + } + + func testDMatchToMat() { + let dmatchesIn = [DMatch(queryIdx: 2, trainIdx: 4, distance: 0.7), DMatch(queryIdx: 3, trainIdx: 7, distance: 0.1), DMatch(queryIdx: 4, trainIdx: 8, distance: 0.01)] + let dmatchesOut = Converters.Mat_to_vector_DMatch(Converters.vector_DMatch_to_Mat(dmatchesIn)) + XCTAssertEqual(dmatchesIn, dmatchesOut) + } + + func testRotatedRectToMat() { + let rectsIn = [RotatedRect(center: Point2f(x: 0.4, y: 0.9), size: Size2f(width: 3.0, height: 8.9), angle: 0.3342)] + let rectsOut = Converters.Mat_to_vector_RotatedRect(Converters.vector_RotatedRect_to_Mat(rectsIn)) + XCTAssertEqual(rectsIn[0].center, rectsOut[0].center) + XCTAssertEqual(rectsIn[0].size, rectsOut[0].size) + XCTAssertEqual(rectsIn[0].angle, rectsOut[0].angle, accuracy: OpenCVTestCase.EPS) + } +} diff --git a/modules/core/misc/objc/test/CoreTest.swift b/modules/core/misc/objc/test/CoreTest.swift new file mode 100644 index 0000000000..efa18bbd7f --- /dev/null +++ b/modules/core/misc/objc/test/CoreTest.swift @@ -0,0 +1,1710 @@ +// +// CoreTest.swift +// +// Created by Giles Payne on 2020/01/27. +// + +import XCTest +import OpenCV + +class CoreTest: OpenCVTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testAbsdiff() throws { + Core.absdiff(src1: gray128, src2: gray255, dst: dst) + + try assertMatEqual(gray127, dst) + } + + func testAddMatMatMat() throws { + Core.add(src1: gray128, src2: gray128, dst: dst) + + try assertMatEqual(gray255, dst) + } + + func testAddMatMatMatMatInt() throws { + Core.add(src1: gray0, src2: gray1, dst: dst, mask: gray1, dtype: CvType.CV_32F) + + XCTAssertEqual(CvType.CV_32F, dst.depth()) + try assertMatEqual(gray1_32f, dst, OpenCVTestCase.EPS) + } + + func testAddWeightedMatDoubleMatDoubleDoubleMat() throws { + Core.addWeighted(src1: gray1, alpha: 120.0, src2: gray127, beta: 1.0, gamma: 10.0, dst: dst) + + try assertMatEqual(gray255, dst) + } + + func testAddWeightedMatDoubleMatDoubleDoubleMatInt() throws { + Core.addWeighted(src1: gray1, alpha: 126.0, src2: gray127, beta: 1.0, gamma: 2.0, dst: dst, dtype: CvType.CV_32F) + + XCTAssertEqual(CvType.CV_32F, dst.depth()) + try assertMatEqual(gray255_32f, dst, OpenCVTestCase.EPS) + } + + func testBitwise_andMatMatMat() throws { + Core.bitwise_and(src1: gray127, src2: gray3, dst: dst) + + try assertMatEqual(gray3, dst) + } + + func testBitwise_andMatMatMatMat() throws { + Core.bitwise_and(src1: gray3, src2: gray1, dst: dst, mask: gray255) + + try assertMatEqual(gray1, dst) + } + + func testBitwise_notMatMat() throws { + Core.bitwise_not(src: gray255, dst: dst) + + try assertMatEqual(gray0, dst) + } + + func testBitwise_notMatMatMat() throws { + Core.bitwise_not(src: gray0, dst: dst, mask: gray1) + + try assertMatEqual(gray255, dst) + } + + func testBitwise_orMatMatMat() throws { + Core.bitwise_or(src1: gray1, src2: gray2, dst: dst) + + try assertMatEqual(gray3, dst) + } + + func testBitwise_orMatMatMatMat() throws { + Core.bitwise_or(src1: gray127, src2: gray3, dst: dst, mask: gray255) + + try assertMatEqual(gray127, dst) + } + + func testBitwise_xorMatMatMat() throws { + Core.bitwise_xor(src1: gray3, src2: gray2, dst: dst) + + try assertMatEqual(gray1, dst) + } + + func testBitwise_xorMatMatMatMat() throws { + Core.bitwise_or(src1: gray127, src2: gray128, dst: dst, mask: gray255) + + try assertMatEqual(gray255, dst) + } + + func testCalcCovarMatrixMatMatMatInt() throws { + let covar = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_64FC1) + let mean = Mat(rows: 1, cols: OpenCVTestCase.matSize, type: CvType.CV_64FC1) + + Core.calcCovarMatrix(samples: gray0_32f, covar: covar, mean: mean, flags: CovarFlags.COVAR_ROWS.rawValue | CovarFlags.COVAR_NORMAL.rawValue) + + try assertMatEqual(gray0_64f, covar, OpenCVTestCase.EPS) + try assertMatEqual(gray0_64f_1d, mean, OpenCVTestCase.EPS) + } + + func testCalcCovarMatrixMatMatMatIntInt() throws { + let covar = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32F) + let mean = Mat(rows: 1, cols: OpenCVTestCase.matSize, type: CvType.CV_32F) + + Core.calcCovarMatrix(samples: gray0_32f, covar: covar, mean: mean, flags: CovarFlags.COVAR_ROWS.rawValue | CovarFlags.COVAR_NORMAL.rawValue, ctype: CvType.CV_32F) + + try assertMatEqual(gray0_32f, covar, OpenCVTestCase.EPS) + try assertMatEqual(gray0_32f_1d, mean, OpenCVTestCase.EPS) + } + + func testCartToPolarMatMatMatMat() throws { + let x = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [3.0, 6.0, 5, 0] as [Float]) + let y = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try y.put(row: 0, col: 0, data: [4.0, 8.0, 12.0] as [Float]) + let dst_angle = Mat() + + Core.cartToPolar(x: x, y: y, magnitude: dst, angle: dst_angle) + + let expected_magnitude = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try expected_magnitude.put(row: 0, col: 0, data: [5.0, 10.0, 13.0] as [Float]) + + let expected_angle = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try expected_angle.put(row: 0, col: 0, data: [atan2rad(4,3), atan2rad(8,6), atan2rad(12,5)]) + try assertMatEqual(expected_magnitude, dst, OpenCVTestCase.EPS) + try assertMatEqual(expected_angle, dst_angle, OpenCVTestCase.EPS) + } + + func testCartToPolarMatMatMatMatBoolean() throws { + let x = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [3.0, 6.0, 5, 0] as [Float]) + let y = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try y.put(row: 0, col: 0, data: [4.0, 8.0, 12.0] as [Float]) + let dst_angle = Mat() + + Core.cartToPolar(x: x, y: y, magnitude: dst, angle: dst_angle, angleInDegrees: true) + + let expected_magnitude = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try expected_magnitude.put(row: 0, col: 0, data: [5.0, 10.0, 13.0] as [Float]) + let expected_angle = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try expected_angle.put(row: 0, col: 0, data:[atan2deg(4,3), atan2deg(8,6), atan2deg(12,5)]) + try assertMatEqual(expected_magnitude, dst, OpenCVTestCase.EPS) + try assertMatEqual(expected_angle, dst_angle, OpenCVTestCase.EPS * 180/Double.pi) + } + + + func testCheckRangeMat() throws { + let outOfRange = Mat(rows: 2, cols: 2, type: CvType.CV_64F) + try outOfRange.put(row: 0, col: 0, data: [Double.nan, -Double.infinity, Double.infinity, 0]) + + XCTAssert(Core.checkRange(a: grayRnd_32f)) + XCTAssert(Core.checkRange(a: Mat())) + XCTAssertFalse(Core.checkRange(a: outOfRange)) + } + + func testCheckRangeMatBooleanPointDoubleDouble() throws { + let inRange = Mat(rows: 2, cols: 3, type: CvType.CV_64F) + try inRange.put(row: 0, col: 0, data: [14, 48, 76, 33, 5, 99] as [Double]) + + XCTAssert(Core.checkRange(a: inRange, quiet: true, minVal: 5, maxVal: 100)) + + let outOfRange = Mat(rows: 2, cols: 3, type: CvType.CV_64F) + try inRange.put(row: 0, col: 0, data: [-4, 0, 6, 33, 4, 109] as [Double]) + + XCTAssertFalse(Core.checkRange(a: outOfRange, quiet: true, minVal: 5, maxVal: 100)) + } + + func testCompare() throws { + Core.compare(src1: gray0, src2: gray0, dst: dst, cmpop: .CMP_EQ) + + try assertMatEqual(dst, gray255) + + Core.compare(src1: gray0, src2: gray1, dst: dst, cmpop: .CMP_EQ) + + try assertMatEqual(dst, gray0) + + try grayRnd.put(row: 0, col: 0, data: [0, 0] as [Int8]) + + Core.compare(src1: gray0, src2: grayRnd, dst: dst, cmpop: .CMP_GE) + + let expected = Int32(grayRnd.total()) - Core.countNonZero(src: grayRnd) + XCTAssertEqual(expected, Core.countNonZero(src: dst)) + } + + func testCompleteSymmMat() throws { + Core.completeSymm(m: grayRnd_32f) + + try assertMatEqual(grayRnd_32f, grayRnd_32f.t(), OpenCVTestCase.EPS) + } + + func testCompleteSymmMatBoolean() throws { + let grayRnd_32f2 = grayRnd_32f.clone() + + Core.completeSymm(m: grayRnd_32f, lowerToUpper: true) + + try assertMatEqual(grayRnd_32f, grayRnd_32f.t(), OpenCVTestCase.EPS) + Core.completeSymm(m: grayRnd_32f2, lowerToUpper: false) + try assertMatNotEqual(grayRnd_32f2, grayRnd_32f, OpenCVTestCase.EPS) + } + + func testConvertScaleAbsMatMat() throws { + Core.convertScaleAbs(src: gray0, dst: dst) + + try assertMatEqual(gray0, dst, OpenCVTestCase.EPS) + + Core.convertScaleAbs(src: gray_16u_256, dst: dst) + + try assertMatEqual(gray255, dst, OpenCVTestCase.EPS) + } + + func testConvertScaleAbsMatMatDoubleDouble() throws { + Core.convertScaleAbs(src: gray_16u_256, dst: dst, alpha: 2, beta: -513) + + try assertMatEqual(gray1, dst) + } + + func testCountNonZero() throws { + XCTAssertEqual(0, Core.countNonZero(src: gray0)) + let gray0copy = gray0.clone() + + try gray0copy.put(row: 0, col: 0, data: [-1] as [Int8]) + try gray0copy.put(row: gray0copy.rows() - 1, col: gray0copy.cols() - 1, data: [-1] as [Int8]) + + XCTAssertEqual(2, Core.countNonZero(src: gray0copy)) + } + + func testCubeRoot() { + let res:Float = Core.cubeRoot(val: -27.0) + + XCTAssertEqual(-3.0, res) + } + + func testDctMatMat() throws { + let m = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try m.put(row: 0, col: 0, data: [135.22211, 50.811096, 102.27016, 207.6682] as [Float]) + let dst1 = Mat() + let dst2 = Mat() + + Core.dct(src: gray0_32f_1d, dst: dst1) + Core.dct(src: m, dst: dst2) + + truth = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try truth!.put(row: 0, col: 0, data: [247.98576, -61.252407, 94.904533, 14.013477] as [Float]) + try assertMatEqual(gray0_32f_1d, dst1, OpenCVTestCase.EPS) + try assertMatEqual(truth!, dst2, OpenCVTestCase.EPS) + } + + func testDctMatMatInt() throws { + let m = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try m.put(row: 0, col: 0, data: [247.98576, -61.252407, 94.904533, 14.013477] as [Float]) + let dst1 = Mat() + let dst2 = Mat() + + Core.dct(src: gray0_32f_1d, dst: dst1, flags:DftFlags.DCT_INVERSE.rawValue) + Core.dct(src: m, dst: dst2, flags:DftFlags.DCT_INVERSE.rawValue) + + truth = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try truth!.put(row: 0, col: 0, data: [135.22211, 50.811096, 102.27016, 207.6682] as [Float]) + try assertMatEqual(gray0_32f_1d, dst1, OpenCVTestCase.EPS) + try assertMatEqual(truth!, dst2, OpenCVTestCase.EPS) + } + + func testDeterminant() throws { + let mat = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + try mat.put(row: 0, col: 0, data: [4.0] as [Float]) + try mat.put(row: 0, col: 1, data: [2.0] as [Float]) + try mat.put(row: 1, col: 0, data: [4.0] as [Float]) + try mat.put(row: 1, col: 1, data: [4.0] as [Float]) + + let det = Core.determinant(mtx: mat) + + XCTAssertEqual(8.0, det) + } + + func testDftMatMat() throws { + Core.dft(src: gray0_32f_1d, dst: dst) + + try assertMatEqual(gray0_32f_1d, dst, OpenCVTestCase.EPS) + } + + func testDftMatMatIntInt() throws { + let src1 = Mat(rows: 2, cols: 4, type: CvType.CV_32F) + try src1.put(row: 0, col: 0, data: [1, 2, 3, 4] as [Float]) + try src1.put(row: 1, col: 0, data: [1, 1, 1, 1] as [Float]) + let src2 = Mat(rows: 2, cols: 4, type: CvType.CV_32F) + try src2.put(row: 0, col: 0, data: [1, 2, 3, 4] as [Float]) + try src2.put(row: 1, col: 0, data: [0, 0, 0, 0] as [Float]) + let dst1 = Mat() + let dst2 = Mat() + + Core.dft(src: src1, dst: dst1, flags: DftFlags.DFT_REAL_OUTPUT.rawValue, nonzeroRows: 1) + Core.dft(src: src2, dst: dst2, flags: DftFlags.DFT_REAL_OUTPUT.rawValue, nonzeroRows: 0) + + try assertMatEqual(dst2, dst1, OpenCVTestCase.EPS) + } + + func testDivideDoubleMatMat() throws { + Core.divide(scale: 4.0, src: gray2, dst: dst) + + try assertMatEqual(gray2, dst) + + Core.divide(scale: 4.0, src: gray0, dst: dst) + + try assertMatEqual(gray0, dst) + } + + func testDivideDoubleMatMatInt() throws { + Core.divide(scale: 9.0, src: gray3, dst: dst, dtype: CvType.CV_32F) + + try assertMatEqual(gray3_32f, dst, OpenCVTestCase.EPS) + } + + func testDivideMatMatMat() throws { + Core.divide(src1: gray9, src2: gray3, dst: dst) + + try assertMatEqual(gray3, dst) + } + + func testDivideMatMatMatDouble() throws { + Core.divide(src1: gray1, src2: gray2, dst: dst, scale: 6.0) + + try assertMatEqual(gray3, dst) + } + + func testDivideMatMatMatDoubleInt() throws { + Core.divide(src1: gray1, src2: gray2, dst: dst, scale: 6.0, dtype: CvType.CV_32F) + + try assertMatEqual(gray3_32f, dst, OpenCVTestCase.EPS) + } + + func testEigen() throws { + let src = Mat(rows: 3, cols: 3, type: CvType.CV_32FC1) + try src.put(row: 0, col: 0, data: [2, 0, 0] as [Float]) + try src.put(row: 1, col: 0, data: [0, 6, 0] as [Float]) + try src.put(row: 2, col: 0, data: [0, 0, 4] as [Float]) + let eigenVals = Mat() + let eigenVecs = Mat() + + Core.eigen(src: src, eigenvalues: eigenVals, eigenvectors: eigenVecs) + + let expectedEigenVals = Mat(rows: 3, cols: 1, type: CvType.CV_32FC1) + try expectedEigenVals.put(row: 0, col: 0, data: [6, 4, 2] as [Float]) + try assertMatEqual(eigenVals, expectedEigenVals, OpenCVTestCase.EPS) + + // check by definition + let EPS = 1e-3 + for i:Int32 in 0..<3 { + let vec = eigenVecs.row(i).t() + let lhs = Mat(rows: 3, cols: 1, type: CvType.CV_32FC1) + Core.gemm(src1: src, src2: vec, alpha: 1.0, src3: Mat(), beta: 1.0, dst: lhs) + let rhs = Mat(rows: 3, cols: 1, type: CvType.CV_32FC1) + Core.gemm(src1: vec, src2: eigenVals.row(i), alpha: 1.0, src3: Mat(), beta: 1.0, dst: rhs) + try assertMatEqual(lhs, rhs, EPS) + } + } + + func testExp() throws { + Core.exp(src: gray0_32f, dst: dst) + + try assertMatEqual(gray1_32f, dst, OpenCVTestCase.EPS) + } + + func testExtractChannel() throws { + Core.extractChannel(src: rgba128, dst: dst, coi: 0) + + try assertMatEqual(gray128, dst) + } + + func testFastAtan2() { + let EPS: Float = 0.3 + + let res = Core.fastAtan2(y: 50, x: 50) + + XCTAssertEqual(Float(45.0), res, accuracy:EPS) + + let res2 = Core.fastAtan2(y: 80, x: 20) + + XCTAssertEqual(atan2(80, 20) * 180 / Float.pi, res2, accuracy:EPS) + } + + func testFillConvexPolyMatListOfPointScalar() { + let polyline = [Point(x: 1, y: 1), Point(x: 5, y: 0), Point(x: 6, y: 8), Point(x: 0, y: 9)] + dst = gray0.clone() + + Imgproc.fillConvexPoly(img: dst, points: polyline, color: Scalar(150)) + + XCTAssert(0 < Core.countNonZero(src: dst)) + XCTAssert(dst.total() > Core.countNonZero(src: dst)) + } + + func testFillConvexPolyMatListOfPointScalarIntInt() { + let polyline1 = [Point(x: 2, y: 1), Point(x: 5, y: 1), Point(x: 5, y: 7), Point(x: 2, y: 7)] + let polyline2 = [Point(x: 4, y: 2), Point(x: 10, y: 2), Point(x: 10, y: 14), Point(x: 4, y: 14)] + + // current implementation of fixed-point version of fillConvexPoly + // requires image to be at least 2-pixel wider in each direction than + // contour + Imgproc.fillConvexPoly(img: gray0, points: polyline1, color: colorWhite, lineType: .LINE_8, shift: 0) + + XCTAssert(0 < Core.countNonZero(src: gray0)) + XCTAssert(gray0.total() > Core.countNonZero(src: gray0)) + + Imgproc.fillConvexPoly(img: gray0, points: polyline2, color: colorBlack, lineType: .LINE_8, shift: 1) + + XCTAssertEqual(0, Core.countNonZero(src: gray0)) + } + + func testFillPolyMatListOfListOfPointScalar() throws { + let matSize = 10; + let gray0 = Mat.zeros(Int32(matSize), cols: Int32(matSize), type: CvType.CV_8U) + let polyline = [Point(x: 1, y: 4), Point(x: 1, y: 8), Point(x: 4, y: 1), Point(x: 7, y: 8), Point(x: 7, y: 4)] + let polylines = [polyline] + + Imgproc.fillPoly(img: gray0, pts: polylines, color: Scalar(1)) + + let truth:[Int8] = + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + + let truthMat = Mat(size: gray0.size(), type: CvType.CV_8U) + try truthMat.put(row:0, col:0, data:truth) + + try assertMatEqual(truthMat, gray0) + } + + func testFillPolyMatListOfListOfPointScalarIntIntPoint() { + let polyline1 = [Point(x: 1, y: 4), Point(x: 1, y: 8), Point(x: 4, y: 1), Point(x: 7, y: 8), Point(x: 7, y: 4)] + let polyline2 = [Point(x: 0, y: 3), Point(x: 0, y: 7), Point(x: 3, y: 0), Point(x: 6, y: 7), Point(x: 6, y: 3)] + + let polylines1 = [polyline1] + let polylines2 = [polyline2] + + Imgproc.fillPoly(img: gray0, pts: polylines1, color: Scalar(1), lineType: .LINE_8, shift: 0, offset: Point(x: 0, y: 0)) + + XCTAssert(0 < Core.countNonZero(src: gray0)) + + Imgproc.fillPoly(img: gray0, pts: polylines2, color: Scalar(0), lineType: .LINE_8, shift: 0, offset: Point(x: 1, y: 1)) + + XCTAssertEqual(0, Core.countNonZero(src: gray0)) + } + + func testFlip() throws { + let src = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + try src.put(row: 0, col: 0, data: [1.0] as [Float]) + try src.put(row: 0, col: 1, data: [2.0] as [Float]) + try src.put(row: 1, col: 0, data: [3.0] as [Float]) + try src.put(row: 1, col: 1, data: [4.0] as [Float]) + let dst1 = Mat() + let dst2 = Mat() + + Core.flip(src: src, dst: dst1, flipCode: 0) + Core.flip(src: src, dst: dst2, flipCode: 1) + + let dst_f1 = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + try dst_f1.put(row: 0, col: 0, data: [3.0] as [Float]) + try dst_f1.put(row: 0, col: 1, data: [4.0] as [Float]) + try dst_f1.put(row: 1, col: 0, data: [1.0] as [Float]) + try dst_f1.put(row: 1, col: 1, data: [2.0] as [Float]) + let dst_f2 = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + try dst_f2.put(row: 0, col: 0, data: [2.0] as [Float]) + try dst_f2.put(row: 0, col: 1, data: [1.0] as [Float]) + try dst_f2.put(row: 1, col: 0, data: [4.0] as [Float]) + try dst_f2.put(row: 1, col: 1, data: [3.0] as [Float]) + try assertMatEqual(dst_f1, dst1, OpenCVTestCase.EPS) + try assertMatEqual(dst_f2, dst2, OpenCVTestCase.EPS) + } + + func testGemmMatMatDoubleMatDoubleMat() throws { + let m1 = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1) + try m1.put(row: 0, col: 0, data: [1.0, 0.0] as [Float]) + try m1.put(row: 1, col: 0, data: [1.0, 0.0] as [Float]) + let m2 = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1) + try m2.put(row: 0, col: 0, data: [1.0, 0.0] as [Float]) + try m2.put(row: 1, col: 0, data: [1.0, 0.0] as [Float]) + let dmatrix = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1); + try dmatrix.put(row: 0, col: 0, data: [0.001, 0.001] as [Float]) + try dmatrix.put(row: 1, col: 0, data: [0.001, 0.001] as [Float]) + + Core.gemm(src1: m1, src2: m2, alpha: 1.0, src3: dmatrix, beta: 1.0, dst: dst) + + let expected = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1) + try expected.put(row: 0, col: 0, data: [1.001, 0.001] as [Float]) + try expected.put(row: 1, col: 0, data: [1.001, 0.001] as [Float]) + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testGemmMatMatDoubleMatDoubleMatInt() throws { + let m1 = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1) + try m1.put(row: 0, col: 0, data: [1.0, 0.0]) + try m1.put(row: 1, col: 0, data: [1.0, 0.0]) + let m2 = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1) + try m2.put(row: 0, col: 0, data: [1.0, 0.0]) + try m2.put(row: 1, col: 0, data: [1.0, 0.0]) + let dmatrix = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1) + try dmatrix.put(row: 0, col: 0, data: [0.001, 0.001]) + try dmatrix.put(row: 1, col: 0, data: [0.001, 0.001]) + + Core.gemm(src1: m1, src2: m2, alpha: 1.0, src3: dmatrix, beta: 1.0, dst: dst, flags: GemmFlags.GEMM_1_T.rawValue) + + let expected = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1) + try expected.put(row: 0, col: 0, data: [2.001, 0.001]) + try expected.put(row: 1, col: 0, data: [0.001, 0.001]) + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testGetCPUTickCount() { + let cpuCountStart = Core.getCPUTickCount() + Core.sum(src: gray255) + let actualTickCount = Core.getCPUTickCount() + + let expectedTickCount = actualTickCount - cpuCountStart; + XCTAssert(expectedTickCount > 0) + } + + func testGetNumberOfCPUs() { + let cpus = Core.getNumberOfCPUs() + + XCTAssert(ProcessInfo().processorCount <= cpus) + } + + func testGetOptimalDFTSize() { + XCTAssertEqual(1, Core.getOptimalDFTSize(vecsize: 0)) + XCTAssertEqual(135, Core.getOptimalDFTSize(vecsize: 133)) + XCTAssertEqual(15, Core.getOptimalDFTSize(vecsize: 13)) + } + + func testGetTickCount() { + + let startCount = Core.getTickCount() + Core.divide(src1: gray2, src2: gray1, dst: dst) + let endCount = Core.getTickCount() + + let count = endCount - startCount; + XCTAssert(count > 0) + } + + func testGetTickFrequency() { + let freq1 = Core.getTickFrequency() + Core.divide(src1: gray2, src2: gray1, dst: dst) + let freq2 = Core.getTickFrequency() + + XCTAssert(0 < freq1) + XCTAssertEqual(freq1, freq2) + } + + func testHconcat() throws { + let mats = [Mat.eye(rows: 3, cols: 3, type: CvType.CV_8U), Mat.zeros(3, cols: 2, type: CvType.CV_8U)] + + Core.hconcat(src: mats, dst: dst) + + try assertMatEqual(Mat.eye(rows: 3, cols: 5, type: CvType.CV_8U), dst) + } + + func testIdctMatMat() throws { + let mat = Mat(rows: 1, cols: 8, type: CvType.CV_32F) + try mat.put(row: 0, col: 0, data: [1.0, 2.0, 1.0, 0.0, 1.0, 2.0, 3.0, 1.0]) + + Core.idct(src: mat, dst: dst) + + truth = Mat(rows: 1, cols: 8, type: CvType.CV_32F) + + try truth!.put(row: 0, col: 0, data: [3.3769724, -1.6215782, 2.3608727, 0.20730907, -0.86502546, 0.028082132, -0.7673766, 0.10917115]) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testIdctMatMatInt() throws { + let mat = Mat(rows: 2, cols: 8, type: CvType.CV_32F) + try mat.put(row: 0, col: 0, data: [1.0, 2.0, 1.0, 0.0, 1.0, 2.0, 3.0, 1.0]) + try mat.put(row: 1, col: 0, data: [1.0, 2.0, 1.0, 0.0, 1.0, 2.0, 3.0, 1.0]) + + Core.idct(src: mat, dst: dst, flags: DftFlags.DCT_ROWS.rawValue) + + truth = Mat(rows: 2, cols: 8, type: CvType.CV_32F) + + try truth!.put(row: 0, col: 0, data: [3.3769724, -1.6215782, 2.3608727, 0.20730907, -0.86502546, 0.028082132, -0.7673766, 0.10917115]) + try truth!.put(row: 1, col: 0, data: [3.3769724, -1.6215782, 2.3608727, 0.20730907, -0.86502546, 0.028082132, -0.7673766, 0.10917115]) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testIdftMatMat() throws { + let mat = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try mat.put(row: 0, col: 0, data: [1.0, 2.0, 3.0, 4.0]) + + Core.idft(src: mat, dst: dst) + + truth = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try truth!.put(row: 0, col: 0, data: [9, -9, 1, 3] as [Float]) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testIdftMatMatIntInt() throws { + let mat = Mat(rows: 2, cols: 4, type: CvType.CV_32F) + try mat.put(row: 0, col: 0, data: [1.0, 2.0, 3.0, 4.0] as [Float]) + try mat.put(row: 1, col: 0, data: [1.0, 2.0, 3.0, 4.0] as [Float]) + let dst = Mat() + + Core.idft(src: mat, dst: dst, flags: DftFlags.DFT_REAL_OUTPUT.rawValue, nonzeroRows: 1) + + truth = Mat(rows: 2, cols: 4, type: CvType.CV_32F) + try truth!.put(row: 0, col: 0, data: [18, -18, 2, 6] as [Float]) + try truth!.put(row: 1, col: 0, data: [0, 0, 0, 0] as [Float]) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testInRange() throws { + let gray0copy = gray0.clone() + try gray0copy.put(row: 1, col: 1, data: [100, -105, -55] as [Int8]) + + Core.inRange(src: gray0copy, lowerb: Scalar(120), upperb: Scalar(160), dst: dst) + + var vals = [Int8](repeating: 0, count: 3) + try dst.get(row: 1, col: 1, data: &vals) + + XCTAssertEqual(0, vals[0]) + XCTAssertEqual(-1, vals[1]) + XCTAssertEqual(0, vals[2]) + XCTAssertEqual(1, Core.countNonZero(src: dst)) + } + + func testInsertChannel() throws { + dst = rgba128.clone() + Core.insertChannel(src: gray0, dst: dst, coi: 0) + Core.insertChannel(src: gray0, dst: dst, coi: 1) + Core.insertChannel(src: gray0, dst: dst, coi: 2) + Core.insertChannel(src: gray0, dst: dst, coi: 3) + + try assertMatEqual(rgba0, dst) + } + + func testInvertMatMat() throws { + let src = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + try src.put(row: 0, col: 0, data: [1.0] as [Float]) + try src.put(row: 0, col: 1, data: [2.0] as [Float]) + try src.put(row: 1, col: 0, data: [1.5] as [Float]) + try src.put(row: 1, col: 1, data: [4.0] as [Float]) + + Core.invert(src: src, dst: dst) + + truth = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + + try truth!.put(row: 0, col: 0, data: [4.0] as [Float]) + try truth!.put(row: 0, col: 1, data: [-2.0] as [Float]) + try truth!.put(row: 1, col: 0, data: [-1.5] as [Float]) + try truth!.put(row: 1, col: 1, data: [1.0] as [Float]) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testInvertMatMatInt() throws { + let src = Mat.eye(rows: 3, cols: 3, type: CvType.CV_32FC1) + try src.put(row: 0, col: 2, data: [1] as [Float]) + + let cond = Core.invert(src: src, dst: dst, flags: DecompTypes.DECOMP_SVD.rawValue) + + truth = Mat.eye(rows: 3, cols: 3, type: CvType.CV_32FC1) + try truth!.put(row: 0, col: 2, data: [-1] as [Float]) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + XCTAssertEqual(0.3819660544395447, cond, accuracy:OpenCVTestCase.EPS) + } + + func testKmeansMatIntMatTermCriteriaIntInt() throws { + let data = Mat(rows: 4, cols: 5, type: CvType.CV_32FC1) + try data.put(row: 0, col: 0, data: [1, 2, 3, 4, 5] as [Float]) + try data.put(row: 1, col: 0, data: [2, 3, 4, 5, 6] as [Float]) + try data.put(row: 2, col: 0, data: [5, 4, 3, 2, 1] as [Float]) + try data.put(row: 3, col: 0, data: [6, 5, 4, 3, 2] as [Float]) + let criteria = TermCriteria(type: TermCriteria.eps, maxCount: 0, epsilon: OpenCVTestCase.EPS) + let labels = Mat() + + Core.kmeans(data: data, K: 2, bestLabels: labels, criteria: criteria, attempts: 1, flags: KmeansFlags.KMEANS_PP_CENTERS.rawValue) + + var first_center = [Int32](repeating: 0, count: 1) + try labels.get(row: 0, col: 0, data: &first_center) + let c1 = first_center[0] + let expected_labels = Mat(rows: 4, cols: 1, type: CvType.CV_32S) + try expected_labels.put(row: 0, col: 0, data: [c1, c1, 1 - c1, 1 - c1]) + try assertMatEqual(expected_labels, labels) + } + + func testKmeansMatIntMatTermCriteriaIntIntMat() throws { + let data = Mat(rows: 4, cols: 5, type: CvType.CV_32FC1) + try data.put(row: 0, col: 0, data: [1, 2, 3, 4, 5] as [Float]) + try data.put(row: 1, col: 0, data: [2, 3, 4, 5, 6] as [Float]) + try data.put(row: 2, col: 0, data: [5, 4, 3, 2, 1] as [Float]) + try data.put(row: 3, col: 0, data: [6, 5, 4, 3, 2] as [Float]) + let criteria = TermCriteria(type:TermCriteria.eps, maxCount: 0, epsilon: OpenCVTestCase.EPS) + let labels = Mat() + let centers = Mat() + + Core.kmeans(data: data, K: 2, bestLabels: labels, criteria: criteria, attempts: 6, flags: KmeansFlags.KMEANS_RANDOM_CENTERS.rawValue, centers: centers) + + var first_center = [Int32](repeating: 0, count: 1) + try labels.get(row: 0, col: 0, data: &first_center) + let c1 = first_center[0] + let expected_labels = Mat(rows: 4, cols: 1, type: CvType.CV_32S) + try expected_labels.put(row: 0, col: 0, data: [c1, c1, 1 - c1, 1 - c1]) + let expected_centers = Mat(rows: 2, cols: 5, type: CvType.CV_32FC1) + try expected_centers.put(row: c1, col: 0, data: [1.5, 2.5, 3.5, 4.5, 5.5] as [Float]) + try expected_centers.put(row: 1 - c1, col: 0, data: [5.5, 4.5, 3.5, 2.5, 1.5] as [Float]) + try assertMatEqual(expected_labels, labels) + try assertMatEqual(expected_centers, centers, OpenCVTestCase.EPS) + } + + func testLineMatPointPointScalar() { + let nPoints = min(gray0.cols(), gray0.rows()) + let point1 = Point(x: 0, y: 0) + let point2 = Point(x: nPoints, y: nPoints) + let color = Scalar(255) + + Imgproc.line(img: gray0, pt1: point1, pt2: point2, color: color) + + XCTAssert(nPoints == Core.countNonZero(src: gray0)) + } + + func testLineMatPointPointScalarInt() { + let nPoints = min(gray0.cols(), gray0.rows()) + let point1 = Point(x: 0, y: 0) + let point2 = Point(x: nPoints, y: nPoints) + + Imgproc.line(img: gray0, pt1: point1, pt2: point2, color: colorWhite, thickness: 1) + + XCTAssert(nPoints == Core.countNonZero(src: gray0)) + } + + func testLineMatPointPointScalarIntIntInt() { + let nPoints = min(gray0.cols(), gray0.rows()) + let point1 = Point(x: 3, y: 4) + let point2 = Point(x: nPoints, y: nPoints) + let point1_4 = Point(x: 3 * 4, y: 4 * 4) + let point2_4 = Point(x: nPoints * 4, y: nPoints * 4) + + Imgproc.line(img: gray0, pt1: point2, pt2: point1, color: colorWhite, thickness: 2, lineType: .LINE_8, shift: 0) + + XCTAssertFalse(0 == Core.countNonZero(src: gray0)) + + Imgproc.line(img: gray0, pt1: point2_4, pt2: point1_4, color: colorBlack, thickness: 2, lineType: .LINE_8, shift: 2) + + XCTAssertEqual(0, Core.countNonZero(src: gray0)) + } + + func testLog() throws { + let mat = Mat(rows: 1, cols: 4, type: CvType.CV_32FC1) + try mat.put(row: 0, col: 0, data: [1.0, 10.0, 100.0, 1000.0]) + + Core.log(src: mat, dst: dst) + + let expected = Mat(rows: 1, cols: 4, type: CvType.CV_32FC1) + try expected.put(row: 0, col: 0, data: [0, 2.3025851, 4.6051702, 6.9077554]) + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testLUTMatMatMat() throws { + let lut = Mat(rows: 1, cols: 256, type: CvType.CV_8UC1) + lut.setTo(scalar: Scalar(0)) + + Core.LUT(src: grayRnd, lut: lut, dst: dst) + + try assertMatEqual(gray0, dst) + + lut.setTo(scalar: Scalar(255)) + + Core.LUT(src: grayRnd, lut: lut, dst: dst) + + try assertMatEqual(gray255, dst) + } + + func testMagnitude() throws { + let x = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + let y = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [3.0, 5.0, 9.0, 6.0]) + try y.put(row: 0, col: 0, data: [4.0, 12.0, 40.0, 8.0]) + + Core.magnitude(x: x, y: y, magnitude: dst) + + let out = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try out.put(row: 0, col: 0, data: [5.0, 13.0, 41.0, 10.0]) + try assertMatEqual(out, dst, OpenCVTestCase.EPS) + + Core.magnitude(x: gray0_32f, y: gray255_32f, magnitude: dst) + + try assertMatEqual(gray255_32f, dst, OpenCVTestCase.EPS) + } + + func testMahalanobis() { + Core.setRNGSeed(seed: 45) + let src = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32F) + Core.randu(dst: src, low: -128, high: 128) + + var covar = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32F) + let mean = Mat(rows: 1, cols: OpenCVTestCase.matSize, type: CvType.CV_32F) + Core.calcCovarMatrix(samples: src, covar: covar, mean: mean, flags: CovarFlags.COVAR_ROWS.rawValue | CovarFlags.COVAR_NORMAL.rawValue, ctype: CvType.CV_32F) + covar = covar.inv() + + let line1 = src.row(0) + let line2 = src.row(1) + + var d = Core.Mahalanobis(v1: line1, v2: line1, icovar: covar) + + XCTAssertEqual(0.0, d) + + d = Core.Mahalanobis(v1: line1, v2: line2, icovar: covar) + + XCTAssert(d > 0.0) + } + + func testMax() throws { + Core.max(src1: gray0, src2: gray255, dst: dst) + + try assertMatEqual(gray255, dst) + + let x = Mat(rows: 1, cols: 1, type: CvType.CV_32F) + let y = Mat(rows: 1, cols: 1, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [23.0]) + try y.put(row: 0, col: 0, data: [4.0]) + + Core.max(src1: x, src2: y, dst: dst) + + let truth = Mat(rows: 1, cols: 1, type: CvType.CV_32F) + try truth.put(row: 0, col: 0, data: [23.0]) + try assertMatEqual(truth, dst, OpenCVTestCase.EPS) + } + + func testMeanMat() { + let mean = Core.mean(src: makeMask(gray128)) + + assertScalarEqual(Scalar(64), mean, OpenCVTestCase.EPS) + } + + func testMeanMatMat() { + let mask1 = makeMask(gray1.clone()) + let mask2 = makeMask(gray0, vals: [1]) + + let mean1 = Core.mean(src: grayRnd, mask: mask1) + let mean2 = Core.mean(src: grayRnd, mask: mask2) + let mean = Core.mean(src: grayRnd, mask: gray1) + + assertScalarEqual(mean, Scalar(0.5 * (mean1.val[0].doubleValue + mean2.val[0].doubleValue)), OpenCVTestCase.EPS) + } + + func testMeanStdDevMatMatMat() { + let mean = DoubleVector() + let stddev = DoubleVector() + Core.meanStdDev(src: rgbLena, mean: mean, stddev: stddev) + + let expectedMean = [105.3989906311035, 99.56269836425781, 179.7303047180176] + let expectedDev = [33.74205485167219, 52.8734582803278, 49.01569488056406] + + assertArrayEquals(expectedMean as [NSNumber], mean.array as [NSNumber], OpenCVTestCase.EPS) + assertArrayEquals(expectedDev as [NSNumber], stddev.array as [NSNumber], OpenCVTestCase.EPS) + } + + func testMeanStdDevMatMatMatMat() { + var submat = grayRnd.submat(rowStart: 0, rowEnd: grayRnd.rows() / 2, colStart: 0, colEnd: grayRnd.cols() / 2) + submat.setTo(scalar: Scalar(33)) + let mask = gray0.clone() + submat = mask.submat(rowStart: 0, rowEnd: mask.rows() / 2, colStart: 0, colEnd: mask.cols() / 2) + submat.setTo(scalar: Scalar(1)) + let mean = DoubleVector() + let stddev = DoubleVector() + + Core.meanStdDev(src: grayRnd, mean: mean, stddev: stddev, mask: mask) + + let expectedMean = [33] + let expectedDev = [0] + + assertArrayEquals(expectedMean as [NSNumber], mean.array as [NSNumber], OpenCVTestCase.EPS) + assertArrayEquals(expectedDev as [NSNumber], stddev.array as [NSNumber], OpenCVTestCase.EPS) + } + + func testMerge() throws { + let src1 = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(1)) + let src2 = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(2)) + let src3 = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(3)) + let srcArray = [src1, src2, src3] + + Core.merge(mv: srcArray, dst: dst) + + truth = Mat(rows: 2, cols: 2, type: CvType.CV_32FC3, scalar: Scalar(1, 2, 3)) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testMin() throws { + Core.min(src1: gray0, src2: gray255, dst: dst) + + try assertMatEqual(gray0, dst) + } + + func testMinMaxLocMat() throws { + let minVal:Double = 1 + let maxVal:Double = 10 + let minLoc = Point(x: gray3.cols() / 4, y: gray3.rows() / 2) + let maxLoc = Point(x: gray3.cols() / 2, y: gray3.rows() / 4) + let gray3copy = gray3.clone() + try gray3copy.put(row: minLoc.y, col: minLoc.x, data: [minVal]) + try gray3copy.put(row: maxLoc.y, col: maxLoc.x, data: [maxVal]) + + let mmres = Core.minMaxLoc(gray3copy) + + XCTAssertEqual(minVal, mmres.minVal) + XCTAssertEqual(maxVal, mmres.maxVal) + assertPointEquals(minLoc, mmres.minLoc) + assertPointEquals(maxLoc, mmres.maxLoc) + } + + func testMinMaxLocMatMat() throws { + let src = Mat(rows: 4, cols: 4, type: CvType.CV_8U) + try src.put(row: 0, col: 0, data: [2, 4, 27, 3] as [Int8]) + try src.put(row: 1, col: 0, data: [0, 8, 7, -126] as [Int8]) + try src.put(row: 2, col: 0, data: [13, 4, 13, 4] as [Int8]) + try src.put(row: 3, col: 0, data: [6, 4, 2, 13] as [Int8]) + let mask = Mat(rows: 4, cols: 4, type: CvType.CV_8U, scalar: Scalar(0)) + mask.submat(rowStart: 1, rowEnd: 3, colStart: 1, colEnd: 4).setTo(scalar: Scalar(1)) + + let res = Core.minMaxLoc(src, mask: mask) + + XCTAssertEqual(4.0, res.minVal) + XCTAssertEqual(130.0, res.maxVal) + assertPointEquals(Point(x: 1, y: 2), res.minLoc) + assertPointEquals(Point(x: 3, y: 1), res.maxLoc) + } + + func testMixChannels() throws { + let rgba0Copy = rgba0.clone() + rgba0Copy.setTo(scalar: Scalar(10, 20, 30, 40)) + let src = [rgba0Copy] + let dst = [gray3, gray2, gray1, gray0, getMat(CvType.CV_8UC3, vals: [0, 0, 0])] + let fromTo = IntVector([ + 3, 0, + 3, 1, + 2, 2, + 0, 3, + 2, 4, + 1, 5, + 0, 6]) + + Core.mixChannels(src: src, dst: dst, fromTo: fromTo) + + try assertMatEqual(getMat(CvType.CV_8U, vals: [40]), dst[0]) + try assertMatEqual(getMat(CvType.CV_8U, vals: [40]), dst[1]) + try assertMatEqual(getMat(CvType.CV_8U, vals: [30]), dst[2]) + try assertMatEqual(getMat(CvType.CV_8U, vals: [10]), dst[3]) + try assertMatEqual(getMat(CvType.CV_8UC3, vals: [30, 20, 10]), dst[4]) + } + + func testMulSpectrumsMatMatMatInt() throws { + let src1 = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try src1.put(row: 0, col: 0, data: [1.0, 2.0, 3.0, 4.0]) + + let src2 = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try src2.put(row: 0, col: 0, data: [1.0, 2.0, 3.0, 4.0]) + + Core.mulSpectrums(a: src1, b: src2, c: dst, flags: DftFlags.DFT_ROWS.rawValue) + + let expected = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try expected.put(row: 0, col: 0, data: [1, -5, 12, 16] as [Float]) + + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testMulSpectrumsMatMatMatIntBoolean() throws { + let src1 = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try src1.put(row: 0, col: 0, data: [1.0, 2.0, 3.0, 4.0]) + let src2 = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try src2.put(row: 0, col: 0, data: [1.0, 2.0, 3.0, 4.0]) + + Core.mulSpectrums(a: src1, b: src2, c: dst, flags: DftFlags.DFT_ROWS.rawValue, conjB: true) + + let expected = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try expected.put(row: 0, col: 0, data: [1, 13, 0, 16] as [Float]) + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testMultiplyMatMatMat() throws { + Core.multiply(src1: gray0, src2: gray255, dst: dst) + + try assertMatEqual(gray0, dst) + } + + func testMultiplyMatMatMatDouble() throws { + Core.multiply(src1: gray1, src2: gray1, dst: dst, scale: 2.0) + + try assertMatEqual(gray2, dst) + + } + + func testMultiplyMatMatMatDoubleInt() throws { + Core.multiply(src1: gray1, src2: gray2, dst: dst, scale: 1.5, dtype: CvType.CV_32F) + + try assertMatEqual(gray3_32f, dst, OpenCVTestCase.EPS) + } + + func testMulTransposedMatMatBoolean() throws { + Core.mulTransposed(src: grayE_32f, dst: dst, aTa: true) + + try assertMatEqual(grayE_32f, dst, OpenCVTestCase.EPS) + } + + func testMulTransposedMatMatBooleanMatDouble() throws { + Core.mulTransposed(src: grayE_32f, dst: dst, aTa: true, delta: gray0_32f, scale: 2) + + truth = gray0_32f; + truth!.diag().setTo(scalar: Scalar(2)) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testMulTransposedMatMatBooleanMatDoubleInt() throws { + let a = getMat(CvType.CV_32F, vals: [1]) + + Core.mulTransposed(src: a, dst: dst, aTa: true, delta: gray0_32f, scale: 3, dtype: CvType.CV_64F) + + try assertMatEqual(getMat(CvType.CV_64F, vals: [3 * a.rows()] as [NSNumber]), dst, OpenCVTestCase.EPS) + } + + func testNormalizeMatMat() throws { + let m = gray0.clone() + m.diag().setTo(scalar: Scalar(2)) + + Core.normalize(src: m, dst: dst) + + try assertMatEqual(gray0, dst) + } + + func testNormalizeMatMatDoubleDoubleInt() throws { + let src = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try src.put(row: 0, col: 0, data: [1.0, 2.0, 3.0, 4.0]) + + Core.normalize(src: src, dst: dst, alpha: 1.0, beta: 2.0, norm_type: .NORM_INF) + + let expected = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try expected.put(row: 0, col: 0, data: [0.25, 0.5, 0.75, 1]) + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testNormalizeMatMatDoubleDoubleIntInt() throws { + let src = Mat(rows: 1, cols: 5, type: CvType.CV_32F) + try src.put(row: 0, col: 0, data: [0, 1, 2, 3, 4] as [Float]) + + Core.normalize(src: src, dst: dst, alpha: 1, beta: 2, norm_type: .NORM_MINMAX, dtype: CvType.CV_64F) + + let expected = Mat(rows: 1, cols: 5, type: CvType.CV_64F) + try expected.put(row: 0, col: 0, data: [1, 1.25, 1.5, 1.75, 2]) + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testNormalizeMatMatDoubleDoubleIntIntMat() throws { + let src = Mat(rows: 1, cols: 5, type: CvType.CV_32F) + try src.put(row: 0, col: 0, data: [0, 1, 2, 3, 4] as [Float]) + let mask = Mat(rows: 1, cols: 5, type: CvType.CV_8U) + try mask.put(row: 0, col: 0, data: [1, 0, 0, 0, 1] as [Int8]) + dst = src.clone() + + Core.normalize(src: src, dst: dst, alpha: 1, beta: 2, norm_type: .NORM_MINMAX, dtype: CvType.CV_32F, mask: mask) + + let expected = Mat(rows: 1, cols: 5, type: CvType.CV_32F) + try expected.put(row: 0, col: 0, data: [1, 1, 2, 3, 2] as [Float]) + try assertMatEqual(expected, dst, OpenCVTestCase.EPS) + } + + func testNormMat() throws { + let n = Core.norm(src1: gray1) + + XCTAssertEqual(10, n) + } + + func testNormMatInt() throws { + let n = Core.norm(src1: gray127, normType: .NORM_INF) + + XCTAssertEqual(127, n) + } + + func testNormMatIntMat() throws { + let n = Core.norm(src1: gray3, normType: .NORM_L1, mask: gray0) + + XCTAssertEqual(0.0, n) + } + + func testNormMatMat() throws { + let n = Core.norm(src1: gray0, src2: gray1) + + XCTAssertEqual(10.0, n) + } + + func testNormMatMatInt() throws { + let n = Core.norm(src1: gray127, src2: gray1, normType: .NORM_INF) + + XCTAssertEqual(126.0, n) + } + + func testNormMatMatIntMat() throws { + let n = Core.norm(src1: gray3, src2: gray0, normType: .NORM_L1, mask: makeMask(gray0.clone(), vals: [1])) + + XCTAssertEqual(150.0, n) + } + + func testPCABackProject() throws { + let mean = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try mean.put(row: 0, col: 0, data: [2, 4, 4, 8] as [Float]) + let vectors = Mat(rows: 1, cols: 4, type: CvType.CV_32F, scalar: Scalar(0)) + try vectors.put(row: 0, col: 0, data: [0.2, 0.4, 0.4, 0.8]) + let data = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try data.put(row: 0, col: 0, data: [-5, 0, -10] as [Float]) + let result = Mat() + + Core.PCABackProject(data: data, mean: mean, eigenvectors: vectors, result: result) + + let truth = Mat(rows: 3, cols: 4, type: CvType.CV_32F) + try truth.put(row: 0, col: 0, data: [1, 2, 2, 4] as [Float]) + try truth.put(row: 1, col: 0, data: [2, 4, 4, 8] as [Float]) + try truth.put(row: 2, col: 0, data: [0, 0, 0, 0] as [Float]) + try assertMatEqual(truth, result, OpenCVTestCase.EPS) + } + + func testPCAComputeMatMatMat() throws { + let data = Mat(rows: 3, cols: 4, type: CvType.CV_32F) + try data.put(row: 0, col: 0, data: [1, 2, 2, 4] as [Float]) + try data.put(row: 1, col: 0, data: [2, 4, 4, 8] as [Float]) + try data.put(row: 2, col: 0, data: [3, 6, 6, 12] as [Float]) + let mean = Mat() + let vectors = Mat() + + Core.PCACompute(data: data, mean: mean, eigenvectors: vectors) + let mean_truth = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try mean_truth.put(row: 0, col: 0, data: [2, 4, 4, 8] as [Float]) + let vectors_truth = Mat(rows: 3, cols: 4, type: CvType.CV_32F, scalar: Scalar(0)) + try vectors_truth.put(row: 0, col: 0, data: [0.2, 0.4, 0.4, 0.8] as [Float]) + try assertMatEqual(mean_truth, mean, OpenCVTestCase.EPS) + + // eigenvectors are normalized (length = 1), + // but direction is unknown (v and -v are both eigen vectors) + // so this direct check doesn't work: + // try assertMatEqual(vectors_truth, vectors, OpenCVTestCase.EPS) + for i in 0..<1 { + let vec0 = vectors_truth.row(Int32(i)) + let vec1 = vectors.row(Int32(i)) + let vec1_ = Mat() + Core.subtract(src1: Mat(rows: 1, cols: 4, type: CvType.CV_32F, scalar: Scalar(0)), src2: vec1, dst: vec1_) + let scale1 = Core.norm(src1: vec0, src2: vec1) + let scale2 = Core.norm(src1: vec0, src2: vec1_) + XCTAssert(min(scale1, scale2) < OpenCVTestCase.EPS) + } + } + + func testPCAComputeMatMatMatInt() throws { + let data = Mat(rows: 3, cols: 4, type: CvType.CV_32F) + try data.put(row: 0, col: 0, data: [1, 2, 2, 4] as [Float]) + try data.put(row: 1, col: 0, data: [2, 4, 4, 8] as [Float]) + try data.put(row: 2, col: 0, data: [3, 6, 6, 12] as [Float]) + let mean = Mat() + let vectors = Mat() + + Core.PCACompute(data:data, mean:mean, eigenvectors:vectors, maxComponents:1) + + let mean_truth = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try mean_truth.put(row: 0, col: 0, data: [2, 4, 4, 8] as [Float]) + let vectors_truth = Mat(rows: 1, cols: 4, type: CvType.CV_32F, scalar: Scalar(0)) + try vectors_truth.put(row: 0, col: 0, data: [0.2, 0.4, 0.4, 0.8] as [Float]) + try assertMatEqual(mean_truth, mean, OpenCVTestCase.EPS) + // eigenvectors are normalized (length = 1), + // but direction is unknown (v and -v are both eigen vectors) + // so this direct check doesn't work: + // try assertMatEqual(vectors_truth, vectors, OpenCVTestCase.EPS) + for i in 0..<1 { + let vec0 = vectors_truth.row(Int32(i)) + let vec1 = vectors.row(Int32(i)) + let vec1_ = Mat() + Core.subtract(src1: Mat(rows: 1, cols: 4, type: CvType.CV_32F, scalar: Scalar(0)), src2: vec1, dst: vec1_) + let scale1 = Core.norm(src1: vec0, src2: vec1) + let scale2 = Core.norm(src1: vec0, src2: vec1_) + XCTAssert(min(scale1, scale2) < OpenCVTestCase.EPS) + } + } + + func testPCAProject() throws { + let mean = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try mean.put(row: 0, col: 0, data: [2, 4, 4, 8] as [Float]) + let vectors = Mat(rows: 1, cols: 4, type: CvType.CV_32F, scalar: Scalar(0)) + try vectors.put(row: 0, col: 0, data: [0.2, 0.4, 0.4, 0.8] as [Float]) + let data = Mat(rows: 3, cols: 4, type: CvType.CV_32F) + try data.put(row: 0, col: 0, data: [1, 2, 2, 4] as [Float]) + try data.put(row: 1, col: 0, data: [2, 4, 4, 8] as [Float]) + try data.put(row: 2, col: 0, data: [0, 0, 0, 0] as [Float]) + let result = Mat() + + Core.PCAProject(data: data, mean: mean, eigenvectors: vectors, result: result) + + let truth = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try truth.put(row: 0, col: 0, data: [-5, 0, -10] as [Float]) + try assertMatEqual(truth, result, OpenCVTestCase.EPS) + } + + func testPerspectiveTransform() throws { + let src = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32FC2) + Core.randu(dst: src, low: 0, high: 256) + let transformMatrix = Mat.eye(rows: 3, cols: 3, type: CvType.CV_32F) + + Core.perspectiveTransform(src: src, dst: dst, m: transformMatrix) + try assertMatEqual(src, dst, OpenCVTestCase.EPS) + } + + func testPerspectiveTransform3D() throws { + let src = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32FC3) + Core.randu(dst: src, low: 0, high: 256) + let transformMatrix = Mat.eye(rows: 4, cols: 4, type: CvType.CV_32F) + + Core.perspectiveTransform(src: src, dst: dst, m: transformMatrix) + + try assertMatEqual(src, dst, OpenCVTestCase.EPS) + } + + func testPhaseMatMatMat() throws { + let x = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [10.0, 10.0, 20.0, 5.0] as [Float]) + let y = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try y.put(row: 0, col: 0, data: [20.0, 15.0, 20.0, 20.0] as [Float]) + let gold = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try gold.put(row: 0, col: 0, data: [atan2rad(20, 10), atan2rad(15, 10), atan2rad(20, 20), atan2rad(20, 5)]) + + Core.phase(x: x, y: y, angle: dst) + + try assertMatEqual(gold, dst, OpenCVTestCase.EPS) + } + + func testPhaseMatMatMatBoolean() throws { + let x = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [10.0, 10.0, 20.0, 5.0] as [Float]) + let y = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try y.put(row: 0, col: 0, data: [20.0, 15.0, 20.0, 20.0] as [Float]) + let gold = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try gold.put(row: 0, col: 0, data: [atan2deg(20, 10), atan2deg(15, 10), atan2deg(20, 20), atan2deg(20, 5)]) + + Core.phase(x: x, y: y, angle: dst, angleInDegrees: true) + + try assertMatEqual(gold, dst, OpenCVTestCase.EPS * 180 / Double.pi) + } + + func testPolarToCartMatMatMatMat() throws { + let magnitude = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try magnitude.put(row: 0, col: 0, data: [5.0, 10.0, 13.0]) + let angle = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try angle.put(row: 0, col: 0, data: [0.92729962, 0.92729962, 1.1759995]) + let xCoordinate = Mat() + let yCoordinate = Mat() + + Core.polarToCart(magnitude: magnitude, angle: angle, x: xCoordinate, y: yCoordinate) + + let x = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [3.0, 6.0, 5, 0]) + let y = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try y.put(row: 0, col: 0, data: [4.0, 8.0, 12.0]) + try assertMatEqual(x, xCoordinate, OpenCVTestCase.EPS) + try assertMatEqual(y, yCoordinate, OpenCVTestCase.EPS) + } + + func testPolarToCartMatMatMatMatBoolean() throws { + let magnitude = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try magnitude.put(row: 0, col: 0, data: [5.0, 10.0, 13.0]) + let angle = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try angle.put(row: 0, col: 0, data: [0.92729962, 0.92729962, 1.1759995]) + let xCoordinate = Mat() + let yCoordinate = Mat() + + Core.polarToCart(magnitude: magnitude, angle: angle, x: xCoordinate, y: yCoordinate, angleInDegrees: true) + + let x = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try x.put(row: 0, col: 0, data: [4.9993458, 9.9986916, 12.997262]) + let y = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try y.put(row: 0, col: 0, data: [0.080918625, 0.16183725, 0.26680708]) + try assertMatEqual(x, xCoordinate, OpenCVTestCase.EPS) + try assertMatEqual(y, yCoordinate, OpenCVTestCase.EPS) + } + + func testPow() throws { + Core.pow(src: gray2, power: 7, dst: dst) + + try assertMatEqual(gray128, dst) + } + + func testRandn() { + Core.randn(dst: gray0, mean: 100, stddev: 23) + + XCTAssertEqual(100, Core.mean(src: gray0).val[0] as! Double, accuracy:23 / 2) + } + + func testRandShuffleMat() throws { + let original = Mat(rows: 1, cols: 10, type: CvType.CV_32F) + try original.put(row: 0, col: 0, data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as [Float]) + let shuffled = original.clone() + + Core.randShuffle(dst: shuffled) + + try assertMatNotEqual(original, shuffled, OpenCVTestCase.EPS) + let dst1 = Mat() + let dst2 = Mat() + Core.sort(src: original, dst: dst1, flags: SortFlags.SORT_ASCENDING.rawValue) + Core.sort(src: shuffled, dst: dst2, flags: SortFlags.SORT_ASCENDING.rawValue) + try assertMatEqual(dst1, dst2, OpenCVTestCase.EPS) + } + + func testRandShuffleMatDouble() throws { + let original = Mat(rows: 1, cols: 10, type: CvType.CV_32F) + try original.put(row: 0, col: 0, data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as [Float]) + let shuffled = original.clone() + + Core.randShuffle(dst: shuffled, iterFactor: 10) + + try assertMatNotEqual(original, shuffled, OpenCVTestCase.EPS) + let dst1 = Mat() + let dst2 = Mat() + Core.sort(src: original, dst: dst1, flags: SortFlags.SORT_ASCENDING.rawValue) + Core.sort(src: shuffled, dst: dst2, flags: SortFlags.SORT_ASCENDING.rawValue) + try assertMatEqual(dst1, dst2, OpenCVTestCase.EPS) + } + + func testRandu() { + Core.randu(dst: gray0, low: 3, high: 23) + XCTAssert(Core.checkRange(a: gray0, quiet: true, minVal: 3, maxVal: 23)) + } + + func testRectangleMatPointPointScalar() { + let bottomRight = Point(x: gray0.cols() / 2, y: gray0.rows() / 2) + let topLeft = Point(x: 0, y: 0) + let color = Scalar(128) + + Imgproc.rectangle(img: gray0, pt1: bottomRight, pt2: topLeft, color: color) + + XCTAssert(0 != Core.countNonZero(src: gray0)) + } + + func testRectangleMatPointPointScalarInt() { + let bottomRight = Point(x: gray0.cols(), y: gray0.rows()) + let topLeft = Point(x: 0, y: 0) + let color = Scalar(128) + + Imgproc.rectangle(img: gray0, pt1: bottomRight, pt2: topLeft, color: color, thickness: 2) + Imgproc.rectangle(img: gray0, pt1: bottomRight, pt2: topLeft, color: colorBlack) + + XCTAssert(0 != Core.countNonZero(src: gray0)) + } + + func testRectangleMatPointPointScalarIntInt() { + let bottomRight = Point(x: gray0.cols() / 2, y: gray0.rows() / 2) + let topLeft = Point(x: 0, y: 0) + let color = Scalar(128) + + Imgproc.rectangle(img: gray0, pt1: bottomRight, pt2: topLeft, color: color, thickness: 2, lineType: .LINE_AA, shift: 0) + Imgproc.rectangle(img: gray0, pt1: bottomRight, pt2: topLeft, color: colorBlack, thickness: 2, lineType: .LINE_4, shift: 0) + + XCTAssert(0 != Core.countNonZero(src: gray0)) + } + + func testRectangleMatPointPointScalarIntIntInt() { + let bottomRight1 = Point(x: gray0.cols(), y: gray0.rows()) + let bottomRight2 = Point(x: gray0.cols() / 2, y: gray0.rows() / 2) + let topLeft = Point(x: 0, y: 0) + let color = Scalar(128) + + Imgproc.rectangle(img: gray0, pt1: bottomRight1, pt2: topLeft, color: color, thickness: 2, lineType: .LINE_8, shift: 1) + + XCTAssert(0 != Core.countNonZero(src: gray0)) + + Imgproc.rectangle(img: gray0, pt1: bottomRight2, pt2: topLeft, color: colorBlack, thickness: 2, lineType: .LINE_8, shift: 0) + + XCTAssertEqual(0, Core.countNonZero(src: gray0)) + } + + func testReduceMatMatIntInt() throws { + let src = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + + try src.put(row: 0, col: 0, data: [1, 0] as [Float]) + try src.put(row: 1, col: 0, data: [3, 0] as [Float]) + + Core.reduce(src: src, dst: dst, dim: 0, rtype: Int32(Core.REDUCE_AVG)) + + let out = Mat(rows: 1, cols: 2, type: CvType.CV_32F) + try out.put(row: 0, col: 0, data: [2, 0] as [Float]) + try assertMatEqual(out, dst, OpenCVTestCase.EPS) + } + + func testReduceMatMatIntIntInt() throws { + let src = Mat(rows: 2, cols: 2, type: CvType.CV_32F) + try src.put(row: 0, col: 0, data: [1, 0] as [Float]) + try src.put(row: 1, col: 0, data: [2, 3] as [Float]) + + Core.reduce(src: src, dst: dst, dim: 1, rtype: Int32(Core.REDUCE_SUM), dtype: CvType.CV_64F) + + let out = Mat(rows: 2, cols: 1, type: CvType.CV_64F) + try out.put(row: 0, col: 0, data: [1, 5] as [Double]) + try assertMatEqual(out, dst, OpenCVTestCase.EPS) + } + + func testRepeat() throws { + let src = Mat(rows: 1, cols: 2, type: CvType.CV_32F, scalar: Scalar(0)) + + Core.repeat(src: src, ny: OpenCVTestCase.matSize, nx: OpenCVTestCase.matSize / 2, dst: dst) + + try assertMatEqual(gray0_32f, dst, OpenCVTestCase.EPS) + } + + func testScaleAdd() throws { + Core.scaleAdd(src1: gray3, alpha: 2.0, src2: gray3, dst: dst) + + try assertMatEqual(gray9, dst) + } + + func testSetIdentityMat() throws { + Core.setIdentity(mtx: gray0_32f) + + try assertMatEqual(grayE_32f, gray0_32f, OpenCVTestCase.EPS) + } + + func testSetIdentityMatScalar() throws { + let m = gray0_32f; + + Core.setIdentity(mtx: m, s: Scalar(5)) + + truth = Mat(size: m.size(), type: m.type(), scalar: Scalar(0)) + truth!.diag().setTo(scalar: Scalar(5)) + try assertMatEqual(truth!, m, OpenCVTestCase.EPS) + } + + func testSolveCubic() throws { + let coeffs = Mat(rows: 1, cols: 4, type: CvType.CV_32F) + try coeffs.put(row: 0, col: 0, data: [1, 6, 11, 6] as [Float]) + + XCTAssertEqual(3, Core.solveCubic(coeffs: coeffs, roots: dst)) + + let roots = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try roots.put(row: 0, col: 0, data: [-3, -1, -2] as [Float]) + try assertMatEqual(roots, dst, OpenCVTestCase.EPS) + } + + func testSolveMatMatMat() throws { + let a = Mat(rows: 3, cols: 3, type: CvType.CV_32F) + try a.put(row: 0, col: 0, data: [1, 1, 1] as [Float]) + try a.put(row: 1, col: 0, data: [1, -2, 2] as [Float]) + try a.put(row: 2, col: 0, data: [1, 2, 1] as [Float]) + let b = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try b.put(row: 0, col: 0, data: [0, 4, 2] as [Float]) + + XCTAssert(Core.solve(src1: a, src2: b, dst: dst)) + + let res = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try res.put(row: 0, col: 0, data: [-12, 2, 10] as [Float]) + try assertMatEqual(res, dst, OpenCVTestCase.EPS) + } + + func testSolveMatMatMatInt() throws { + let a = Mat(rows: 3, cols: 3, type: CvType.CV_32F) + try a.put(row: 0, col: 0, data: [1, 1, 1] as [Float]) + try a.put(row: 1, col: 0, data: [1, -2, 2] as [Float]) + try a.put(row: 2, col: 0, data: [1, 2, 1] as [Float]) + let b = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try b.put(row: 0, col: 0, data: [0, 4, 2] as [Float]) + + XCTAssert(Core.solve(src1: a, src2: b, dst: dst, flags: DecompTypes.DECOMP_QR.rawValue | DecompTypes.DECOMP_NORMAL.rawValue)) + + let res = Mat(rows: 3, cols: 1, type: CvType.CV_32F) + try res.put(row: 0, col: 0, data: [-12, 2, 10] as [Float]) + try assertMatEqual(res, dst, OpenCVTestCase.EPS) + } + + func testSolvePolyMatMat() throws { + let coeffs = Mat(rows: 4, cols: 1, type: CvType.CV_32F) + try coeffs.put(row: 0, col: 0, data: [-6, 11, -6, 1] as [Float]) + let roots = Mat() + + XCTAssertGreaterThanOrEqual(1e-6, abs(Core.solvePoly(coeffs: coeffs, roots: roots))) + + truth = Mat(rows: 3, cols: 1, type: CvType.CV_32FC2) + try truth!.put(row: 0, col: 0, data: [1, 0, 2, 0, 3, 0] as [Float]) + try assertMatEqual(truth!, roots, OpenCVTestCase.EPS) + } + + func testSolvePolyMatMatInt() throws { + let coeffs = Mat(rows: 4, cols: 1, type: CvType.CV_32F) + try coeffs.put(row: 0, col: 0, data: [-6, 11, -6, 1] as [Float]) + let roots = Mat() + + XCTAssertEqual(10.198039027185569, Core.solvePoly(coeffs: coeffs, roots: roots, maxIters: 1)) + + truth = Mat(rows: 3, cols: 1, type: CvType.CV_32FC2) + try truth!.put(row: 0, col: 0, data: [1, 0, -1, 2, -2, 12] as [Float]) + try assertMatEqual(truth!, roots, OpenCVTestCase.EPS) + } + + func testSort() { + var submat = gray0.submat(rowStart: 0, rowEnd: gray0.rows() / 2, colStart: 0, colEnd: gray0.cols() / 2) + submat.setTo(scalar: Scalar(1.0)) + + Core.sort(src: gray0, dst: dst, flags: SortFlags.SORT_EVERY_ROW.rawValue) + + submat = dst.submat(rowStart: 0, rowEnd: dst.rows() / 2, colStart: dst.cols() / 2, colEnd: dst.cols()) + XCTAssert(submat.total() == Core.countNonZero(src: submat)) + + Core.sort(src: gray0, dst: dst, flags: SortFlags.SORT_EVERY_COLUMN.rawValue) + + submat = dst.submat(rowStart: dst.rows() / 2, rowEnd: dst.rows(), colStart: 0, colEnd: dst.cols() / 2) + + XCTAssert(submat.total() == Core.countNonZero(src: submat)) + } + + func testSortIdx() throws { + let a = Mat.eye(rows: 3, cols: 3, type: CvType.CV_8UC1) + let b = Mat() + + Core.sortIdx(src: a, dst: b, flags: SortFlags.SORT_EVERY_ROW.rawValue | SortFlags.SORT_ASCENDING.rawValue) + + truth = Mat(rows: 3, cols: 3, type: CvType.CV_32SC1) + try truth!.put(row: 0, col: 0, data: [1, 2, 0] as [Int32]) + try truth!.put(row: 1, col: 0, data: [0, 2, 1] as [Int32]) + try truth!.put(row: 2, col: 0, data: [0, 1, 2] as [Int32]) + try assertMatEqual(truth!, b) + } + + func testSplit() throws { + let m = getMat(CvType.CV_8UC3, vals: [1, 2, 3]) + let cois = NSMutableArray() + + Core.split(m: m, mv: cois) + + try assertMatEqual(gray1, cois[0] as! Mat) + try assertMatEqual(gray2, cois[1] as! Mat) + try assertMatEqual(gray3, cois[2] as! Mat) + } + + func testSqrt() throws { + Core.sqrt(src: gray9_32f, dst: dst) + + try assertMatEqual(gray3_32f, dst, OpenCVTestCase.EPS) + + let rgba144 = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32FC4, scalar: Scalar.all(144)) + let rgba12 = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32FC4, scalar: Scalar.all(12)) + + Core.sqrt(src: rgba144, dst: dst) + + try assertMatEqual(rgba12, dst, OpenCVTestCase.EPS) + } + + func testSubtractMatMatMat() throws { + Core.subtract(src1: gray128, src2: gray1, dst: dst) + + try assertMatEqual(gray127, dst) + } + + func testSubtractMatMatMatMat() throws { + let mask = makeMask(gray1.clone()) + dst = gray128.clone() + + Core.subtract(src1: gray128, src2: gray1, dst: dst, mask: mask) + + try assertMatEqual(makeMask(gray127, vals: [128]), dst) + } + + func testSubtractMatMatMatMatInt() throws { + Core.subtract(src1: gray3, src2: gray2, dst: dst, mask: gray1, dtype: CvType.CV_32F) + + try assertMatEqual(gray1_32f, dst, OpenCVTestCase.EPS) + } + + func testSumElems() throws { + let src = Mat(rows: 4, cols: 4, type: CvType.CV_8U, scalar: Scalar(10)) + + let res1 = Core.sum(src: src) + + assertScalarEqual(Scalar(160), res1, OpenCVTestCase.EPS) + } + + func testSVBackSubst() throws { + let w = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(2)) + let u = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(4)) + let vt = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(2)) + let rhs = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(1)) + + Core.SVBackSubst(w: w, u: u, vt: vt, rhs: rhs, dst: dst) + + let truth = Mat(rows: 2, cols: 2, type: CvType.CV_32FC1, scalar: Scalar(16)) + try assertMatEqual(truth, dst, OpenCVTestCase.EPS) + } + + func testSVDecompMatMatMatMat() throws { + let src = Mat(rows: 1, cols: 4, type: CvType.CV_32FC1) + try src.put(row: 0, col: 0, data: [1, 4, 8, 6] as [Float]) + let w = Mat() + let u = Mat() + let vt = Mat() + + Core.SVDecomp(src: src, w: w, u: u, vt: vt) + + let truthW = Mat(rows: 1, cols: 1, type: CvType.CV_32FC1, scalar: Scalar(10.816654)) + let truthU = Mat(rows: 1, cols: 1, type: CvType.CV_32FC1, scalar: Scalar(1)) + let truthVT = Mat(rows: 1, cols: 4, type: CvType.CV_32FC1) + try truthVT.put(row: 0, col: 0, data: [0.09245003, 0.36980012, 0.73960024, 0.5547002]) + try assertMatEqual(truthW, w, OpenCVTestCase.EPS) + try assertMatEqual(truthU, u, OpenCVTestCase.EPS) + try assertMatEqual(truthVT, vt, OpenCVTestCase.EPS) + } + + func testSVDecompMatMatMatMatInt() throws { + let src = Mat(rows: 1, cols: 4, type: CvType.CV_32FC1) + try src.put(row: 0, col: 0, data: [1, 4, 8, 6] as [Float]) + let w = Mat() + let u = Mat() + let vt = Mat() + + Core.SVDecomp(src: src, w: w, u: u, vt: vt, flags: Int32(Core.SVD_NO_UV)) + + let truthW = Mat(rows: 1, cols: 1, type: CvType.CV_32FC1, scalar: Scalar(10.816654)) + try assertMatEqual(truthW, w, OpenCVTestCase.EPS) + XCTAssert(u.empty()) + XCTAssert(vt.empty()) + } + + func testTrace() { + let s = Core.trace(mtx: gray1) + + XCTAssertEqual(Scalar(Double(OpenCVTestCase.matSize)), s) + } + + func testTransform() throws { + let src = Mat(rows: 2, cols: 2, type: CvType.CV_32F, scalar: Scalar(55)) + let m = Mat.eye(rows: 2, cols: 2, type: CvType.CV_32FC1) + + Core.transform(src: src, dst: dst, m: m) + + truth = Mat(rows: 2, cols: 2, type: CvType.CV_32FC2, scalar: Scalar(55, 1)) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testTranspose() { + gray0.submat(rowStart: 0, rowEnd: gray0.rows() / 2, colStart: 0, colEnd: gray0.cols()).setTo(scalar: Scalar(1)) + let destination = getMat(CvType.CV_8U, vals: [0]) + + Core.transpose(src: gray0, dst: destination) + + let subdst = destination.submat(rowStart: 0, rowEnd: destination.rows(), colStart: 0, colEnd: destination.cols() / 2) + XCTAssert(subdst.total() == Core.countNonZero(src: subdst)) + } + + func testVconcat() throws { + let mats = [Mat.eye(rows: 3, cols: 3, type: CvType.CV_8U), Mat.zeros(2, cols: 3, type: CvType.CV_8U)] + + Core.vconcat(src: mats, dst: dst) + + try assertMatEqual(Mat.eye(rows: 5, cols: 3, type: CvType.CV_8U), dst) + + } + + func testCopyMakeBorderMatMatIntIntIntIntInt() throws { + let src = Mat(rows: 2, cols: 2, type: CvType.CV_32F, scalar: Scalar(1)) + let border: Int32 = 2 + + Core.copyMakeBorder(src: src, dst: dst, top: border, bottom: border, left: border, right: border, borderType: .BORDER_REPLICATE) + + truth = Mat(rows: 6, cols: 6, type: CvType.CV_32F, scalar: Scalar(1)) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testCopyMakeBorderMatMatIntIntIntIntIntScalar() throws { + let src = Mat(rows: 2, cols: 2, type: CvType.CV_32F, scalar: Scalar(1)) + + let value = Scalar(0) + let border: Int32 = 2 + + Core.copyMakeBorder(src: src, dst: dst, top: border, bottom: border, left: border, right: border, borderType: .BORDER_REPLICATE, value: value) + // TODO_: write better test (use Core.BORDER_CONSTANT) + + truth = Mat(rows: 6, cols: 6, type: CvType.CV_32F, scalar: Scalar(1)) + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testBorderInterpolate() { + let val1 = Core.borderInterpolate(p: 100, len: 150, borderType: .BORDER_REFLECT_101) + XCTAssertEqual(100, val1) + + let val2 = Core.borderInterpolate(p: -5, len: 10, borderType: .BORDER_WRAP) + XCTAssertEqual(5, val2) + } + + func atan2deg(_ y:Double, _ x:Double) -> Double { + var res = atan2(y, x) + if (res < 0) { + res = Double.pi * 2 + res + } + return res * 180 / Double.pi + } + + func atan2rad(_ y:Double, _ x:Double) -> Double { + var res = atan2(y, x) + if (res < 0) { + res = Double.pi * 2 + res + } + return res + } +} diff --git a/modules/core/misc/objc/test/CvTypeTest.swift b/modules/core/misc/objc/test/CvTypeTest.swift new file mode 100644 index 0000000000..28f462f81c --- /dev/null +++ b/modules/core/misc/objc/test/CvTypeTest.swift @@ -0,0 +1,75 @@ +// +// CvTypeTest.swift +// +// Created by Giles Payne on 2020/01/31. +// + +import XCTest +import OpenCV + +class CvTypeTest: OpenCVTestCase { + + func testMakeType() { + XCTAssertEqual(CvType.CV_8UC4, CvType.make(CvType.CV_8U, channels: 4)) + } + + func testCV_8UC() { + XCTAssertEqual(CvType.CV_8UC4, CvType.CV_8UC(4)) + } + + func testCV_8SC() { + XCTAssertEqual(CvType.CV_8SC4, CvType.CV_8SC(4)) + } + + func testCV_16UC() { + XCTAssertEqual(CvType.CV_16UC4, CvType.CV_16UC(4)) + } + + func testCV_16SC() { + XCTAssertEqual(CvType.CV_16SC4, CvType.CV_16SC(4)) + } + + func testCV_32SC() { + XCTAssertEqual(CvType.CV_32SC4, CvType.CV_32SC(4)) + } + + func testCV_32FC() { + XCTAssertEqual(CvType.CV_32FC4, CvType.CV_32FC(4)) + } + + func testCV_64FC() { + XCTAssertEqual(CvType.CV_64FC4, CvType.CV_64FC(4)) + } + + func testCV_16FC() { + XCTAssertEqual(CvType.CV_16FC1, CvType.CV_16FC(1)) + XCTAssertEqual(CvType.CV_16FC2, CvType.CV_16FC(2)) + XCTAssertEqual(CvType.CV_16FC3, CvType.CV_16FC(3)) + XCTAssertEqual(CvType.CV_16FC4, CvType.CV_16FC(4)) + } + + func testChannels() { + XCTAssertEqual(1, CvType.channels(CvType.CV_64F)) + } + + func testDepth() { + XCTAssertEqual(CvType.CV_64F, CvType.depth(CvType.CV_64FC3)) + } + + func testIsInteger() { + XCTAssertFalse(CvType.isInteger(CvType.CV_32FC3)); + XCTAssert(CvType.isInteger(CvType.CV_16S)); + } + + func testELEM_SIZE() { + XCTAssertEqual(3 * 8, CvType.elemSize(CvType.CV_64FC3)); + XCTAssertEqual(3 * 2, CvType.elemSize(CvType.CV_16FC3)); + } + + func testTypeToString() { + XCTAssertEqual("CV_32FC1", CvType.type(toString: CvType.CV_32F)); + XCTAssertEqual("CV_32FC3", CvType.type(toString: CvType.CV_32FC3)); + XCTAssertEqual("CV_32FC(128)", CvType.type(toString: CvType.CV_32FC(128))); + } + +} diff --git a/modules/core/misc/objc/test/DMatchTest.swift b/modules/core/misc/objc/test/DMatchTest.swift new file mode 100644 index 0000000000..82419271d8 --- /dev/null +++ b/modules/core/misc/objc/test/DMatchTest.swift @@ -0,0 +1,44 @@ +// +// DMatchTest.swift +// +// Created by Giles Payne on 2020/01/31. +// + +import XCTest +import OpenCV + +class DMatchTest: OpenCVTestCase { + + func testDMatchIntIntFloat() { + let dm1 = DMatch(queryIdx: 1, trainIdx: 4, distance: 4.0) + + XCTAssertEqual(1, dm1.queryIdx) + XCTAssertEqual(4, dm1.trainIdx) + XCTAssertEqual(4.0, dm1.distance) + } + + func testDMatchIntIntIntFloat() { + let dm2 = DMatch(queryIdx: 2, trainIdx: 6, imgIdx: -1, distance: 8.0) + + XCTAssertEqual(2, dm2.queryIdx) + XCTAssertEqual(6, dm2.trainIdx) + XCTAssertEqual(-1, dm2.imgIdx) + XCTAssertEqual(8.0, dm2.distance) + } + + func testLessThan() { + let dm1 = DMatch(queryIdx: 1, trainIdx: 4, distance: 4.0) + let dm2 = DMatch(queryIdx: 2, trainIdx: 6, imgIdx: -1, distance: 8.0) + XCTAssert(dm1.lessThan(dm2)) + } + + func testToString() { + let dm2 = DMatch(queryIdx: 2, trainIdx: 6, imgIdx: -1, distance: 8.0) + + let actual = "\(dm2)" + + let expected = "DMatch { queryIdx: 2, trainIdx: 6, imgIdx: -1, distance: 8.000000}" + XCTAssertEqual(expected, actual) + } + +} diff --git a/modules/core/misc/objc/test/KeyPointTest.swift b/modules/core/misc/objc/test/KeyPointTest.swift new file mode 100644 index 0000000000..30490f5057 --- /dev/null +++ b/modules/core/misc/objc/test/KeyPointTest.swift @@ -0,0 +1,59 @@ +// +// KeyPointTest.swift +// +// Created by Giles Payne on 2020/01/31. +// + +import XCTest +import OpenCV + +class KeyPointTest: OpenCVTestCase { + + let angle:Float = 30 + let classId:Int32 = 1 + let octave:Int32 = 1 + let response:Float = 2.0 + let size:Float = 3.0 + let x:Float = 1.0 + let y:Float = 2.0 + + func testKeyPoint() { + let keyPoint = KeyPoint() + assertPoint2fEquals(Point2f(x: 0, y: 0), keyPoint.pt, OpenCVTestCase.FEPS) + } + + func testKeyPointFloatFloatFloat() { + let keyPoint = KeyPoint(x: x, y: y, size: size) + assertPoint2fEquals(Point2f(x: 1, y: 2), keyPoint.pt, OpenCVTestCase.FEPS) + } + + func testKeyPointFloatFloatFloatFloat() { + let keyPoint = KeyPoint(x: x, y: y, size: size, angle: 10.0) + XCTAssertEqual(10.0, keyPoint.angle); + } + + func testKeyPointFloatFloatFloatFloatFloat() { + let keyPoint = KeyPoint(x: x, y: y, size: size, angle: 1.0, response: 1.0) + XCTAssertEqual(1.0, keyPoint.response) + } + + func testKeyPointFloatFloatFloatFloatFloatInt() { + let keyPoint = KeyPoint(x: x, y: y, size: size, angle: 1.0, response: 1.0, octave: 1) + XCTAssertEqual(1, keyPoint.octave) + } + + func testKeyPointFloatFloatFloatFloatFloatIntInt() { + let keyPoint = KeyPoint(x: x, y: y, size: size, angle: 1.0, response: 1.0, octave: 1, classId: 1) + XCTAssertEqual(1, keyPoint.classId) + } + + func testToString() { + let keyPoint = KeyPoint(x: x, y: y, size: size, angle: angle, response: response, octave: octave, classId: classId) + + let actual = "\(keyPoint)" + + let expected = "KeyPoint { pt: Point2f {1.000000,2.000000}, size: 3.000000, angle: 30.000000, response: 2.000000, octave: 1, classId: 1}" + XCTAssertEqual(expected, actual) + } + +} diff --git a/modules/core/misc/objc/test/MatTest.swift b/modules/core/misc/objc/test/MatTest.swift new file mode 100644 index 0000000000..af26eb0bdb --- /dev/null +++ b/modules/core/misc/objc/test/MatTest.swift @@ -0,0 +1,1146 @@ +// +// StitchAppTests.swift +// +// Created by Giles Payne on 2020/01/19. +// + +import XCTest +import OpenCV + +class MatTests: OpenCVTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testAdjustROI() throws { + let roi = gray0.submat(rowStart: 3, rowEnd: 5, colStart: 7, colEnd: 10) + let originalroi = roi.clone() + let adjusted = roi.adjustRoi(top: 2, bottom: 2, left: 2, right: 2) + try assertMatEqual(adjusted, roi) + assertSizeEquals(Size(width: 5, height: 6), adjusted.size()) + XCTAssertEqual(originalroi.type(), adjusted.type()) + XCTAssertTrue(adjusted.isSubmatrix()) + XCTAssertFalse(adjusted.isContinuous()) + + let offset = Point() + let size = Size() + adjusted.locateROI(wholeSize: size, offset: offset) + assertPointEquals(Point(x: 5, y: 1), offset); + assertSizeEquals(gray0.size(), size); + } + + func testAssignToMat() throws { + gray0.assign(to: dst) + try assertMatEqual(gray0, dst) + gray255.assign(to: dst) + try assertMatEqual(gray255, dst) + } + + func testAssignToMatInt() throws { + gray255.assign(to: dst, type: CvType.CV_32F) + try assertMatEqual(gray255_32f, dst, OpenCVTestCase.EPS) + } + + func testChannels() { + XCTAssertEqual(1, gray0.channels()) + XCTAssertEqual(3, rgbLena.channels()) + XCTAssertEqual(4, rgba0.channels()) + } + + func testCheckVectorInt() { + // ! returns N if the matrix is 1-channel (N x ptdim) or ptdim-channel + // (1 x N) or (N x 1); negative number otherwise + XCTAssertEqual(2, Mat(rows: 2, cols: 10, type: CvType.CV_8U).checkVector(elemChannels: 10)) + XCTAssertEqual(2, Mat(rows: 1, cols: 2, type: CvType.CV_8UC(10)).checkVector(elemChannels: 10)) + XCTAssertEqual(2, Mat(rows: 2, cols: 1, type: CvType.CV_8UC(10)).checkVector(elemChannels: 10)) + XCTAssertEqual(10, Mat(rows: 1, cols: 10, type: CvType.CV_8UC2).checkVector(elemChannels: 2)) + + XCTAssert(0 > Mat().checkVector(elemChannels: 0)) + XCTAssert(0 > Mat(rows: 10, cols: 1, type: CvType.CV_8U).checkVector(elemChannels: 10)) + XCTAssert(0 > Mat(rows: 10, cols: 20, type: CvType.CV_8U).checkVector(elemChannels: 10)) + } + + func testCheckVectorIntInt() { + XCTAssertEqual(2, Mat(rows: 2, cols: 10, type: CvType.CV_8U).checkVector(elemChannels: 10, depth: CvType.CV_8U)) + XCTAssertEqual(2, Mat(rows: 1, cols: 2, type: CvType.CV_8UC(10)).checkVector(elemChannels: 10, depth: CvType.CV_8U)) + XCTAssertEqual(2, Mat(rows: 2, cols: 1, type: CvType.CV_8UC(10)).checkVector(elemChannels: 10, depth: CvType.CV_8U)) + XCTAssertEqual(10, Mat(rows: 1, cols: 10, type: CvType.CV_8UC2).checkVector(elemChannels: 2, depth: CvType.CV_8U)) + + XCTAssert(0 > Mat(rows: 2, cols: 10, type: CvType.CV_8U).checkVector(elemChannels: 10, depth: CvType.CV_8S)); + XCTAssert(0 > Mat(rows: 1, cols: 2, type: CvType.CV_8UC(10)).checkVector(elemChannels: 10, depth: CvType.CV_8S)); + XCTAssert(0 > Mat(rows: 2, cols: 1, type: CvType.CV_8UC(10)).checkVector(elemChannels: 10, depth: CvType.CV_8S)); + XCTAssert(0 > Mat(rows: 1, cols: 10, type: CvType.CV_8UC2).checkVector(elemChannels: 10, depth: CvType.CV_8S)); + } + + func testCheckVectorIntIntBoolean() { + let mm = Mat(rows: 5, cols: 1, type: CvType.CV_8UC(10)) + let roi = Mat(rows: 5, cols: 3, type: CvType.CV_8UC(10)).submat(rowStart: 1, rowEnd: 3, colStart: 2, colEnd: 3); + + XCTAssertEqual(5, mm.checkVector(elemChannels: 10, depth: CvType.CV_8U, requireContinuous: true)); + XCTAssertEqual(5, mm.checkVector(elemChannels: 10, depth: CvType.CV_8U, requireContinuous: false)); + XCTAssertEqual(2, roi.checkVector(elemChannels: 10, depth: CvType.CV_8U, requireContinuous: false)); + XCTAssert(0 > roi.checkVector(elemChannels: 10, depth: CvType.CV_8U, requireContinuous: true)); + } + + func testClone() throws { + dst = gray0.clone() + try assertMatEqual(gray0, dst) + XCTAssertFalse(dst.isSameMat(gray0)) + } + + func testCol() { + let col = gray0.col(0) + XCTAssertEqual(1, col.cols()) + XCTAssertEqual(gray0.rows(), col.rows()) + } + + func testColRangeIntInt() { + let cols = gray0.colRange(start: 0, end: gray0.cols() / 2) + XCTAssertEqual(gray0.cols() / 2, cols.cols()) + XCTAssertEqual(gray0.rows(), cols.rows()) + } + + func testColRangeRange() throws { + let range = Range(start: 0, end: 5) + dst = gray0.colRange(range) + + truth = Mat(rows: 10, cols: 5, type: CvType.CV_8UC1, scalar: Scalar(0.0)) + try assertMatEqual(truth!, dst) + } + + func testCols() { + XCTAssertEqual(OpenCVTestCase.matSize, gray0.cols()) + } + + func testConvertToMatInt() throws { + gray255.convert(to: dst, rtype: CvType.CV_32F) + + truth = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_32F, scalar: Scalar(255)); + try assertMatEqual(truth!, dst, OpenCVTestCase.EPS) + } + + func testConvertToMatIntDouble() throws { + gray2.convert(to: dst, rtype: CvType.CV_16U, alpha: 2.0) + + truth = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_16U, scalar: Scalar(4)) + try assertMatEqual(truth!, dst) + } + + func testConvertToMatIntDoubleDouble() throws { + gray0_32f.convert(to: dst, rtype: CvType.CV_8U, alpha: 2.0, beta: 4.0) + + truth = Mat(rows: OpenCVTestCase.matSize, cols: OpenCVTestCase.matSize, type: CvType.CV_8U, scalar: Scalar(4)) + try assertMatEqual(truth!, dst) + } + + func testCopyToMat() throws { + rgbLena.copy(to:dst) + try assertMatEqual(rgbLena, dst) + } + + func testCopyToMatMat() throws { + let src = Mat(rows: 4, cols: 4, type: CvType.CV_8U, scalar: Scalar(5)) + let mask = makeMask(src.clone()) + + src.copy(to: dst, mask: mask) + + truth = Mat(rows: 4, cols: 4, type: CvType.CV_8U) + try XCTAssertEqual(truth!.put(row: 0, col: 0, data: [0, 0, 5, 5] as [Int8]), 4) + try XCTAssertEqual(truth!.put(row: 1, col: 0, data: [0, 0, 5, 5] as [Int8]), 4) + try XCTAssertEqual(truth!.put(row: 2, col: 0, data: [0, 0, 5, 5] as [Int8]), 4) + try XCTAssertEqual(truth!.put(row: 3, col: 0, data: [0, 0, 5, 5] as [Int8]), 4) + try assertMatEqual(truth!, dst) + } + + func testCreateIntIntInt() { + gray255.create(rows: 4, cols: 5, type: CvType.CV_32F) + + XCTAssertEqual(4, gray255.rows()) + XCTAssertEqual(5, gray255.cols()) + XCTAssertEqual(CvType.CV_32F, gray255.type()) + } + + func testCreateSizeInt() { + let size = Size(width: 5, height: 5) + dst.create(size: size, type: CvType.CV_16U) + + XCTAssertEqual(5, dst.rows()) + XCTAssertEqual(5, dst.cols()) + XCTAssertEqual(CvType.CV_16U, dst.type()) + } + + func testCreateIntArrayInt() { + dst.create(sizes:[5, 6, 7], type:CvType.CV_16U) + + XCTAssertEqual(5, dst.size(0)) + XCTAssertEqual(6, dst.size(1)) + XCTAssertEqual(7, dst.size(2)) + XCTAssertEqual(CvType.CV_16U, dst.type()) + } + + func testCross() throws { + let answer = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + try XCTAssertEqual(answer.put(row: 0, col: 0, data: [7.0, 1.0, -5.0] as [Float]), 12) + + let cross = v1.cross(v2) + try assertMatEqual(answer, cross, OpenCVTestCase.EPS) + } + + func testDepth() { + XCTAssertEqual(CvType.CV_8U, gray0.depth()) + XCTAssertEqual(CvType.CV_32F, gray0_32f.depth()) + } + + func testDiag() throws { + dst = gray0.diag() + truth = Mat(rows: 10, cols: 1, type: CvType.CV_8UC1, scalar: Scalar(0)) + try assertMatEqual(truth!, dst) + } + + func testDiagInt() throws { + dst = gray255.diag(2) + truth = Mat(rows: 8, cols: 1, type: CvType.CV_8UC1, scalar: Scalar(255)) + try assertMatEqual(truth!, dst) + } + + func testDiagMat() throws { + let diagVector = Mat(rows: OpenCVTestCase.matSize, cols: 1, type: CvType.CV_32F, scalar: Scalar(1)) + dst = Mat.diag(diagVector) + try assertMatEqual(grayE_32f, dst, OpenCVTestCase.EPS); + } + + func testDot() { + let s = v1.dot(v2) + XCTAssertEqual(11.0, s) + } + + func testDump() { + XCTAssertEqual("[1, 3, 2]", v1.dump()) + } + + func testElemSize() { + XCTAssertEqual(MemoryLayout.size * Int(gray0.channels()), gray0.elemSize()) + XCTAssertEqual(MemoryLayout.size * Int(gray0_32f.channels()), gray0_32f.elemSize()) + XCTAssertEqual(MemoryLayout.size * Int(rgbLena.channels()), rgbLena.elemSize()) + } + + func testElemSize1() { + XCTAssertEqual(MemoryLayout.size, gray255.elemSize1()) + XCTAssertEqual(MemoryLayout.size, gray0_64f.elemSize1()) + XCTAssertEqual(MemoryLayout.size, rgbLena.elemSize1()) + } + + func testEmpty() { + XCTAssert(dst.empty()) + XCTAssertFalse(gray0.empty()) + } + + func testEyeIntIntInt() throws { + let eye = Mat.eye(rows: 3, cols: 3, type: CvType.CV_32FC1) + try assertMatEqual(eye, eye.inv(), OpenCVTestCase.EPS) + } + + func testEyeSizeInt() { + let size = Size(width: 5, height: 5) + let eye = Mat.eye(size: size, type: CvType.CV_32S) + XCTAssertEqual(5, Core.countNonZero(src: eye)) + } + + func getTestMat(size:Int32, type:Int32) throws -> Mat { + let ret = Mat(rows: size, cols: size, type: type) + let ch = CvType.channels(type) + var buff:[Double] = [] + for i: Int32 in (0.. 4D + let src = Mat(rows: 6, cols: 5, type: CvType.CV_8UC3, scalar: Scalar(0)) + XCTAssertEqual(2, src.dims()) + XCTAssertEqual(src.rows(), src.size(0)) + XCTAssertEqual(src.cols(), src.size(1)) + + let newShape = [1, src.channels() * src.cols(), 1, src.rows()] + dst = src.reshape(channels: 1, newshape: newShape as [NSNumber]) + XCTAssertEqual(newShape.count, Int(dst.dims())) + for i in 0.. 2D + let src2 = Mat(sizes: [4, 6, 7], type: CvType.CV_8UC3, scalar: Scalar(0)) + XCTAssertEqual(3, src2.dims()) + XCTAssertEqual(4, src2.size(0)) + XCTAssertEqual(6, src2.size(1)) + XCTAssertEqual(7, src2.size(2)) + + let newShape2 = [src2.channels() * src2.size(2), src2.size(0) * src2.size(1)] + dst = src2.reshape(channels: 1, newshape: newShape2 as [NSNumber]) + XCTAssertEqual(newShape2.count, Int(dst.dims())) + for i in 0..= ceil(center.x + halfDiagonal)) && (r.br().y >= ceil(center.y + halfDiagonal))) + + XCTAssert((r.br().x - ceil(center.x + halfDiagonal)) <= 1 && (r.br().y - ceil(center.y + halfDiagonal)) <= 1) + } + + func testClone() { + let rrect = RotatedRect(center: center, size: size, angle: angle) + let clone = rrect.clone(); + + XCTAssertNotNil(clone) + XCTAssert(rrect.center == clone.center) + XCTAssert(rrect.size == clone.size) + XCTAssert(rrect.angle == clone.angle) + } + + func testEqualsObject() { + let center2 = Point2f(x: Float(OpenCVTestCase.matSize / 3), y: Float(OpenCVTestCase.matSize) / 1.5) + let size2 = Size2f(width: Float(OpenCVTestCase.matSize / 2), height: Float(OpenCVTestCase.matSize / 4)) + let angle2:Double = 0 + + let rrect1 = RotatedRect(center: center, size: size, angle: angle) + let rrect2 = RotatedRect(center: center2, size: size2, angle: angle2) + let rrect3 = rrect1 + let clone1 = rrect1.clone() + let clone2 = rrect2.clone() + + XCTAssert(rrect1 == rrect3) + XCTAssertFalse(rrect1 == rrect2) + + XCTAssert(rrect2 == clone2) + clone2.angle = 10 + XCTAssertFalse(rrect2 == clone2) + + XCTAssert(rrect1 == clone1) + + clone1.center.x += 1 + XCTAssertFalse(rrect1 == clone1) + + clone1.center.x -= 1 + XCTAssert(rrect1 == clone1) + + clone1.size.width += 1 + XCTAssertFalse(rrect1 == clone1) + + XCTAssertFalse(rrect1 == size) + } + + func testHashCode() { + let rr = RotatedRect(center: center, size: size, angle: angle) + XCTAssertEqual(rr.hash(), rr.hash()) + } + + func testPoints() { + let rrect = RotatedRect(center: center, size: size, angle: angle); + + let p = rrect.points() + + let is_p0_irrational = (100 * p[0].x != round(100 * p[0].x)) && (100 * p[0].y != round(100 * p[0].y)) + let is_p1_irrational = (100 * p[1].x != round(100 * p[1].x)) && (100 * p[1].y != round(100 * p[1].y)); + let is_p2_irrational = (100 * p[2].x != round(100 * p[2].x)) && (100 * p[2].y != round(100 * p[2].y)); + let is_p3_irrational = (100 * p[3].x != round(100 * p[3].x)) && (100 * p[3].y != round(100 * p[3].y)); + + XCTAssert(is_p0_irrational && is_p1_irrational && is_p2_irrational && is_p3_irrational) + + XCTAssert(abs((p[0].x + p[2].x) / 2 - center.x) + abs((p[0].y + p[2].y) / 2 - center.y) < OpenCVTestCase.FEPS, "Symmetric points 0 and 2") + + XCTAssert(abs((p[1].x + p[3].x) / 2 - center.x) + abs((p[1].y + p[3].y) / 2 - center.y) < OpenCVTestCase.FEPS, "Symmetric points 1 and 3") + + XCTAssert(abs((p[1].x - p[0].x) * (p[2].x - p[1].x) + + (p[1].y - p[0].y) * (p[2].y - p[1].y)) < OpenCVTestCase.FEPS, "Orthogonal vectors 01 and 12") + + XCTAssert(abs((p[2].x - p[1].x) * (p[3].x - p[2].x) + + (p[2].y - p[1].y) * (p[3].y - p[2].y)) < OpenCVTestCase.FEPS, "Orthogonal vectors 12 and 23"); + + XCTAssert(abs((p[3].x - p[2].x) * (p[0].x - p[3].x) + + (p[3].y - p[2].y) * (p[0].y - p[3].y)) < OpenCVTestCase.FEPS, "Orthogonal vectors 23 and 30") + + XCTAssert(abs((p[0].x - p[3].x) * (p[1].x - p[0].x) + + (p[0].y - p[3].y) * (p[1].y - p[0].y)) < OpenCVTestCase.FEPS, "Orthogonal vectors 30 and 01") + + XCTAssert(abs((p[1].x - p[0].x) * (p[1].x - p[0].x) + + (p[1].y - p[0].y) * (p[1].y - p[0].y) - size.height * size.height) < OpenCVTestCase.FEPS, "Length of the vector 01") + + XCTAssert(abs((p[1].x - p[2].x) * (p[1].x - p[2].x) + + (p[1].y - p[2].y) * (p[1].y - p[2].y) - size.width * size.width) < OpenCVTestCase.FEPS, "Length of the vector 21") + + XCTAssert(abs((p[2].x - p[1].x) / size.width - Float(cos(angle * Double.pi / 180))) < OpenCVTestCase.FEPS, "Angle of the vector 21 with the axes"); + } + + func testRotatedRect() { + let rr = RotatedRect() + + XCTAssertNotNil(rr) + XCTAssertNotNil(rr.center) + XCTAssertNotNil(rr.size) + XCTAssertEqual(0.0, rr.angle) + } + + func testRotatedRectDoubleArray() { + let vals = [1.5, 2.6, 3.7, 4.2, 5.1] + let rr = RotatedRect(vals: vals as [NSNumber]) + + XCTAssertNotNil(rr) + XCTAssertEqual(1.5, rr.center.x) + XCTAssertEqual(2.6, rr.center.y) + XCTAssertEqual(3.7, rr.size.width) + XCTAssertEqual(4.2, rr.size.height) + XCTAssertEqual(5.1, rr.angle) + } + + func testRotatedRectPointSizeDouble() { + let rr = RotatedRect(center: center, size: size, angle: 40); + + XCTAssertNotNil(rr) + XCTAssertNotNil(rr.center) + XCTAssertNotNil(rr.size) + XCTAssertEqual(40.0, rr.angle); + } + + func testSet() { + let vals1: [Double] = [] + let r1 = RotatedRect(center: center, size: size, angle: 40); + + r1.set(vals: vals1 as [NSNumber]) + + XCTAssertEqual(0, r1.angle) + assertPoint2fEquals(Point2f(x: 0, y: 0), r1.center, OpenCVTestCase.FEPS) + assertSize2fEquals(Size2f(width: 0, height: 0), r1.size, OpenCVTestCase.FEPS) + + let vals2 = [1, 2, 3, 4, 5] + let r2 = RotatedRect(center: center, size: size, angle: 40) + + r2.set(vals: vals2 as [NSNumber]) + + XCTAssertEqual(5, r2.angle) + assertPoint2fEquals(Point2f(x: 1, y: 2), r2.center, OpenCVTestCase.FEPS) + assertSize2fEquals(Size2f(width: 3, height: 4), r2.size, OpenCVTestCase.FEPS) + } + + func testToString() { + let actual = "\(RotatedRect(center: Point2f(x:1, y:2), size: Size2f(width:10, height:12), angle:4.5))" + let expected = "RotatedRect {Point2f {1.000000,2.000000},Size2f {10.000000,12.000000},4.500000}" + XCTAssertEqual(expected, actual); + } + +} diff --git a/modules/core/misc/objc/test/ScalarTest.swift b/modules/core/misc/objc/test/ScalarTest.swift new file mode 100644 index 0000000000..72495dbc76 --- /dev/null +++ b/modules/core/misc/objc/test/ScalarTest.swift @@ -0,0 +1,98 @@ +// +// ScalarTest.swift +// +// Created by Giles Payne on 2020/01/31. +// + +import XCTest +import OpenCV + +class ScalarTest: OpenCVTestCase { + + let s1 = Scalar(1.0) + let s2 = Scalar.all(1.0) + + func testAll() { + let dstScalar = Scalar.all(2.0) + let truth = Scalar(2.0, 2.0, 2.0, 2.0) + XCTAssertEqual(truth, dstScalar) + } + + func testClone() { + let dstScalar = s2.clone() + XCTAssertEqual(s2, dstScalar) + } + + func testConj() { + let dstScalar = s2.conj() + let truth = Scalar(1, -1, -1, -1) + XCTAssertEqual(truth, dstScalar) + } + + func testEqualsObject() { + let dstScalar = s2.clone() + XCTAssert(s2 == dstScalar) + + XCTAssertFalse(s2 == s1) + } + + func testHashCode() { + XCTAssertEqual(s2.hash(), s2.hash()) + } + + func testIsReal() { + XCTAssert(s1.isReal()) + + XCTAssertFalse(s2.isReal()) + } + + func testMulScalar() { + let dstScalar = s2.mul(s1) + XCTAssertEqual(s1, dstScalar) + } + + func testMulScalarDouble() { + let multiplier = 2.0 + let dstScalar = s2.mul(s1, scale: multiplier) + let truth = Scalar(2) + XCTAssertEqual(truth, dstScalar) + } + + func testScalarDouble() { + let truth = Scalar(1) + XCTAssertEqual(truth, s1) + } + + func testScalarDoubleArray() { + let vals: [Double] = [2.0, 4.0, 5.0, 3.0] + let dstScalar = Scalar(vals:vals as [NSNumber]) + + let truth = Scalar(2.0, 4.0, 5.0, 3.0) + XCTAssertEqual(truth, dstScalar) + } + + func testScalarDoubleDouble() { + let dstScalar = Scalar(2, 5) + let truth = Scalar(2.0, 5.0, 0.0, 0.0) + XCTAssertEqual(truth, dstScalar) + } + + func testScalarDoubleDoubleDouble() { + let dstScalar = Scalar(2.0, 5.0, 5.0) + let truth = Scalar(2.0, 5.0, 5.0, 0.0) + XCTAssertEqual(truth, dstScalar); + } + + func testScalarDoubleDoubleDoubleDouble() { + let dstScalar = Scalar(2.0, 5.0, 5.0, 9.0) + let truth = Scalar(2.0, 5.0, 5.0, 9.0) + XCTAssertEqual(truth, dstScalar) + } + + func testToString() { + let actual = "\(s2)" + let expected = "Scalar [1.000000, 1.000000, 1.000000, 1.000000]" + XCTAssertEqual(expected, actual) + } + +} diff --git a/modules/core/misc/objc/test/SizeTest.swift b/modules/core/misc/objc/test/SizeTest.swift new file mode 100644 index 0000000000..cd8bcd1448 --- /dev/null +++ b/modules/core/misc/objc/test/SizeTest.swift @@ -0,0 +1,86 @@ +// +// SizeTest.swift +// +// Created by Giles Payne on 2020/01/31. +// + +import XCTest +import OpenCV + +class SizeTest: OpenCVTestCase { + + let sz1 = Size2d(width: 10.0, height: 10.0) + let sz2 = Size2d(width: -1, height: -1) + + func testArea() { + let area = sz1.area() + XCTAssertEqual(100.0, area); + } + + func testClone() { + let dstSize = sz1.clone() + XCTAssertEqual(sz1, dstSize) + } + + func testEqualsObject() { + XCTAssertFalse(sz1 == sz2); + + let sz2 = sz1.clone(); + XCTAssertTrue(sz1 == sz2); + } + + func testHashCode() { + XCTAssertEqual(sz1.hash(), sz1.hash()); + } + + func testSet() { + let vals1:[Double] = [] + sz2.set(vals: vals1 as [NSNumber]) + XCTAssertEqual(0, sz2.width); + XCTAssertEqual(0, sz2.height); + + let vals2:[Double] = [9, 12] + sz1.set(vals: vals2 as [NSNumber]); + XCTAssertEqual(9, sz1.width); + XCTAssertEqual(12, sz1.height); + } + + func testSize() { + let dstSize = Size2d() + + XCTAssertNotNil(dstSize) + XCTAssertEqual(0, dstSize.width) + XCTAssertEqual(0, dstSize.height) + } + + func testSizeDoubleArray() { + let vals:[Double] = [10, 20] + let sz2 = Size2d(vals: vals as [NSNumber]) + + XCTAssertEqual(10, sz2.width) + XCTAssertEqual(20, sz2.height) + } + + func testSizeDoubleDouble() { + XCTAssertNotNil(sz1) + + XCTAssertEqual(10.0, sz1.width) + XCTAssertEqual(10.0, sz1.height) + } + + func testSizePoint() { + let p = Point2d(x: 2, y: 4) + let sz1 = Size2d(point: p) + + XCTAssertNotNil(sz1) + XCTAssertEqual(2.0, sz1.width) + XCTAssertEqual(4.0, sz1.height) + } + + func testToString() { + let actual = "\(sz1)" + let expected = "Size2d {10.000000,10.000000}" + XCTAssertEqual(expected, actual); + } + +} diff --git a/modules/core/misc/objc/test/TermCriteriaTest.swift b/modules/core/misc/objc/test/TermCriteriaTest.swift new file mode 100644 index 0000000000..25a2e9933b --- /dev/null +++ b/modules/core/misc/objc/test/TermCriteriaTest.swift @@ -0,0 +1,82 @@ +// +// TermCriteriaTest.swift +// +// Created by Giles Payne on 2020/01/31. +// + +import XCTest +import OpenCV + +class TermCriteriaTest: OpenCVTestCase { + + let tc2 = TermCriteria(type: 2, maxCount: 4, epsilon: EPS) + + func testClone() { + let tc1 = tc2.clone() + XCTAssertEqual(tc2, tc1) + } + + func testEqualsObject() { + var tc1 = TermCriteria() + XCTAssertFalse(tc2 == tc1) + + tc1 = tc2.clone() + XCTAssert(tc2 == tc1) + } + + func testHashCode() { + XCTAssertEqual(tc2.hash(), tc2.hash()) + } + + func testSet() { + let tc1 = TermCriteria() + let vals1:[Double] = [] + + tc1.set(vals: vals1 as [NSNumber]) + + XCTAssertEqual(0, tc1.type) + XCTAssertEqual(0, tc1.maxCount) + XCTAssertEqual(0.0, tc1.epsilon) + + let vals2 = [9, 8, 0.002] + tc2.set(vals: vals2 as [NSNumber]) + + XCTAssertEqual(9, tc2.type) + XCTAssertEqual(8, tc2.maxCount) + XCTAssertEqual(0.002, tc2.epsilon) + } + + func testTermCriteria() { + let tc1 = TermCriteria() + + XCTAssertNotNil(tc1) + XCTAssertEqual(0, tc1.type) + XCTAssertEqual(0, tc1.maxCount) + XCTAssertEqual(0.0, tc1.epsilon) + } + + func testTermCriteriaDoubleArray() { + let vals = [ 3, 2, 0.007] + let tc1 = TermCriteria(vals: vals as [NSNumber]) + + XCTAssertEqual(3, tc1.type) + XCTAssertEqual(2, tc1.maxCount) + XCTAssertEqual(0.007, tc1.epsilon) + } + + func testTermCriteriaIntIntDouble() { + let tc1 = TermCriteria(type: 2, maxCount: 4, epsilon: OpenCVTestCase.EPS) + + XCTAssertNotNil(tc1) + XCTAssertEqual(2, tc1.type) + XCTAssertEqual(4, tc1.maxCount) + XCTAssertEqual(OpenCVTestCase.EPS, tc1.epsilon) + } + + func testToString() { + let actual = "\(tc2)" + let expected = "TermCriteria { type: 2, maxCount: 4, epsilon: 0.001000}" + XCTAssertEqual(expected, actual) + } + +} diff --git a/modules/core/misc/objc/test/resources/chessboard.jpg b/modules/core/misc/objc/test/resources/chessboard.jpg new file mode 100644 index 0000000000..6ade32006e Binary files /dev/null and b/modules/core/misc/objc/test/resources/chessboard.jpg differ diff --git a/modules/core/misc/objc/test/resources/lena.png b/modules/core/misc/objc/test/resources/lena.png new file mode 100644 index 0000000000..3e8668734a Binary files /dev/null and b/modules/core/misc/objc/test/resources/lena.png differ diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index 67ace67ff5..e8c23f6a44 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -10,7 +10,7 @@ set(the_description "Deep neural network module. It allows to load models from d ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX) -ocv_add_module(dnn opencv_core opencv_imgproc WRAP python java js) +ocv_add_module(dnn opencv_core opencv_imgproc WRAP python java objc js) ocv_option(OPENCV_DNN_OPENCL "Build with OpenCL support" HAVE_OPENCL AND NOT APPLE) if(HAVE_TENGINE) diff --git a/modules/dnn/misc/objc/gen_dict.json b/modules/dnn/misc/objc/gen_dict.json new file mode 100644 index 0000000000..15dee3fde4 --- /dev/null +++ b/modules/dnn/misc/objc/gen_dict.json @@ -0,0 +1,41 @@ +{ + "func_arg_fix" : { + "(Net*)readNetFromCaffe:(NSString*)prototxt caffeModel:(NSString*)caffeModel" : { "readNetFromCaffe" : {"name" : "readNetFromCaffeFile"} }, + "(Net*)readNetFromCaffe:(ByteVector*)bufferProto bufferModel:(ByteVector*)bufferModel" : { "readNetFromCaffe" : {"name" : "readNetFromCaffeBuffer"} }, + "(Net*)readNetFromDarknet:(NSString*)cfgFile darknetModel:(NSString*)darknetModel" : { "readNetFromDarknet" : {"name" : "readNetFromDarknetFile"} }, + "(Net*)readNetFromDarknet:(ByteVector*)bufferCfg bufferModel:(ByteVector*)bufferModel" : { "readNetFromDarknet" : {"name" : "readNetFromDarknetBuffer"} }, + "(Net*)readNetFromONNX:(NSString*)onnxFile" : { "readNetFromONNX" : {"name" : "readNetFromONNXFile"} }, + "(Net*)readNetFromONNX:(ByteVector*)buffer" : { "readNetFromONNX" : {"name" : "readNetFromONNXBuffer"} }, + "(Net*)readNetFromTensorflow:(NSString*)model config:(NSString*)config" : { "readNetFromTensorflow" : {"name" : "readNetFromTensorflowFile"} }, + "(Net*)readNetFromTensorflow:(ByteVector*)bufferModel bufferConfig:(ByteVector*)bufferConfig" : { "readNetFromTensorflow" : {"name" : "readNetFromTensorflowBuffer"} }, + "(void)forward:(NSMutableArray*)outputBlobs outputName:(NSString*)outputName" : { "forward" : {"name" : "forwardOutputBlobs"} }, + "(void)forward:(NSMutableArray*)outputBlobs outBlobNames:(NSArray*)outBlobNames" : { "forward" : {"name" : "forwardOutputBlobs"} }, + "(long)getFLOPS:(IntVector*)netInputShape" : { "getFLOPS" : {"name" : "getFLOPSWithNetInputShape"} }, + "(long)getFLOPS:(NSArray*)netInputShapes" : { "getFLOPS" : {"name" : "getFLOPSWithNetInputShapes"} }, + "(long)getFLOPS:(int)layerId netInputShape:(IntVector*)netInputShape" : { "getFLOPS" : {"name" : "getFLOPSWithLayerId"} }, + "(long)getFLOPS:(int)layerId netInputShapes:(NSArray*)netInputShapes" : { "getFLOPS" : {"name" : "getFLOPSWithLayerId"} }, + "(void)getLayersShapes:(IntVector*)netInputShape layersIds:(IntVector*)layersIds inLayersShapes:(NSMutableArray*>*)inLayersShapes outLayersShapes:(NSMutableArray*>*)outLayersShapes" : { "getLayersShapes" : {"name" : "getLayersShapesWithNetInputShape"} }, + "(void)getLayersShapes:(NSArray*)netInputShapes layersIds:(IntVector*)layersIds inLayersShapes:(NSMutableArray*>*)inLayersShapes outLayersShapes:(NSMutableArray*>*)outLayersShapes" : { "getLayersShapes" : {"name" : "getLayersShapesWithNetInputShapes"} } + }, + "type_dict": { + "MatShape": { + "objc_type": "IntVector*", + "to_cpp": "%(n)s.nativeRef", + "from_cpp": "[IntVector fromNative:%(n)s]", + "cast_to": "std::vector" + }, + "vector_MatShape": { + "objc_type": "IntVector*", + "v_type": "IntVector" + }, + "vector_vector_MatShape": { + "objc_type": "IntVector*", + "v_v_type": "IntVector" + }, + "LayerId": { + "objc_type": "DictValue*", + "to_cpp": "*(cv::dnn::DictValue*)(%(n)s.nativePtr)", + "from_cpp": "[DictValue fromNative:%(n)s]" + } + } +} diff --git a/modules/features2d/CMakeLists.txt b/modules/features2d/CMakeLists.txt index 1d29320a14..a586d4606e 100644 --- a/modules/features2d/CMakeLists.txt +++ b/modules/features2d/CMakeLists.txt @@ -6,4 +6,4 @@ set(debug_modules "") if(DEBUG_opencv_features2d) list(APPEND debug_modules opencv_highgui) endif() -ocv_define_module(features2d opencv_imgproc ${debug_modules} OPTIONAL opencv_flann WRAP java python js) +ocv_define_module(features2d opencv_imgproc ${debug_modules} OPTIONAL opencv_flann WRAP java objc python js) diff --git a/modules/features2d/misc/objc/gen_dict.json b/modules/features2d/misc/objc/gen_dict.json new file mode 100644 index 0000000000..dd22459e52 --- /dev/null +++ b/modules/features2d/misc/objc/gen_dict.json @@ -0,0 +1,16 @@ +{ + "enum_fix" : { + "FastFeatureDetector" : { "DetectorType": "FastDetectorType" }, + "AgastFeatureDetector" : { "DetectorType": "AgastDetectorType" } + }, + "func_arg_fix" : { + "(void)compute:(NSArray*)images keypoints:(NSMutableArray*>*)keypoints descriptors:(NSMutableArray*)descriptors" : { "compute" : {"name" : "compute2"} }, + "(void)detect:(NSArray*)images keypoints:(NSMutableArray*>*)keypoints masks:(NSArray*)masks" : { "detect" : {"name" : "detect2"} }, + "(DescriptorMatcher*)create:(NSString*)descriptorMatcherType" : { "create" : {"name" : "create2"} }, + "FlannBasedMatcher": { "indexParams" : {"defval" : "cv::makePtr()"}, "searchParams" : {"defval" : "cv::makePtr()"} }, + "(SimpleBlobDetector*)create" : { "parameters" : {"defval" : "cv::SimpleBlobDetector::Params()"} }, + "BFMatcher" : { "normType" : {"ctype" : "NormTypes"} }, + "(BFMatcher*)create:(int)normType crossCheck:(BOOL)crossCheck" : { "create" : {"name" : "createBFMatcher"}, + "normType" : {"ctype" : "NormTypes"} } + } +} diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt index f1deb920a6..f8bfd18e1f 100644 --- a/modules/imgcodecs/CMakeLists.txt +++ b/modules/imgcodecs/CMakeLists.txt @@ -1,5 +1,5 @@ set(the_description "Image I/O") -ocv_add_module(imgcodecs opencv_imgproc WRAP java python) +ocv_add_module(imgcodecs opencv_imgproc WRAP java objc python) # ---------------------------------------------------------------------------- # CMake file for imgcodecs. See root CMakeLists.txt @@ -115,10 +115,10 @@ file(GLOB imgcodecs_ext_hdrs if(IOS) list(APPEND imgcodecs_srcs ${CMAKE_CURRENT_LIST_DIR}/src/ios_conversions.mm) - list(APPEND IMGCODECS_LIBRARIES "-framework Accelerate" "-framework CoreGraphics" "-framework QuartzCore") + list(APPEND IMGCODECS_LIBRARIES "-framework UIKit" "-framework AssetsLibrary") endif() if(APPLE_FRAMEWORK) - list(APPEND IMGCODECS_LIBRARIES "-framework UIKit") + list(APPEND IMGCODECS_LIBRARIES "-framework Accelerate" "-framework CoreGraphics" "-framework QuartzCore") endif() if(TRUE) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs/ios.h b/modules/imgcodecs/include/opencv2/imgcodecs/ios.h index a90c6d37a8..0f15820892 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs/ios.h +++ b/modules/imgcodecs/include/opencv2/imgcodecs/ios.h @@ -45,7 +45,7 @@ #import #import #import -#include "opencv2/core/core.hpp" +#include "opencv2/core.hpp" //! @addtogroup imgcodecs_ios //! @{ diff --git a/modules/imgcodecs/misc/objc/ios/Mat+Converters.h b/modules/imgcodecs/misc/objc/ios/Mat+Converters.h new file mode 100644 index 0000000000..73dbe9cbc7 --- /dev/null +++ b/modules/imgcodecs/misc/objc/ios/Mat+Converters.h @@ -0,0 +1,27 @@ +// +// Mat+UIImage.h +// +// Created by Giles Payne on 2020/03/03. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import +#import +#import "Mat.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface Mat (Converters) + +-(UIImage*)toUIImage; +-(instancetype)initWithUIImage:(UIImage*)image; +-(instancetype)initWithUIImage:(UIImage*)image alphaExist:(BOOL)alphaExist; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/imgcodecs/misc/objc/ios/Mat+Converters.mm b/modules/imgcodecs/misc/objc/ios/Mat+Converters.mm new file mode 100644 index 0000000000..3ea3117267 --- /dev/null +++ b/modules/imgcodecs/misc/objc/ios/Mat+Converters.mm @@ -0,0 +1,28 @@ +// +// Mat+UIImage.mm +// +// Created by Giles Payne on 2020/03/03. +// + +#import "Mat+Converters.h" +#import + +@implementation Mat (Converters) + +-(UIImage*)toUIImage { + return MatToUIImage(self.nativeRef); +} + +-(instancetype)initWithUIImage:(UIImage*)image { + return [self initWithUIImage:image alphaExist:NO]; +} + +-(instancetype)initWithUIImage:(UIImage*)image alphaExist:(BOOL)alphaExist { + self = [self init]; + if (self) { + UIImageToMat(image, self.nativeRef, (bool)alphaExist); + } + return self; +} + +@end diff --git a/modules/imgcodecs/misc/objc/test/ImgcodecsTest.swift b/modules/imgcodecs/misc/objc/test/ImgcodecsTest.swift new file mode 100644 index 0000000000..857c95ed74 --- /dev/null +++ b/modules/imgcodecs/misc/objc/test/ImgcodecsTest.swift @@ -0,0 +1,50 @@ +// +// Imgcodecs.swift +// +// Created by Giles Payne on 2020/02/10. +// + +import XCTest +import OpenCV + +class ImgcodecsTest: OpenCVTestCase { + + let LENA_PATH = Bundle(for: ImgcodecsTest.self).path(forResource:"lena", ofType:"png", inDirectory:"resources")! + + func testImencodeStringMatListOfByte() { + let buff = ByteVector() + XCTAssert(Imgcodecs.imencode(ext: ".jpg", img: gray127, buf: buff)) + XCTAssertFalse(0 == buff.length) + } + + func testImencodeStringMatListOfByteListOfInteger() { + let params40 = IntVector([ImwriteFlags.IMWRITE_JPEG_QUALITY.rawValue, 40]) + let params90 = IntVector([ImwriteFlags.IMWRITE_JPEG_QUALITY.rawValue, 90]) + + let buff40 = ByteVector() + let buff90 = ByteVector() + + XCTAssert(Imgcodecs.imencode(ext: ".jpg", img: rgbLena, buf: buff40, params: params40)) + XCTAssert(Imgcodecs.imencode(ext: ".jpg", img: rgbLena, buf: buff90, params: params90)) + + XCTAssert(buff40.length > 0) + XCTAssert(buff40.length < buff90.length) + } + + func testImreadString() { + dst = Imgcodecs.imread(filename: LENA_PATH) + XCTAssertFalse(dst.empty()) + XCTAssertEqual(3, dst.channels()) + XCTAssert(512 == dst.cols()) + XCTAssert(512 == dst.rows()) + } + + func testImreadStringInt() { + dst = Imgcodecs.imread(filename: LENA_PATH, flags: 0) + XCTAssertFalse(dst.empty()); + XCTAssertEqual(1, dst.channels()); + XCTAssert(512 == dst.cols()); + XCTAssert(512 == dst.rows()); + } + +} diff --git a/modules/imgproc/CMakeLists.txt b/modules/imgproc/CMakeLists.txt index 0886c6991e..8ee300c320 100644 --- a/modules/imgproc/CMakeLists.txt +++ b/modules/imgproc/CMakeLists.txt @@ -10,7 +10,7 @@ ocv_add_dispatched_file(median_blur SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(morph SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(smooth SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(sumpixels SSE2 AVX2 AVX512_SKX) -ocv_define_module(imgproc opencv_core WRAP java python js) +ocv_define_module(imgproc opencv_core WRAP java objc python js) ocv_check_environment_variables(OPENCV_IPP_GAUSSIAN_BLUR) option(OPENCV_IPP_GAUSSIAN_BLUR "Enable IPP optimizations for GaussianBlur (+8Mb in binary size)" OFF) diff --git a/modules/imgproc/misc/objc/common/Moments.h b/modules/imgproc/misc/objc/common/Moments.h new file mode 100644 index 0000000000..cf64625403 --- /dev/null +++ b/modules/imgproc/misc/objc/common/Moments.h @@ -0,0 +1,66 @@ +// +// Moments.h +// +// Created by Giles Payne on 2019/10/06. +// + +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface Moments : NSObject + +@property double m00; +@property double m10; +@property double m01; +@property double m20; +@property double m11; +@property double m02; +@property double m30; +@property double m21; +@property double m12; +@property double m03; + +@property double mu20; +@property double mu11; +@property double mu02; +@property double mu30; +@property double mu21; +@property double mu12; +@property double mu03; + +@property double nu20; +@property double nu11; +@property double nu02; +@property double nu30; +@property double nu21; +@property double nu12; +@property double nu03; + +#ifdef __cplusplus +@property(readonly) cv::Moments& nativeRef; +#endif + +-(instancetype)initWithM00:(double)m00 m10:(double)m10 m01:(double)m01 m20:(double)m20 m11:(double)m11 m02:(double)m02 m30:(double)m30 m21:(double)m21 m12:(double)m12 m03:(double)m03; + +-(instancetype)init; + +-(instancetype)initWithVals:(NSArray*)vals; + +#ifdef __cplusplus ++(instancetype)fromNative:(cv::Moments&)moments; +#endif + +-(void)set:(NSArray*)vals; +-(void)completeState; +-(NSString *)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/imgproc/misc/objc/common/Moments.mm b/modules/imgproc/misc/objc/common/Moments.mm new file mode 100644 index 0000000000..20fa47dbd4 --- /dev/null +++ b/modules/imgproc/misc/objc/common/Moments.mm @@ -0,0 +1,304 @@ +// +// Moments.mm +// +// Created by Giles Payne on 2019/10/09. +// + +#import "Moments.h" + +@implementation Moments { + cv::Moments native; +} + +-(cv::Moments&)nativeRef { + return native; +} + +- (double)m00 { + return native.m00; +} + +- (void)setM00:(double)val { + native.m00 = val; +} + +- (double)m10 { + return native.m10; +} + +- (void)setM10:(double)val { + native.m10 = val; +} + +- (double)m01 { + return native.m01; +} + +- (void)setM01:(double)val { + native.m01 = val; +} + +- (double)m20 { + return native.m20; +} + +- (void)setM20:(double)val { + native.m20 = val; +} + +- (double)m11 { + return native.m11; +} + +- (void)setM11:(double)val { + native.m11 = val; +} + +- (double)m02 { + return native.m02; +} + +- (void)setM02:(double)val { + native.m02 = val; +} + +- (double)m30 { + return native.m30; +} + +- (void)setM30:(double)val { + native.m30 = val; +} + +- (double)m21 { + return native.m21; +} + +- (void)setM21:(double)val { + native.m21 = val; +} + +- (double)m12 { + return native.m12; +} + +- (void)setM12:(double)val { + native.m12 = val; +} + +- (double)m03 { + return native.m03; +} + +- (void)setM03:(double)val { + native.m03 = val; +} + +- (double)mu20 { + return native.mu20; +} + +- (void)setMu20:(double)val { + native.mu20 = val; +} + +- (double)mu11 { + return native.mu11; +} + +- (void)setMu11:(double)val { + native.mu11 = val; +} + +- (double)mu02 { + return native.mu02; +} + +- (void)setMu02:(double)val { + native.mu02 = val; +} + +- (double)mu30 { + return native.mu30; +} + +- (void)setMu30:(double)val { + native.mu30 = val; +} + +- (double)mu21 { + return native.mu21; +} + +- (void)setMu21:(double)val { + native.mu21 = val; +} +- (double)mu12 { + return native.mu12; +} + +- (void)setMu12:(double)val { + native.mu12 = val; +} + +- (double)mu03 { + return native.mu03; +} + +- (void)setMu03:(double)val { + native.mu03 = val; +} + +- (double)nu20 { + return native.nu20; +} + +- (void)setNu20:(double)val { + native.nu20 = val; +} + +- (double)nu11 { + return native.nu11; +} + +- (void)setNu11:(double)val { + native.nu11 = val; +} + +- (double)nu02 { + return native.nu02; +} + +- (void)setNu02:(double)val { + native.nu02 = val; +} + +- (double)nu30 { + return native.nu30; +} + +- (void)setNu30:(double)val { + native.nu30 = val; +} + +- (double)nu21 { + return native.nu21; +} + +- (void)setNu21:(double)val { + native.nu21 = val; +} + +- (double)nu12 { + return native.nu12; +} + +- (void)setNu12:(double)val { + native.nu12 = val; +} + +- (double)nu03 { + return native.nu03; +} + +- (void)setNu03:(double)val { + native.nu03 = val; +} + +-(instancetype)initWithM00:(double)m00 m10:(double)m10 m01:(double)m01 m20:(double)m20 m11:(double)m11 m02:(double)m02 m30:(double)m30 m21:(double)m21 m12:(double)m12 m03:(double)m03 { + self = [super init]; + if (self) { + self.m00 = m00; + self.m10 = m10; + self.m01 = m01; + self.m20 = m20; + self.m11 = m11; + self.m02 = m02; + self.m30 = m30; + self.m21 = m21; + self.m12 = m12; + self.m03 = m03; + [self completeState]; + } + return self; +} +-(instancetype)init { + return [self initWithM00:0 m10:0 m01:0 m20:0 m11:0 m02:0 m30:0 m21:0 m12:0 m03:0]; +} + +-(instancetype)initWithVals:(NSArray*)vals { + self = [super init]; + if (self) { + [self set:vals]; + } + return self; +} + ++(instancetype)fromNative:(cv::Moments&)moments { + return [[Moments alloc] initWithM00:moments.m00 m10:moments.m10 m01:moments.m01 m20:moments.m20 m11:moments.m11 m02:moments.m02 m30:moments.m30 m21:moments.m21 m12:moments.m12 m03:moments.m03]; +} + +-(void)set:(NSArray*)vals { + self.m00 = (vals != nil && vals.count > 0) ? vals[0].doubleValue : 0; + self.m10 = (vals != nil && vals.count > 1) ? vals[1].doubleValue : 0; + self.m01 = (vals != nil && vals.count > 2) ? vals[2].doubleValue : 0; + self.m20 = (vals != nil && vals.count > 3) ? vals[3].doubleValue : 0; + self.m11 = (vals != nil && vals.count > 4) ? vals[4].doubleValue : 0; + self.m02 = (vals != nil && vals.count > 5) ? vals[5].doubleValue : 0; + self.m30 = (vals != nil && vals.count > 6) ? vals[6].doubleValue : 0; + self.m21 = (vals != nil && vals.count > 7) ? vals[7].doubleValue : 0; + self.m12 = (vals != nil && vals.count > 8) ? vals[8].doubleValue : 0; + self.m03 = (vals != nil && vals.count > 9) ? vals[9].doubleValue : 0; + [self completeState]; +} + +-(void)completeState { + double cx = 0, cy = 0; + double mu20, mu11, mu02; + double inv_m00 = 0.0; + + if (abs(self.m00) > 0.00000001) { + inv_m00 = 1. / self.m00; + cx = self.m10 * inv_m00; + cy = self.m01 * inv_m00; + } + + // mu20 = m20 - m10*cx + mu20 = self.m20 - self.m10 * cx; + // mu11 = m11 - m10*cy + mu11 = self.m11 - self.m10 * cy; + // mu02 = m02 - m01*cy + mu02 = self.m02 - self.m01 * cy; + + self.mu20 = mu20; + self.mu11 = mu11; + self.mu02 = mu02; + + // mu30 = m30 - cx*(3*mu20 + cx*m10) + self.mu30 = self.m30 - cx * (3 * mu20 + cx * self.m10); + mu11 += mu11; + // mu21 = m21 - cx*(2*mu11 + cx*m01) - cy*mu20 + self.mu21 = self.m21 - cx * (mu11 + cx * self.m01) - cy * mu20; + // mu12 = m12 - cy*(2*mu11 + cy*m10) - cx*mu02 + self.mu12 = self.m12 - cy * (mu11 + cy * self.m10) - cx * mu02; + // mu03 = m03 - cy*(3*mu02 + cy*m01) + self.mu03 = self.m03 - cy * (3 * mu02 + cy * self.m01); + + + double inv_sqrt_m00 = sqrt(abs(inv_m00)); + double s2 = inv_m00*inv_m00, s3 = s2*inv_sqrt_m00; + + self.nu20 = self.mu20*s2; + self.nu11 = self.mu11*s2; + self.nu02 = self.mu02*s2; + self.nu30 = self.mu30*s3; + self.nu21 = self.mu21*s3; + self.nu12 = self.mu12*s3; + self.nu03 = self.mu03*s3; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Moments [ \nm00=%lf, \nm10=%lf, m01=%lf, \nm20=%lf, m11=%lf, m02=%lf, \nm30=%lf, m21=%lf, m12=%lf, m03=%lf, \nmu20=%lf, mu11=%lf, mu02=%lf, \nmu30=%lf, mu21=%lf, mu12=%lf, mu03=%lf, \nnu20=%lf, nu11=%lf, nu02=%lf, \nnu30=%lf, nu21=%lf, nu12=%lf, nu03=%lf, \n]", self.m00, self.m10, self.m01, self.m20, self.m11, self.m02, self.m30, self.m21, self.m12, self.m03, self.mu20, self.mu11, self.mu02, self.mu30, self.mu21, self.mu12, self.mu03, self.nu20, self.nu11, self.nu02, self.nu30, self.nu21, self.nu12, self.nu03]; +} + +@end diff --git a/modules/imgproc/misc/objc/gen_dict.json b/modules/imgproc/misc/objc/gen_dict.json new file mode 100644 index 0000000000..87614924a1 --- /dev/null +++ b/modules/imgproc/misc/objc/gen_dict.json @@ -0,0 +1,121 @@ +{ + "module_imports": ["Size2i"], + "const_ignore_list": [ + "CV_TM_.+", + "CV_COLORCVT_MAX", + "CV_.*Bayer.*", + "CV_YUV420(i|sp|p)2.+", + "CV_L?(BGRA?|RGBA?|GRAY|XYZ|YCrCb|Luv|Lab|HLS|YUV|HSV)\\d*2L?(BGRA?|RGBA?|GRAY|XYZ|YCrCb|Luv|Lab|HLS|YUV|HSV).*", + "CV_FLOODFILL_.+", + "CV_ADAPTIVE_THRESH_.+", + "CV_DIST_.+", + "CV_HOUGH_.+", + "CV_CONTOURS_MATCH_.+", + "CV_COMP_.+" + ], + "const_private_list" : [ + "CV_MOP_.+", + "CV_INTER_.+", + "CV_THRESH_.+", + "CV_INPAINT_.+", + "CV_RETR_.+", + "CV_CHAIN_APPROX_.+" + ], + "missing_consts" : { + "Imgproc" : { + "private" : [ + ["IPL_BORDER_CONSTANT", 0 ], + ["IPL_BORDER_REPLICATE", 1 ], + ["IPL_BORDER_REFLECT", 2 ], + ["IPL_BORDER_WRAP", 3 ], + ["IPL_BORDER_REFLECT_101", 4 ], + ["IPL_BORDER_TRANSPARENT", 5 ] + ] + } + }, + "func_arg_fix" : { + "goodFeaturesToTrack" : { "corners" : {"ctype" : "vector_Point"} }, + "minEnclosingCircle" : { "points" : {"ctype" : "vector_Point2f"} }, + "fitEllipse" : { "points" : {"ctype" : "vector_Point2f"} }, + "fillPoly" : { "pts" : {"ctype" : "vector_vector_Point"}, + "lineType" : {"ctype" : "LineTypes"}}, + "polylines" : { "pts" : {"ctype" : "vector_vector_Point"}, + "lineType" : {"ctype" : "LineTypes"} }, + "fillConvexPoly" : { "points" : {"ctype" : "vector_Point"}, + "lineType" : {"ctype" : "LineTypes"} }, + "approxPolyDP" : { "curve" : {"ctype" : "vector_Point2f"}, + "approxCurve" : {"ctype" : "vector_Point2f"} }, + "arcLength" : { "curve" : {"ctype" : "vector_Point2f"} }, + "pointPolygonTest" : { "contour" : {"ctype" : "vector_Point2f"} }, + "minAreaRect" : { "points" : {"ctype" : "vector_Point2f"} }, + "getAffineTransform" : { "src" : {"ctype" : "vector_Point2f"}, + "dst" : {"ctype" : "vector_Point2f"} }, + "drawContours" : { "contours" : {"ctype" : "vector_vector_Point"}, + "lineType" : {"ctype" : "LineTypes"} }, + "findContours" : { "contours" : {"ctype" : "vector_vector_Point"}, + "mode" : {"ctype" : "RetrievalModes"}, + "method" : {"ctype" : "ContourApproximationModes"} }, + "convexityDefects" : { "contour" : {"ctype" : "vector_Point"}, + "convexhull" : {"ctype" : "vector_int"}, + "convexityDefects" : {"ctype" : "vector_Vec4i"} }, + "isContourConvex" : { "contour" : {"ctype" : "vector_Point"} }, + "convexHull" : { "points" : {"ctype" : "vector_Point"}, + "hull" : {"ctype" : "vector_int"}, + "returnPoints" : {"ctype" : ""} }, + "getStructuringElement" : { "shape" : {"ctype" : "MorphShapes"} }, + "EMD" : {"lowerBound" : {"defval" : "cv::Ptr()"}, + "distType" : {"ctype" : "DistanceTypes"}}, + "createLineSegmentDetector" : { "_refine" : {"ctype" : "LineSegmentDetectorModes"}}, + "compareHist" : { "method" : {"ctype" : "HistCompMethods"}}, + "matchShapes" : { "method" : {"ctype" : "ShapeMatchModes"}}, + "threshold" : { "type" : {"ctype" : "ThresholdTypes"}}, + "connectedComponentsWithStatsWithAlgorithm" : { "ccltype" : {"ctype" : "ConnectedComponentsAlgorithmsTypes"}}, + "GaussianBlur" : { "borderType" : {"ctype" : "BorderTypes"}}, + "HoughCircles" : { "method" : {"ctype" : "HoughModes"}}, + "Laplacian" : { "borderType" : {"ctype" : "BorderTypes"}}, + "Scharr" : { "borderType" : {"ctype" : "BorderTypes"}}, + "Sobel" : { "borderType" : {"ctype" : "BorderTypes"}}, + "adaptiveThreshold" : { "adaptiveMethod" : {"ctype" : "AdaptiveThresholdTypes"}, + "thresholdType" : {"ctype" : "ThresholdTypes"}}, + "applyColorMap" : { "colormap" : {"ctype" : "ColormapTypes"}}, + "arrowedLine" : { "line_type" : {"ctype" : "LineTypes"}}, + "bilateralFilter" : { "borderType" : {"ctype" : "BorderTypes"}}, + "blur" : { "borderType" : {"ctype" : "BorderTypes"}}, + "boxFilter" : { "borderType" : {"ctype" : "BorderTypes"}}, + "circle" : { "lineType" : {"ctype" : "LineTypes"}}, + "cornerEigenValsAndVecs" : { "borderType" : {"ctype" : "BorderTypes"}}, + "cornerHarris" : { "borderType" : {"ctype" : "BorderTypes"}}, + "cornerMinEigenVal" : { "borderType" : {"ctype" : "BorderTypes"}}, + "cvtColor" : { "code" : {"ctype" : "ColorConversionCodes"}}, + "dilate" : { "borderType" : {"ctype" : "BorderTypes"}}, + "distanceTransformWithLabels" : { "labelType" : {"ctype" : "DistanceTransformLabelTypes"}, + "distanceType" : {"ctype" : "DistanceTypes"}, + "maskSize" : {"ctype" : "DistanceTransformMasks"}}, + "distanceTransform" : { "distanceType" : {"ctype" : "DistanceTypes"}, + "maskSize" : {"ctype" : "DistanceTransformMasks"}}, + "drawMarker" : { "markerType" : {"ctype" : "MarkerTypes"}, + "line_type" : {"ctype" : "LineTypes"}}, + "ellipse" : { "lineType" : {"ctype" : "LineTypes"}}, + "erode" : { "borderType" : {"ctype" : "BorderTypes"}}, + "filter2D" : { "borderType" : {"ctype" : "BorderTypes"}}, + "fitLine" : { "distType" : {"ctype" : "DistanceTypes"}}, + "line" : { "lineType" : {"ctype" : "LineTypes"}}, + "matchTemplate" : { "method" : {"ctype" : "TemplateMatchModes"}}, + "morphologyEx" : { "op" : {"ctype" : "MorphTypes"}, + "borderType" : {"ctype" : "BorderTypes"}}, + "preCornerDetect" : { "borderType" : {"ctype" : "BorderTypes"}}, + "putText" : { "fontFace" : {"ctype" : "HersheyFonts"}, + "lineType" : {"ctype" : "LineTypes"}}, + "pyrDown" : { "borderType" : {"ctype" : "BorderTypes"}}, + "pyrUp" : { "borderType" : {"ctype" : "BorderTypes"}}, + "rectangle" : { "lineType" : {"ctype" : "LineTypes"}}, + "remap" : { "borderMode": {"ctype" : "BorderTypes"}}, + "sepFilter2D" : { "borderType" : {"ctype" : "BorderTypes"}}, + "spatialGradient" : { "borderType" : {"ctype" : "BorderTypes"}}, + "sqrBoxFilter" : { "borderType" : {"ctype" : "BorderTypes"}}, + "warpAffine" : { "borderMode": {"ctype" : "BorderTypes"}}, + "warpPerspective" : { "borderMode": {"ctype" : "BorderTypes"}}, + "getTextSize" : { "fontFace": {"ctype" : "HersheyFonts"}}, + "(void)insert:(NSArray*)ptvec" : { "insert" : {"name" : "insertVector"} } + } +} diff --git a/modules/imgproc/misc/objc/test/ImgprocTest.swift b/modules/imgproc/misc/objc/test/ImgprocTest.swift new file mode 100644 index 0000000000..f9f89f3c88 --- /dev/null +++ b/modules/imgproc/misc/objc/test/ImgprocTest.swift @@ -0,0 +1,1743 @@ +// +// ImgprocTest.swift +// +// Created by Giles Payne on 2020/02/08. +// + +import XCTest +import OpenCV + +class ImgprocTest: OpenCVTestCase { + + let anchorPoint = Point(x: 2, y: 2) + let imgprocSz: Int32 = 2 + let size = Size(width: 3, height: 3) + + func testAccumulateMatMat() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let dst = getMat(CvType.CV_64F, vals: [0]) + let dst2 = src.clone() + + Imgproc.accumulate(src: src, dst: dst) + Imgproc.accumulate(src: src, dst: dst2) + + try assertMatEqual(src, dst, OpenCVTestCase.EPS) + try assertMatEqual(getMat(CvType.CV_64F, vals: [4]), dst2, OpenCVTestCase.EPS) + } + + func testAccumulateMatMatMat() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let mask = makeMask(getMat(CvType.CV_8U, vals: [1])) + let dst = getMat(CvType.CV_64F, vals: [0]) + let dst2 = src.clone() + + Imgproc.accumulate(src: src, dst: dst, mask: mask) + Imgproc.accumulate(src: src, dst: dst2, mask: mask) + + try assertMatEqual(makeMask(getMat(CvType.CV_64F, vals: [2])), dst, OpenCVTestCase.EPS) + try assertMatEqual(makeMask(getMat(CvType.CV_64F, vals: [4]), vals: [2]), dst2, OpenCVTestCase.EPS) + } + + func testAccumulateProductMatMatMat() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let dst = getMat(CvType.CV_64F, vals: [0]) + let dst2 = src.clone() + + Imgproc.accumulateProduct(src1: src, src2: src, dst: dst) + Imgproc.accumulateProduct(src1: src, src2: dst, dst: dst2) + + try assertMatEqual(getMat(CvType.CV_64F, vals:[4]), dst, OpenCVTestCase.EPS) + try assertMatEqual(getMat(CvType.CV_64F, vals:[10]), dst2, OpenCVTestCase.EPS) + } + + func testAccumulateProductMatMatMatMat() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let mask = makeMask(getMat(CvType.CV_8U, vals: [1])) + let dst = getMat(CvType.CV_64F, vals: [0]) + let dst2 = src.clone() + + Imgproc.accumulateProduct(src1: src, src2: src, dst: dst, mask: mask) + Imgproc.accumulateProduct(src1: src, src2: dst, dst: dst2, mask: mask) + + try assertMatEqual(makeMask(getMat(CvType.CV_64F, vals: [4])), dst, OpenCVTestCase.EPS) + try assertMatEqual(makeMask(getMat(CvType.CV_64F, vals: [10]), vals:[2]), dst2, OpenCVTestCase.EPS) + } + + func testAccumulateSquareMatMat() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let dst = getMat(CvType.CV_64F, vals: [0]) + let dst2 = src.clone() + + Imgproc.accumulateSquare(src: src, dst: dst) + Imgproc.accumulateSquare(src: src, dst: dst2) + + try assertMatEqual(getMat(CvType.CV_64F, vals: [4]), dst, OpenCVTestCase.EPS) + try assertMatEqual(getMat(CvType.CV_64F, vals: [6]), dst2, OpenCVTestCase.EPS) + } + + func testAccumulateSquareMatMatMat() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let mask = makeMask(getMat(CvType.CV_8U, vals: [1])) + let dst = getMat(CvType.CV_64F, vals: [0]) + let dst2 = src.clone() + + Imgproc.accumulateSquare(src: src, dst: dst, mask: mask) + Imgproc.accumulateSquare(src: src, dst: dst2, mask: mask) + + try assertMatEqual(makeMask(getMat(CvType.CV_64F, vals: [4])), dst, OpenCVTestCase.EPS) + try assertMatEqual(makeMask(getMat(CvType.CV_64F, vals: [6]), vals: [2]), dst2, OpenCVTestCase.EPS) + } + + func testAccumulateWeightedMatMatDouble() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let dst = getMat(CvType.CV_64F, vals: [4]) + let dst2 = src.clone() + + Imgproc.accumulateWeighted(src: src, dst: dst, alpha: 0.5) + Imgproc.accumulateWeighted(src: src, dst: dst2, alpha: 2) + + try assertMatEqual(getMat(CvType.CV_64F, vals: [3]), dst, OpenCVTestCase.EPS) + try assertMatEqual(getMat(CvType.CV_64F, vals: [2]), dst2, OpenCVTestCase.EPS) + } + + func testAccumulateWeightedMatMatDoubleMat() throws { + let src = getMat(CvType.CV_64F, vals: [2]) + let mask = makeMask(getMat(CvType.CV_8U, vals: [1])) + let dst = getMat(CvType.CV_64F, vals: [4]) + let dst2 = src.clone() + + Imgproc.accumulateWeighted(src: src, dst: dst, alpha: 0.5, mask: mask) + Imgproc.accumulateWeighted(src: src, dst: dst2, alpha: 2, mask: mask) + + try assertMatEqual(makeMask(getMat(CvType.CV_64F, vals: [3]), vals: [4]), dst, OpenCVTestCase.EPS) + try assertMatEqual(getMat(CvType.CV_64F, vals: [2]), dst2, OpenCVTestCase.EPS) + } + + func testAdaptiveThreshold() { + let src = makeMask(getMat(CvType.CV_8U, vals: [50]), vals:[20]) + let dst = Mat() + + Imgproc.adaptiveThreshold(src: src, dst: dst, maxValue: 1, adaptiveMethod: .ADAPTIVE_THRESH_MEAN_C, thresholdType: .THRESH_BINARY, blockSize: 3, C: 0) + + XCTAssertEqual(src.rows(), Core.countNonZero(src: dst)) + } + + func testApproxPolyDP() { + let curve = [Point2f(x: 1, y: 3), Point2f(x: 2, y: 4), Point2f(x: 3, y: 5), Point2f(x: 4, y: 4), Point2f(x: 5, y: 3)] + + let approxCurve = NSMutableArray() + + Imgproc.approxPolyDP(curve: curve, approxCurve: approxCurve, epsilon: OpenCVTestCase.EPS, closed: true) + + let approxCurveGold = [Point2f(x: 1, y: 3), Point2f(x: 3, y: 5), Point2f(x: 5, y: 3)] + + XCTAssert(approxCurve as! [Point2f] == approxCurveGold) + } + + func testArcLength() { + let curve = [Point2f(x: 1, y: 3), Point2f(x: 2, y: 4), Point2f(x: 3, y: 5), Point2f(x: 4, y: 4), Point2f(x: 5, y: 3)] + + let arcLength = Imgproc.arcLength(curve: curve, closed: false) + + XCTAssertEqual(5.656854249, arcLength, accuracy:0.000001) + } + + func testBilateralFilterMatMatIntDoubleDouble() throws { + Imgproc.bilateralFilter(src: gray255, dst: dst, d: 5, sigmaColor: 10, sigmaSpace: 5) + + try assertMatEqual(gray255, dst) + } + + func testBilateralFilterMatMatIntDoubleDoubleInt() throws { + Imgproc.bilateralFilter(src: gray255, dst: dst, d: 5, sigmaColor: 10, sigmaSpace: 5, borderType: .BORDER_REFLECT) + + try assertMatEqual(gray255, dst) + } + + func testBlurMatMatSize() throws { + Imgproc.blur(src: gray0, dst: dst, ksize: size) + try assertMatEqual(gray0, dst) + + Imgproc.blur(src: gray255, dst: dst, ksize: size) + try assertMatEqual(gray255, dst) + } + + func testBlurMatMatSizePoint() throws { + Imgproc.blur(src: gray0, dst: dst, ksize: size, anchor: anchorPoint) + try assertMatEqual(gray0, dst) + } + + func testBlurMatMatSizePointInt() throws { + Imgproc.blur(src: gray0, dst: dst, ksize: size, anchor: anchorPoint, borderType: .BORDER_REFLECT) + try assertMatEqual(gray0, dst) + } + + func testBoundingRect() { + let points = [Point(x: 0, y: 0), Point(x: 0, y: 4), Point(x: 4, y: 0), Point(x: 4, y: 4)] + let p1 = Point(x: 1, y: 1) + let p2 = Point(x: -5, y: -2) + + let bbox = Imgproc.boundingRect(array: MatOfPoint(array: points)) + + XCTAssert(bbox.contains(p1)) + XCTAssertFalse(bbox.contains(p2)) + } + + func testBoxFilterMatMatIntSize() throws { + let size = Size(width: 3, height: 3) + Imgproc.boxFilter(src: gray0, dst: dst, ddepth: 8, ksize: size) + try assertMatEqual(gray0, dst) + } + + func testBoxFilterMatMatIntSizePointBoolean() throws { + Imgproc.boxFilter(src: gray255, dst: dst, ddepth: 8, ksize: size, anchor: anchorPoint, normalize: false) + try assertMatEqual(gray255, dst) + } + + func testBoxFilterMatMatIntSizePointBooleanInt() throws { + Imgproc.boxFilter(src: gray255, dst: dst, ddepth: 8, ksize: size, anchor: anchorPoint, normalize: false, borderType: .BORDER_REFLECT) + try assertMatEqual(gray255, dst) + } + + func testCalcBackProject() { + let images = [grayChess] + let channels = IntVector([0]) + let histSize = IntVector([10]) + let ranges = FloatVector([0, 256]) + + let hist = Mat() + Imgproc.calcHist(images: images, channels: channels, mask: Mat(), hist: hist, histSize: histSize, ranges: ranges) + Core.normalize(src: hist, dst: hist) + + Imgproc.calcBackProject(images: images, channels: channels, hist: hist, dst: dst, ranges: ranges, scale: 255) + + XCTAssertEqual(grayChess.size(), dst.size()) + XCTAssertEqual(grayChess.depth(), dst.depth()) + XCTAssertFalse(0 == Core.countNonZero(src: dst)) + } + + func testCalcHistListOfMatListOfIntegerMatMatListOfIntegerListOfFloat() throws { + let images = [gray128] + let channels = IntVector([0]) + let histSize = IntVector([10]) + let ranges = FloatVector([0, 256]) + let hist = Mat() + + Imgproc.calcHist(images: images, channels: channels, mask: Mat(), hist: hist, histSize: histSize, ranges: ranges) + + truth = Mat(rows: 10, cols: 1, type: CvType.CV_32F, scalar: Scalar.all(0)) + try truth!.put(row: 5, col: 0, data: [100] as [Float]) + try assertMatEqual(truth!, hist, OpenCVTestCase.EPS) + } + + func testCalcHistListOfMatListOfIntegerMatMatListOfIntegerListOfFloat2D() throws { + let images = [gray255, gray128] + let channels = IntVector([0, 1]) + let histSize = IntVector([10, 10]) + let ranges = FloatVector([0, 256, 0, 256]) + let hist = Mat() + + Imgproc.calcHist(images: images, channels: channels, mask: Mat(), hist: hist, histSize: histSize, ranges: ranges) + + truth = Mat(rows: 10, cols: 10, type: CvType.CV_32F, scalar: Scalar.all(0)) + try truth!.put(row: 9, col: 5, data: [100] as [Float]) + try assertMatEqual(truth!, hist, OpenCVTestCase.EPS) + } + + func testCalcHistListOfMatListOfIntegerMatMatListOfIntegerListOfFloat3D() throws { + let images = [rgbLena] + + let hist3D = Mat() + let histList = [Mat(), Mat(), Mat()] + + let histSize = IntVector([10]) + let ranges = FloatVector([0, 256]) + + for i:Int in 0.. 0) + + Imgproc.polylines(img: img, pts: polyline2, isClosed: true, color: Scalar(0), thickness: 2, lineType: .LINE_8, shift: 1) + + XCTAssertEqual(0, Core.countNonZero(src: img)) + } + + func testPutTextMatStringPointIntDoubleScalar() { + let text = "Hello World" + let labelSize = Size(width: 175, height: 22) + let img = Mat(rows: 20 + labelSize.height, cols: 20 + labelSize.width, type: CvType.CV_8U, scalar: colorBlack) + let origin = Point(x: 10, y: labelSize.height + 10) + + Imgproc.putText(img: img, text: text, org: origin, fontFace: .FONT_HERSHEY_SIMPLEX, fontScale: 1.0, color: colorWhite) + + XCTAssert(Core.countNonZero(src: img) > 0) + // check that border is not corrupted + Imgproc.rectangle(img: img, pt1: Point(x: 11, y: 11), pt2: Point(x: labelSize.width + 10, y: labelSize.height + 10), color: colorBlack, thickness: Core.FILLED) + XCTAssertEqual(0, Core.countNonZero(src: img)) + } + + func testPutTextMatStringPointIntDoubleScalarInt() { + let text = "Hello World" + let labelSize = Size(width: 176, height: 22) + let img = Mat(rows: 20 + labelSize.height, cols: 20 + labelSize.width, type: CvType.CV_8U, scalar: colorBlack) + let origin = Point(x: 10, y: labelSize.height + 10) + + Imgproc.putText(img: img, text: text, org: origin, fontFace: .FONT_HERSHEY_SIMPLEX, fontScale: 1.0, color: colorWhite, thickness: 2) + + XCTAssert(Core.countNonZero(src: img) > 0) + // check that border is not corrupted + Imgproc.rectangle(img: img, pt1: Point(x: 10, y: 10), pt2: Point(x: labelSize.width + 10 + 1, y: labelSize.height + 10 + 1), color: colorBlack, thickness: Core.FILLED) + XCTAssertEqual(0, Core.countNonZero(src: img)) + } + + func testPutTextMatStringPointIntDoubleScalarIntIntBoolean() { + let text = "Hello World" + let labelSize = Size(width: 175, height: 22) + + let img = Mat(rows: 20 + labelSize.height, cols: 20 + labelSize.width, type: CvType.CV_8U, scalar: colorBlack) + let origin = Point(x: 10, y: 10) + + Imgproc.putText(img: img, text: text, org: origin, fontFace: .FONT_HERSHEY_SIMPLEX, fontScale: 1.0, color: colorWhite, thickness: 1, lineType: .LINE_8, bottomLeftOrigin: true) + + XCTAssert(Core.countNonZero(src: img) > 0) + // check that border is not corrupted + Imgproc.rectangle(img: img, pt1: Point(x: 10, y: 10), pt2: Point(x: labelSize.width + 9, y: labelSize.height + 9), color: colorBlack, thickness: Core.FILLED) + XCTAssertEqual(0, Core.countNonZero(src: img)) + } + +} diff --git a/modules/imgproc/misc/objc/test/MomentsTest.swift b/modules/imgproc/misc/objc/test/MomentsTest.swift new file mode 100644 index 0000000000..01902f536f --- /dev/null +++ b/modules/imgproc/misc/objc/test/MomentsTest.swift @@ -0,0 +1,42 @@ +// +// MomentsTest.swift +// +// Created by Giles Payne on 2020/02/10. +// + +import XCTest +import OpenCV + +class MomentsTest: XCTestCase { + + func testAll() { + let data = Mat(rows: 3,cols: 3, type: CvType.CV_8UC1, scalar: Scalar(1)) + data.row(1).setTo(scalar: Scalar(5)) + let res = Imgproc.moments(array: data) + XCTAssertEqual(res.m00, 21.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m10, 21.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m01, 21.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m20, 35.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m11, 21.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m02, 27.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m30, 63.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m21, 35.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m12, 27.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.m03, 39.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.mu20, 14.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.mu11, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.mu02, 6.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.mu30, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.mu21, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.mu12, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.mu03, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.nu20, 0.031746031746031744, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.nu11, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.nu02, 0.013605442176870746, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.nu30, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.nu21, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.nu12, 0.0, accuracy: OpenCVTestCase.EPS); + XCTAssertEqual(res.nu03, 0.0, accuracy: OpenCVTestCase.EPS); + } + +} diff --git a/modules/imgproc/misc/objc/test/Subdiv2DTest.swift b/modules/imgproc/misc/objc/test/Subdiv2DTest.swift new file mode 100644 index 0000000000..0a57fa363b --- /dev/null +++ b/modules/imgproc/misc/objc/test/Subdiv2DTest.swift @@ -0,0 +1,23 @@ +// +// Subdiv2DTest.swift +// +// Created by Giles Payne on 2020/02/10. +// + +import XCTest +import OpenCV + +class Subdiv2DTest: OpenCVTestCase { + + func testGetTriangleList() { + let s2d = Subdiv2D(rect: Rect(x: 0, y: 0, width: 50, height: 50)) + s2d.insert(pt: Point2f(x: 10, y: 10)) + s2d.insert(pt: Point2f(x: 20, y: 10)) + s2d.insert(pt: Point2f(x: 20, y: 20)) + s2d.insert(pt: Point2f(x: 10, y: 20)) + let triangles = NSMutableArray() + s2d.getTriangleList(triangleList: triangles) + XCTAssertEqual(2, triangles.count) + } + +} diff --git a/modules/ml/CMakeLists.txt b/modules/ml/CMakeLists.txt index 1b64cc4f17..e1d5f3100b 100644 --- a/modules/ml/CMakeLists.txt +++ b/modules/ml/CMakeLists.txt @@ -1,2 +1,2 @@ set(the_description "Machine Learning") -ocv_define_module(ml opencv_core WRAP java python) +ocv_define_module(ml opencv_core WRAP java objc python) diff --git a/modules/ml/misc/objc/gen_dict.json b/modules/ml/misc/objc/gen_dict.json new file mode 100644 index 0000000000..1f35051c2d --- /dev/null +++ b/modules/ml/misc/objc/gen_dict.json @@ -0,0 +1,9 @@ +{ + "enum_fix" : { + "EM" : { "Types": "EMTypes" }, + "SVM" : { "Types": "SVMTypes" }, + "KNearest" : { "Types": "KNearestTypes" }, + "DTrees" : { "Flags": "DTreeFlags" }, + "StatModel" : { "Flags": "StatModelFlags" } + } +} diff --git a/modules/objc/CMakeLists.txt b/modules/objc/CMakeLists.txt new file mode 100644 index 0000000000..d4ea6e3563 --- /dev/null +++ b/modules/objc/CMakeLists.txt @@ -0,0 +1,6 @@ +if(OPENCV_INITIAL_PASS AND APPLE_FRAMEWORK AND NOT (BUILD_opencv_objc STREQUAL "OFF")) + # generator for Objective-C source code and documentation signatures + add_subdirectory(generator) +endif() + +#include(${CMAKE_CURRENT_SOURCE_DIR}/common.cmake) diff --git a/modules/objc/common.cmake b/modules/objc/common.cmake new file mode 100644 index 0000000000..ccfc923eaf --- /dev/null +++ b/modules/objc/common.cmake @@ -0,0 +1,11 @@ +ocv_warnings_disable(CMAKE_CXX_FLAGS -Wdeprecated-declarations) + +# get list of modules to wrap +# message(STATUS "Wrapped in Objective-C:") +set(OPENCV_OBJC_MODULES) +foreach(m ${OPENCV_MODULES_BUILD}) + if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";objc;" AND HAVE_${m}) + list(APPEND OPENCV_OBJC_MODULES ${m}) + #message(STATUS "\t${m}") + endif() +endforeach() diff --git a/modules/objc/doc/README.md b/modules/objc/doc/README.md new file mode 100644 index 0000000000..640d8eb70b --- /dev/null +++ b/modules/objc/doc/README.md @@ -0,0 +1,15 @@ +## About + +This is the documentation for the Objective-C/Swift OpenCV wrapper + +To get started: add the OpenCV framework to your project and add the following to your source + +###Objective-C + + #import + +###Swift + + import OpenCV + +For details of core OpenCV functionality see: `Core`, `Mat`, `Imgproc` diff --git a/modules/objc/generator/CMakeLists.txt b/modules/objc/generator/CMakeLists.txt new file mode 100644 index 0000000000..be7ab3ff74 --- /dev/null +++ b/modules/objc/generator/CMakeLists.txt @@ -0,0 +1,86 @@ +set(MODULE_NAME "objc") +set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE) +ocv_add_module(${MODULE_NAME} INTERNAL opencv_core opencv_imgproc) + +set(OPENCV_OBJC_SIGNATURES_FILE "${CMAKE_CURRENT_BINARY_DIR}/opencv_objc_signatures.json" CACHE INTERNAL "") +set(OPENCV_OBJC_BINDINGS_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "") + +file(REMOVE_RECURSE "${OPENCV_OBJC_BINDINGS_DIR}/gen") +file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source") # force re-run after CMake + +# This file is included from a subdirectory +set(OBJC_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") +include(${OBJC_SOURCE_DIR}/common.cmake) + +# common files +file(GLOB_RECURSE deps "${CMAKE_CURRENT_SOURCE_DIR}/templates/*") + +set(__modules_config "") # list of OpenCV modules +foreach(m ${OPENCV_OBJC_MODULES}) + set(module_objc_dir "${OPENCV_MODULE_${m}_LOCATION}/misc/objc") + list(APPEND deps ${OPENCV_MODULE_${m}_HEADERS}) + file(GLOB_RECURSE misc_files "${module_objc_dir}/*") + list(APPEND deps ${misc_files}) + + string(REGEX REPLACE "^opencv_" "" m_ "${m}") + if(__modules_config) + set(__modules_config "${__modules_config},\n") + endif() + file(RELATIVE_PATH rel_path "${OpenCV_SOURCE_DIR}" "${OPENCV_MODULE_${m}_LOCATION}") + set(__modules_config "${__modules_config} { \"name\": \"${m_}\", \"location\": \"${rel_path}\" }") +endforeach(m) + +set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json") +set(__config_str +"{ + \"rootdir\": \"${OpenCV_SOURCE_DIR}\", + \"modules\": [ +${__modules_config} + ] +} +") +if(EXISTS "${CONFIG_FILE}") + file(READ "${CONFIG_FILE}" __content) +else() + set(__content "") +endif() +if(NOT "${__content}" STREQUAL "${__config_str}") + file(WRITE "${CONFIG_FILE}" "${__config_str}") + file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source") +endif() +unset(__config_str) + +set(objc_generated_files + # "${OPENCV_OBJC_SIGNATURES_FILE}" + "${OPENCV_DEPHELPER}/gen_opencv_objc_source" +) + +string(REPLACE "opencv_" "" MODULES "${OPENCV_OBJC_MODULES}") + +if(IOS) + set(TARGET "ios") +else() + set(TARGET "osx") +endif() + +add_custom_command( + OUTPUT ${objc_generated_files} + COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${OBJC_SOURCE_DIR}/generator/gen_objc.py" -p "${OBJC_SOURCE_DIR}/../python/src2/gen2.py" -c "${CONFIG_FILE}" -t "${TARGET}" -f "${FRAMEWORK_NAME}" + COMMAND ${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/gen_opencv_objc_source" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${OBJC_SOURCE_DIR}/generator/gen_objc.py" + "${OBJC_SOURCE_DIR}/../python/src2/gen2.py" + "${OBJC_SOURCE_DIR}/../python/src2/hdr_parser.py" + # don't, result of file(WRITE): "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json" + ${deps} + # not allowed (file(WRITE) result): "${CONFIG_FILE}" + COMMENT "Generate files for Objective-C bindings" +) + +add_custom_target(gen_opencv_objc_source DEPENDS ${objc_generated_files} + SOURCES "${OBJC_SOURCE_DIR}/generator/gen_objc.py" + "${OBJC_SOURCE_DIR}/generator/templates/cmakelists.template" + "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json" +) + +add_dependencies(opencv_world gen_opencv_objc_source) diff --git a/modules/objc/generator/gen_objc.py b/modules/objc/generator/gen_objc.py new file mode 100755 index 0000000000..0e64fcab2a --- /dev/null +++ b/modules/objc/generator/gen_objc.py @@ -0,0 +1,1384 @@ +#!/usr/bin/env python + +import sys, re, os.path, errno, fnmatch +import json +import logging +import codecs +from shutil import copyfile +from pprint import pformat +from string import Template +from distutils.dir_util import copy_tree + +if sys.version_info[0] >= 3: + from io import StringIO +else: + import io + class StringIO(io.StringIO): + def write(self, s): + if isinstance(s, str): + s = unicode(s) # noqa: F821 + return super(StringIO, self).write(s) + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + +# list of modules +config = None +ROOT_DIR = None + +total_files = 0 +updated_files = 0 + +module_imports = [] + +# list of class names, which should be skipped by wrapper generator +# the list is loaded from misc/objc/gen_dict.json defined for the module and its dependencies +class_ignore_list = [] + +# list of constant names, which should be skipped by wrapper generator +# ignored constants can be defined using regular expressions +const_ignore_list = [] + +# list of private constants +const_private_list = [] + +# { Module : { public : [[name, val],...], private : [[]...] } } +missing_consts = {} + +type_dict = { + "" : {"objc_type" : ""}, # c-tor ret_type + "void" : {"objc_type" : "void", "is_primitive" : True}, + "bool" : {"objc_type" : "BOOL", "is_primitive" : True, "to_cpp": "(bool)%(n)s"}, + "char" : {"objc_type" : "char", "is_primitive" : True}, + "int" : {"objc_type" : "int", "is_primitive" : True, "out_type" : "int*", "out_type_ptr": "%(n)s", "out_type_ref": "*(int*)(%(n)s)"}, + "long" : {"objc_type" : "long", "is_primitive" : True}, + "float" : {"objc_type" : "float", "is_primitive" : True, "out_type" : "float*", "out_type_ptr": "%(n)s", "out_type_ref": "*(float*)(%(n)s)"}, + "double" : {"objc_type" : "double", "is_primitive" : True, "out_type" : "double*", "out_type_ptr": "%(n)s", "out_type_ref": "*(double*)(%(n)s)"}, + "size_t" : {"objc_type" : "size_t", "is_primitive" : True}, + "int64" : {"objc_type" : "long", "is_primitive" : True}, + "string" : {"objc_type" : "NSString*", "is_primitive" : True, "from_cpp": "[NSString stringWithUTF8String:%(n)s.c_str()]", "cast_to": "std::string"} +} + +# Defines a rule to add extra prefixes for names from specific namespaces. +# In example, cv::fisheye::stereoRectify from namespace fisheye is wrapped as fisheye_stereoRectify +namespaces_dict = {} + +# { class : [ header ] } +AdditionalImports = {} + +# { class : { func : {declaration, implementation} } } +ManualFuncs = {} + +# { class : { func : { arg_name : {"ctype" : ctype, "attrib" : [attrib]} } } } +func_arg_fix = {} + +# { class : { enum: fixed_enum } } +enum_fix = {} + +# { (class, func) : objc_signature } +method_dict = { + ("Mat", "convertTo") : "-convertTo:rtype:alpha:beta:", + ("Mat", "setTo") : "-setToScalar:mask:", + ("Mat", "zeros") : "+zeros:cols:type:", + ("Mat", "ones") : "+ones:cols:type:", + ("Mat", "dot") : "-dot:" +} + +def read_contents(fname): + with open(fname, 'r') as f: + data = f.read() + return data + +def mkdir_p(path): + ''' mkdir -p ''' + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + +T_OBJC_CLASS_HEADER = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_class_header.template')) +T_OBJC_CLASS_BODY = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_class_body.template')) +T_OBJC_MODULE_HEADER = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_module_header.template')) +T_OBJC_MODULE_BODY = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_module_body.template')) + +class GeneralInfo(): + def __init__(self, type, decl, namespaces): + self.namespace, self.classpath, self.classname, self.name = self.parseName(decl[0], namespaces) + + # parse doxygen comments + self.params={} + + self.deprecated = False + if type == "class": + docstring = "// C++: class " + self.name + "\n" + else: + docstring="" + + if len(decl)>5 and decl[5]: + doc = decl[5] + + if re.search("(@|\\\\)deprecated", doc): + self.deprecated = True + + docstring += sanitize_documentation_string(doc, type) + elif type == "class": + docstring += "/**\n* The " + self.name + " module\n*/\n" + + self.docstring = docstring + + def parseName(self, name, namespaces): + ''' + input: full name and available namespaces + returns: (namespace, classpath, classname, name) + ''' + name = name[name.find(" ")+1:].strip() # remove struct/class/const prefix + spaceName = "" + localName = name # . + for namespace in sorted(namespaces, key=len, reverse=True): + if name.startswith(namespace + "."): + spaceName = namespace + localName = name.replace(namespace + ".", "") + break + pieces = localName.split(".") + if len(pieces) > 2: # ... + return spaceName, ".".join(pieces[:-1]), pieces[-2], pieces[-1] + elif len(pieces) == 2: # . + return spaceName, pieces[0], pieces[0], pieces[1] + elif len(pieces) == 1: # + return spaceName, "", "", pieces[0] + else: + return spaceName, "", "" # error?! + + def fullName(self, isCPP=False): + result = ".".join([self.fullClass(), self.name]) + return result if not isCPP else get_cname(result) + + def fullClass(self, isCPP=False): + result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f)>0]) + return result if not isCPP else get_cname(result) + +class ConstInfo(GeneralInfo): + def __init__(self, decl, addedManually=False, namespaces=[], enumType=None): + GeneralInfo.__init__(self, "const", decl, namespaces) + self.cname = get_cname(self.name) + self.value = decl[1] + self.enumType = enumType + self.addedManually = addedManually + if self.namespace in namespaces_dict: + self.name = '%s_%s' % (namespaces_dict[self.namespace], self.name) + + def __repr__(self): + return Template("CONST $name=$value$manual").substitute(name=self.name, + value=self.value, + manual="(manual)" if self.addedManually else "") + + def isIgnored(self): + for c in const_ignore_list: + if re.match(c, self.name): + return True + return False + +def normalize_field_name(name): + return name.replace(".","_").replace("[","").replace("]","").replace("_getNativeObjAddr()","_nativeObj") + +def normalize_class_name(name): + return re.sub(r"^cv\.", "", name).replace(".", "_") + +def get_cname(name): + return name.replace(".", "::") + +def cast_from(t): + if t in type_dict and "cast_from" in type_dict[t]: + return type_dict[t]["cast_from"] + return t + +def cast_to(t): + if t in type_dict and "cast_to" in type_dict[t]: + return type_dict[t]["cast_to"] + return t + +class ClassPropInfo(): + def __init__(self, decl): # [f_ctype, f_name, '', '/RW'] + self.ctype = decl[0] + self.name = decl[1] + self.rw = "/RW" in decl[3] + + def __repr__(self): + return Template("PROP $ctype $name").substitute(ctype=self.ctype, name=self.name) + +class ClassInfo(GeneralInfo): + def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ] + GeneralInfo.__init__(self, "class", decl, namespaces) + self.cname = get_cname(self.name) + self.methods = [] + self.methods_suffixes = {} + self.consts = [] # using a list to save the occurrence order + self.private_consts = [] + self.imports = set() + self.props= [] + self.objc_name = self.name + self.smart = None # True if class stores Ptr* instead of T* in nativeObj field + self.additionalImports = None # additional import files + self.enum_declarations = None # Objective-C enum declarations stream + self.method_declarations = None # Objective-C method declarations stream + self.method_implementations = None # Objective-C method implementations stream + self.objc_header_template = None # Objective-C header code + self.objc_body_template = None # Objective-C body code + for m in decl[2]: + if m.startswith("="): + self.objc_name = m[1:] + self.base = '' + self.is_base_class = True + if decl[1]: + self.base = re.sub(r"^.*:", "", decl[1].split(",")[0]).strip().replace(self.objc_name, "") + + def __repr__(self): + return Template("CLASS $namespace::$classpath.$name : $base").substitute(**self.__dict__) + + def getImports(self, module): + return ["#import \"%s.h\"" % c for c in sorted(filter(lambda m: m != self.name, map(lambda m: type_dict[m]["import_module"] if m in type_dict and "import_module" in type_dict[m] else m, self.imports)))] + + def isEnum(self, c): + return c in type_dict and type_dict[c].get("is_enum", False) + + def getForwardDeclarations(self, module): + enum_decl = filter(lambda x:self.isEnum(x) and type_dict[x]["import_module"] != module, self.imports) + class_decl = filter(lambda x: not self.isEnum(x), self.imports) + return ["#import \"%s.h\"" % type_dict[c]["import_module"] for c in enum_decl] + [""] + ["@class %s;" % c for c in sorted(class_decl)] + + def addImports(self, ctype, is_out_type): + if ctype == self.cname: + return + if ctype in type_dict: + objc_import = None + if "v_type" in type_dict[ctype]: + objc_import = type_dict[type_dict[ctype]["v_type"]]["objc_type"] + elif "v_v_type" in type_dict[ctype]: + objc_import = type_dict[type_dict[ctype]["v_v_type"]]["objc_type"] + elif not type_dict[ctype].get("is_primitive", False): + objc_import = type_dict[ctype]["objc_type"] + if objc_import is not None and objc_import not in ["NSNumber*", "NSString*"] and not (objc_import in type_dict and type_dict[objc_import].get("is_primitive", False)): + objc_import = objc_import[:-1] if objc_import[-1] == "*" else objc_import # remove trailing "*" + if objc_import != self.cname: + self.imports.add(objc_import) # remove trailing "*" + + def getAllMethods(self): + result = [] + result.extend([fi for fi in sorted(self.methods) if fi.isconstructor]) + result.extend([fi for fi in sorted(self.methods) if not fi.isconstructor]) + return result + + def addMethod(self, fi): + self.methods.append(fi) + + def getConst(self, name): + for cand in self.consts + self.private_consts: + if cand.name == name: + return cand + return None + + def addConst(self, constinfo): + # choose right list (public or private) + consts = self.consts + for c in const_private_list: + if re.match(c, constinfo.name): + consts = self.private_consts + break + consts.append(constinfo) + + def initCodeStreams(self, Module): + self.additionalImports = StringIO() + self.enum_declarations = StringIO() + self.method_declarations = StringIO() + self.method_implementations = StringIO() + if self.base: + self.objc_header_template = T_OBJC_CLASS_HEADER + self.objc_body_template = T_OBJC_CLASS_BODY + self.is_base_class = False + else: + self.base = "NSObject" + if self.name != Module: + self.objc_header_template = T_OBJC_CLASS_HEADER + self.objc_body_template = T_OBJC_CLASS_BODY + else: + self.objc_header_template = T_OBJC_MODULE_HEADER + self.objc_body_template = T_OBJC_MODULE_BODY + # misc handling + if self.name == Module: + for i in module_imports or []: + self.imports.add(i) + + def cleanupCodeStreams(self): + self.additionalImports.close() + self.enum_declarations.close() + self.method_declarations.close() + self.method_implementations.close() + + def generateObjcHeaderCode(self, m, M, objcM): + return Template(self.objc_header_template + "\n\n").substitute( + module = M, + additionalImports = self.additionalImports.getvalue(), + importBaseClass = '#import "' + self.base + '.h"' if not self.is_base_class else "", + forwardDeclarations = "\n".join(filter(None, self.getForwardDeclarations(objcM))), + enumDeclarations = self.enum_declarations.getvalue(), + nativePointerHandling = Template( +""" +#ifdef __cplusplus +@property(readonly)$cName* nativePtr; +#endif + +- (void)dealloc; + +#ifdef __cplusplus +- (instancetype)initWithNativePtr:($cName*)nativePtr; ++ (instancetype)fromNative:($cName*)nativePtr; +#endif +""" + ).substitute( + cName = self.fullName(isCPP=True) + ) if self.is_base_class else "", + manualMethodDeclations = "", + methodDeclarations = self.method_declarations.getvalue(), + name = self.name, + objcName = self.objc_name, + cName = self.cname, + imports = "\n".join(self.getImports(M)), + docs = self.docstring, + base = self.base) + + def generateObjcBodyCode(self, m, M): + return Template(self.objc_body_template + "\n\n").substitute( + module = M, + nativePointerHandling=Template( +""" +- (void)dealloc { + if (_nativePtr != NULL) { + delete _nativePtr; + } +} + +- (instancetype)initWithNativePtr:($cName*)nativePtr { + self = [super init]; + if (self) { + _nativePtr = nativePtr; + } + return self; +} + ++ (instancetype)fromNative:($cName*)nativePtr { + return [[$objcName alloc] initWithNativePtr:nativePtr]; +} +""" + ).substitute( + cName=self.fullName(isCPP=True), + objcName=self.objc_name + ) if self.is_base_class else "", + manualMethodDeclations = "", + methodImplementations = self.method_implementations.getvalue(), + name = self.name, + objcName = self.objc_name, + cName = self.cname, + imports = "\n".join(self.getImports(M)), + docs = self.docstring, + base = self.base) + +class ArgInfo(): + def __init__(self, arg_tuple): # [ ctype, name, def val, [mod], argno ] + self.pointer = False + ctype = arg_tuple[0] + if ctype.endswith("*"): + ctype = ctype[:-1] + self.pointer = True + self.ctype = ctype + self.name = arg_tuple[1] + self.defval = arg_tuple[2] + self.out = "" + if "/O" in arg_tuple[3]: + self.out = "O" + if "/IO" in arg_tuple[3]: + self.out = "IO" + + def __repr__(self): + return Template("ARG $ctype$p $name=$defval").substitute(ctype=self.ctype, + p=" *" if self.pointer else "", + name=self.name, + defval=self.defval) + +class FuncInfo(GeneralInfo): + def __init__(self, decl, namespaces=[]): # [ funcname, return_ctype, [modifiers], [args] ] + GeneralInfo.__init__(self, "func", decl, namespaces) + self.cname = get_cname(decl[0]) + self.objc_name = self.name + self.swift_name = self.name + self.cv_name = self.fullName(isCPP=True) + self.isconstructor = self.name == self.classname + if "[" in self.name: + self.objc_name = "getelem" + if self.namespace in namespaces_dict: + self.objc_name = '%s_%s' % (namespaces_dict[self.namespace], self.objc_name) + for m in decl[2]: + if m.startswith("="): + self.objc_name = m[1:] + self.static = ["","static"][ "/S" in decl[2] ] + self.ctype = re.sub(r"^CvTermCriteria", "TermCriteria", decl[1] or "") + self.args = [] + func_fix_map = func_arg_fix.get(self.objc_name, {}) + for a in decl[3]: + arg = a[:] + arg_fix_map = func_fix_map.get(arg[1], {}) + arg[0] = arg_fix_map.get('ctype', arg[0]) #fixing arg type + arg[2] = arg_fix_map.get('defval', arg[2]) #fixing arg defval + arg[3] = arg_fix_map.get('attrib', arg[3]) #fixing arg attrib + self.args.append(ArgInfo(arg)) + + if type_complete(self.args, self.ctype): + func_fix_map = func_arg_fix.get(self.signature(self.args), {}) + name_fix_map = func_fix_map.get(self.name, {}) + self.objc_name = name_fix_map.get('name', self.objc_name) + for arg in self.args: + arg_fix_map = func_fix_map.get(arg.name, {}) + arg.ctype = arg_fix_map.get('ctype', arg.ctype) #fixing arg type + arg.defval = arg_fix_map.get('defval', arg.defval) #fixing arg type + arg.name = arg_fix_map.get('name', arg.name) #fixing arg name + + def __repr__(self): + return Template("FUNC <$ctype $namespace.$classpath.$name $args>").substitute(**self.__dict__) + + def __lt__(self, other): + return self.__repr__() < other.__repr__() + + def signature(self, args): + objc_args = build_objc_args(args) + return "(" + type_dict[self.ctype]["objc_type"] + ")" + self.objc_name + " ".join(objc_args) + +def type_complete(args, ctype): + for a in args: + if a.ctype not in type_dict: + if not a.defval and a.ctype.endswith("*"): + a.defval = 0 + if a.defval: + a.ctype = '' + continue + return False + if ctype not in type_dict: + return False + return True + +def build_objc_args(args): + objc_args = [] + for a in args: + if a.ctype not in type_dict: + if not a.defval and a.ctype.endswith("*"): + a.defval = 0 + if a.defval: + a.ctype = '' + continue + if not a.ctype: # hidden + continue + objc_type = type_dict[a.ctype]["objc_type"] + if "v_type" in type_dict[a.ctype]: + if "O" in a.out: + objc_type = "NSMutableArray<" + objc_type + ">*" + else: + objc_type = "NSArray<" + objc_type + ">*" + elif "v_v_type" in type_dict[a.ctype]: + if "O" in a.out: + objc_type = "NSMutableArray*>*" + else: + objc_type = "NSArray*>*" + + if a.out and type_dict[a.ctype].get("out_type", ""): + objc_type = type_dict[a.ctype]["out_type"] + objc_args.append((a.name if len(objc_args) > 0 else '') + ':(' + objc_type + ')' + a.name) + return objc_args + +def build_objc_method_name(args): + objc_method_name = "" + for a in args[1:]: + if a.ctype not in type_dict: + if not a.defval and a.ctype.endswith("*"): + a.defval = 0 + if a.defval: + a.ctype = '' + continue + if not a.ctype: # hidden + continue + objc_method_name += a.name + ":" + return objc_method_name + +def build_swift_signature(args): + swift_signature = "" + for a in args: + if a.ctype not in type_dict: + if not a.defval and a.ctype.endswith("*"): + a.defval = 0 + if a.defval: + a.ctype = '' + continue + if not a.ctype: # hidden + continue + swift_signature += a.name + ":" + return swift_signature + +def add_method_to_dict(class_name, fi): + static = fi.static if fi.classname else True + if not method_dict.has_key((class_name, fi.objc_name)): + objc_method_name = ("+" if static else "-") + fi.objc_name + ":" + build_objc_method_name(fi.args) + method_dict[(class_name, fi.objc_name)] = objc_method_name + +def see_lookup(objc_class, see): + semi_colon = see.find("::") + see_class = see[:semi_colon] if semi_colon > 0 else objc_class + see_method = see[(semi_colon + 2):] if semi_colon != -1 else see + if method_dict.has_key((see_class, see_method)): + method = method_dict[(see_class, see_method)] + if see_class == objc_class: + return method + else: + return ("-" if method[0] == "-" else "") + "[" + see_class + " " + method[1:] + "]" + else: + return see + + +class ObjectiveCWrapperGenerator(object): + def __init__(self): + self.header_files = [] + self.clear() + + def clear(self): + self.namespaces = set(["cv"]) + mat_class_info = ClassInfo([ 'class Mat', '', [], [] ], self.namespaces) + mat_class_info.namespace = "cv" + self.classes = { "Mat" : mat_class_info } + self.classes["Mat"].namespace = "cv" + self.module = "" + self.Module = "" + self.ported_func_list = [] + self.skipped_func_list = [] + self.def_args_hist = {} # { def_args_cnt : funcs_cnt } + + def add_class(self, decl): + classinfo = ClassInfo(decl, namespaces=self.namespaces) + if classinfo.name in class_ignore_list: + logging.info('ignored: %s', classinfo) + return + name = classinfo.name + if self.isWrapped(name) and not classinfo.base: + logging.warning('duplicated: %s', classinfo) + return + self.classes[name] = classinfo + if name in type_dict and not classinfo.base: + logging.warning('duplicated: %s', classinfo) + return + if name != self.Module: + type_dict.setdefault(name, {}).update( + { "objc_type" : classinfo.objc_name + "*", + "from_cpp" : "[" + classinfo.objc_name + " fromNative:%(n)s]", + "to_cpp" : "*(" + classinfo.namespace.replace(".", "::") + "::" + classinfo.objc_name + "*)(%(n)s.nativePtr)" } + ) + + # missing_consts { Module : { public : [[name, val],...], private : [[]...] } } + if name in missing_consts: + if 'public' in missing_consts[name]: + for (n, val) in missing_consts[name]['public']: + classinfo.consts.append( ConstInfo([n, val], addedManually=True) ) + + # class props + for p in decl[3]: + if True: #"vector" not in p[0]: + classinfo.props.append( ClassPropInfo(p) ) + else: + logging.warning("Skipped property: [%s]" % name, p) + + if name != self.Module: + type_dict.setdefault("Ptr_"+name, {}).update( + { "objc_type" : classinfo.objc_name + "*", + "c_type" : name, + "to_cpp": "%(n)s.nativePtr", + "from_cpp_ptr": "[" + name + " fromNativePtr:%(n)s]"} + ) + logging.info('ok: class %s, name: %s, base: %s', classinfo, name, classinfo.base) + + def add_const(self, decl, scope=None, enumType=None): # [ "const cname", val, [], [] ] + constinfo = ConstInfo(decl, namespaces=self.namespaces, enumType=enumType) + if constinfo.isIgnored(): + logging.info('ignored: %s', constinfo) + else: + if not self.isWrapped(constinfo.classname): + logging.info('class not found: %s', constinfo) + constinfo.name = constinfo.classname + '_' + constinfo.name + constinfo.classname = '' + + ci = self.getClass(constinfo.classname) + duplicate = ci.getConst(constinfo.name) + if duplicate: + if duplicate.addedManually: + logging.info('manual: %s', constinfo) + else: + logging.warning('duplicated: %s', constinfo) + else: + ci.addConst(constinfo) + logging.info('ok: %s', constinfo) + + def add_enum(self, decl, scope): # [ "enum cname", "", [], [] ] + enumType = decl[0].rsplit(" ", 1)[1] + if enumType.endswith(""): + enumType = None + else: + ctype = normalize_class_name(enumType) + constinfo = ConstInfo(decl[3][0], namespaces=self.namespaces, enumType=enumType) + objc_type = enumType.rsplit(".", 1)[-1] + if enum_fix.has_key(constinfo.classname): + objc_type = enum_fix[constinfo.classname].get(objc_type, objc_type) + import_module = constinfo.classname if constinfo.classname and constinfo.classname != objc_type else self.Module + type_dict[ctype] = { "cast_from" : "int", + "cast_to" : get_cname(enumType), + "objc_type" : objc_type, + "is_enum" : True, + "import_module" : import_module, + "from_cpp" : "(" + objc_type + ")%(n)s"} + type_dict[objc_type] = { "cast_to" : get_cname(enumType), + "objc_type": objc_type, + "is_enum": True, + "import_module": import_module} + const_decls = decl[3] + + for decl in const_decls: + self.add_const(decl, scope, enumType) + + def add_func(self, decl): + fi = FuncInfo(decl, namespaces=self.namespaces) + classname = fi.classname or self.Module + if classname in class_ignore_list: + logging.info('ignored: %s', fi) + elif classname in ManualFuncs and fi.objc_name in ManualFuncs[classname]: + logging.info('manual: %s', fi) + if ManualFuncs[classname][fi.objc_name].has_key("objc_method_name"): + method_dict[(classname, fi.objc_name)] = ManualFuncs[classname][fi.objc_name]["objc_method_name"] + elif not self.isWrapped(classname): + logging.warning('not found: %s', fi) + else: + self.getClass(classname).addMethod(fi) + logging.info('ok: %s', fi) + # calc args with def val + cnt = len([a for a in fi.args if a.defval]) + self.def_args_hist[cnt] = self.def_args_hist.get(cnt, 0) + 1 + add_method_to_dict(classname, fi) + + def save(self, path, buf): + global total_files, updated_files + total_files += 1 + if os.path.exists(path): + with open(path, "rt") as f: + content = f.read() + if content == buf: + return + with codecs.open(path, "w", "utf-8") as f: + f.write(buf) + updated_files += 1 + + def get_namespace_prefix(self, cname): + namespace = self.classes[cname].namespace if self.classes.has_key(cname) else "cv" + return namespace.replace(".", "::") + "::" + + def gen(self, srcfiles, module, output_path, output_objc_path, common_headers): + self.clear() + self.module = module + self.Module = module.capitalize() + # TODO: support UMat versions of declarations (implement UMat-wrapper for Java) + parser = hdr_parser.CppHeaderParser(generate_umat_decls=False) + + self.add_class( ['class ' + self.Module, '', [], []] ) # [ 'class/struct cname', ':bases', [modlist] [props] ] + + # scan the headers and build more descriptive maps of classes, consts, functions + includes = [] + for hdr in common_headers: + logging.info("\n===== Common header : %s =====", hdr) + includes.append('#include "' + hdr + '"') + for hdr in srcfiles: + decls = parser.parse(hdr) + self.namespaces = parser.namespaces + logging.info("\n\n===== Header: %s =====", hdr) + logging.info("Namespaces: %s", parser.namespaces) + if decls: + includes.append('#include "' + hdr + '"') + else: + logging.info("Ignore header: %s", hdr) + for decl in decls: + logging.info("\n--- Incoming ---\n%s", pformat(decl[:5], 4)) # without docstring + name = decl[0] + if name.startswith("struct") or name.startswith("class"): + self.add_class(decl) + elif name.startswith("const"): + self.add_const(decl) + elif name.startswith("enum"): + # enum + self.add_enum(decl, self.Module) + else: # function + self.add_func(decl) + + logging.info("\n\n===== Generating... =====") + package_path = os.path.join(output_objc_path, module) + mkdir_p(package_path) + for ci in self.classes.values(): + if ci.name == "Mat": + continue + ci.initCodeStreams(self.Module) + self.gen_class(ci) + classObjcHeaderCode = ci.generateObjcHeaderCode(self.module, self.Module, ci.objc_name) + header_file = "%s/%s/%s.h" % (output_objc_path, module, ci.objc_name) + self.save(header_file, classObjcHeaderCode) + self.header_files.append(header_file) + classObjcBodyCode = ci.generateObjcBodyCode(self.module, self.Module) + self.save("%s/%s/%s.mm" % (output_objc_path, module, ci.objc_name), classObjcBodyCode) + ci.cleanupCodeStreams() + self.save(os.path.join(output_path, module+".txt"), self.makeReport()) + + def makeReport(self): + ''' + Returns string with generator report + ''' + report = StringIO() + total_count = len(self.ported_func_list)+ len(self.skipped_func_list) + report.write("PORTED FUNCs LIST (%i of %i):\n\n" % (len(self.ported_func_list), total_count)) + report.write("\n".join(self.ported_func_list)) + report.write("\n\nSKIPPED FUNCs LIST (%i of %i):\n\n" % (len(self.skipped_func_list), total_count)) + report.write("".join(self.skipped_func_list)) + for i in self.def_args_hist.keys(): + report.write("\n%i def args - %i funcs" % (i, self.def_args_hist[i])) + return report.getvalue() + + def fullTypeName(self, t): + if not type_dict[t].get("is_primitive", False) or type_dict[t].has_key("cast_to"): + if type_dict[t].has_key("cast_to"): + return type_dict[t]["cast_to"] + else: + namespace_prefix = self.get_namespace_prefix(t) + return namespace_prefix + t + else: + return t + + def build_objc2cv_prologue(self, prologue, vector_type, vector_full_type, objc_type, vector_name, array_name): + if not (type_dict.has_key(vector_type) and type_dict[vector_type].has_key("to_cpp") and type_dict[vector_type]["to_cpp"] != "%(n)s.nativeRef"): + prologue.append("OBJC2CV(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ");") + else: + conv_macro = "CONV_" + array_name + prologue.append("#define " + conv_macro + "(e) " + type_dict[vector_type]["to_cpp"] % {"n": "e"}) + prologue.append("OBJC2CV_CUSTOM(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ", " + conv_macro + ");") + prologue.append("#undef " + conv_macro) + + def build_cv2objc_epilogue(self, epilogue, vector_type, vector_full_type, objc_type, vector_name, array_name): + if not (type_dict.has_key(vector_type) and type_dict[vector_type].has_key("from_cpp") and type_dict[vector_type]["from_cpp"] != ("[" + objc_type[:-1] + " fromNative:%(n)s]")): + epilogue.append("CV2OBJC(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ");") + else: + unconv_macro = "UNCONV_" + array_name + epilogue.append("#define " + unconv_macro + "(e) " + type_dict[vector_type]["from_cpp"] % {"n": "e"}) + epilogue.append("CV2OBJC_CUSTOM(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ", " + unconv_macro + ");") + epilogue.append("#undef " + unconv_macro) + + def gen_func(self, ci, fi): + logging.info("%s", fi) + method_declarations = ci.method_declarations + method_implementations = ci.method_implementations + + decl_args = [] + for a in fi.args: + s = a.ctype or ' _hidden_ ' + if a.pointer: + s += "*" + elif a.out: + s += "&" + s += " " + a.name + if a.defval: + s += " = " + str(a.defval) + decl_args.append(s) + c_decl = "%s %s %s(%s)" % ( fi.static, fi.ctype, fi.cname, ", ".join(decl_args) ) + + # comment + method_declarations.write( "\n//\n// %s\n//\n" % c_decl ) + method_implementations.write( "\n//\n// %s\n//\n" % c_decl ) + # check if we 'know' all the types + if fi.ctype not in type_dict: # unsupported ret type + msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype + self.skipped_func_list.append(c_decl + "\n" + msg) + method_declarations.write( " "*4 + msg ) + logging.warning("SKIP:" + c_decl.strip() + "\t due to RET type " + fi.ctype) + return + for a in fi.args: + if a.ctype not in type_dict: + if not a.defval and a.ctype.endswith("*"): + a.defval = 0 + if a.defval: + a.ctype = '' + continue + msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I") + self.skipped_func_list.append(c_decl + "\n" + msg) + method_declarations.write( msg ) + logging.warning("SKIP:" + c_decl.strip() + "\t due to ARG type " + a.ctype + "/" + (a.out or "I")) + return + + self.ported_func_list.append(c_decl) + + # args + args = fi.args[:] # copy + objc_signatures=[] + while True: + # method args + cv_args = [] + prologue = [] + epilogue = [] + if fi.ctype: + ci.addImports(fi.ctype, False) + for a in args: + if not "v_type" in type_dict[a.ctype] and not "v_v_type" in type_dict[a.ctype]: + cast = ("(" + type_dict[a.ctype]["cast_to"] + ")") if "cast_to" in type_dict[a.ctype] else "" + cv_name = type_dict[a.ctype].get("to_cpp", cast + "%(n)s") if a.ctype else a.defval + if a.pointer and not cv_name == "0": + cv_name = "&(" + cv_name + ")" + if "O" in a.out and type_dict[a.ctype].get("out_type", ""): + cv_name = type_dict[a.ctype].get("out_type_ptr" if a.pointer else "out_type_ref", "%(n)s") + cv_args.append(type_dict[a.ctype].get("cv_name", cv_name) % {"n": a.name}) + if not a.ctype: # hidden + continue + ci.addImports(a.ctype, "O" in a.out) + if "v_type" in type_dict[a.ctype]: # pass as vector + vector_cpp_type = type_dict[a.ctype]["v_type"] + objc_type = type_dict[a.ctype]["objc_type"] + has_namespace = vector_cpp_type.find("::") != -1 + ci.addImports(a.ctype, False) + vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type + vector_cpp_name = a.name + "Vector" + cv_args.append(vector_cpp_name) + self.build_objc2cv_prologue(prologue, vector_cpp_type, vector_full_cpp_type, objc_type, vector_cpp_name, a.name) + if "O" in a.out: + self.build_cv2objc_epilogue(epilogue, vector_cpp_type, vector_full_cpp_type, objc_type, vector_cpp_name, a.name) + + if "v_v_type" in type_dict[a.ctype]: # pass as vector of vector + vector_cpp_type = type_dict[a.ctype]["v_v_type"] + objc_type = type_dict[a.ctype]["objc_type"] + ci.addImports(a.ctype, False) + vector_full_cpp_type = self.fullTypeName(vector_cpp_type) + vector_cpp_name = a.name + "Vector2" + cv_args.append(vector_cpp_name) + prologue.append("OBJC2CV2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", " + vector_cpp_name + ", " + a.name + ");") + if "O" in a.out: + epilogue.append( + "CV2OBJC2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", " + vector_cpp_name + ", " + a.name + ");") + + # calculate method signature to check for uniqueness + objc_args = build_objc_args(args) + objc_signature = fi.signature(args) + logging.info("Objective-C: " + objc_signature) + + if objc_signature in objc_signatures: + if args: + args.pop() + continue + else: + break + + # doc comment + if fi.docstring: + lines = fi.docstring.splitlines() + toWrite = [] + for index, line in enumerate(lines): + p0 = line.find("@param") + if p0 != -1: + p0 += 7 # len("@param" + 1) + p1 = line.find(' ', p0) + p1 = len(line) if p1 == -1 else p1 + name = line[p0:p1] + for arg in args: + if arg.name == name: + toWrite.append(re.sub('\*\s*@param ', '* @param ', line)) + break + else: + s0 = line.find("@see") + if s0 != -1: + sees = line[(s0 + 5):].split(",") + toWrite.append(line[:(s0 + 5)] + ", ".join(["`" + see_lookup(ci.objc_name, see.strip()) + "`" for see in sees])) + else: + toWrite.append(line) + + for line in toWrite: + method_declarations.write(line + "\n") + + # public wrapper method impl (calling native one above) + # e.g. + # public static void add( Mat src1, Mat src2, Mat dst, Mat mask, int dtype ) + # { add_0( src1.nativeObj, src2.nativeObj, dst.nativeObj, mask.nativeObj, dtype ); } + ret_type = fi.ctype + if fi.ctype.endswith('*'): + ret_type = ret_type[:-1] + ret_val = self.fullTypeName(fi.ctype) + " retVal = " + ret = "return retVal;" + tail = "" + constructor = False + if "v_type" in type_dict[ret_type]: + objc_type = type_dict[ret_type]["objc_type"] + vector_type = type_dict[ret_type]["v_type"] + full_cpp_type = (self.get_namespace_prefix(vector_type) if (vector_type.find("::") == -1) else "") + vector_type + prologue.append("NSMutableArray<" + objc_type + ">* retVal = [NSMutableArray new];") + ret_val = "std::vector<" + full_cpp_type + "> retValVector = " + self.build_cv2objc_epilogue(epilogue, vector_type, full_cpp_type, objc_type, "retValVector", "retVal") + elif "v_v_type" in type_dict[ret_type]: + objc_type = type_dict[ret_type]["objc_type"] + cpp_type = type_dict[ret_type]["v_v_type"] + if cpp_type.find("::") == -1: + cpp_type = self.get_namespace_prefix(cpp_type) + cpp_type + prologue.append("NSMutableArray*>* retVal = [NSMutableArray new];") + ret_val = "std::vector<" + cpp_type + "> retValVector = " + epilogue.append("CV2OBJC2(" + cpp_type + ", " + objc_type[:-1] + ", retValVector, retVal);") + elif ret_type.startswith("Ptr_"): + cpp_type = type_dict[ret_type]["c_type"] + namespace_prefix = self.get_namespace_prefix(cpp_type) + ret_val = namespace_prefix + cpp_type + "* retVal = " + ret = "return [" + type_dict[ret_type]["objc_type"][:-1] + " fromNative:retVal];" + elif ret_type == "void": + ret_val = "" + ret = "" + elif ret_type == "": # c-tor + constructor = True + ret_val = "return [self initWithNativePtr:new " + tail = "]" + ret = "" + elif self.isWrapped(ret_type): # wrapped class + namespace_prefix = self.get_namespace_prefix(ret_type) + ret_val = namespace_prefix + ret_type + "* retVal = new " + namespace_prefix + ret_type + "(" + tail = ")" + ret_type_dict = type_dict[ret_type] + from_cpp = ret_type_dict["from_cpp_ptr"] if ret_type_dict.has_key("from_cpp_ptr") else ret_type_dict["from_cpp"] + ret = "return " + (from_cpp % { "n" : "retVal" }) + ";" + elif "from_cpp" in type_dict[ret_type]: + ret = "return " + (type_dict[ret_type]["from_cpp"] % { "n" : "retVal" }) + ";" + + static = fi.static if fi.classname else True + + objc_ret_type = type_dict[fi.ctype]["objc_type"] if type_dict[fi.ctype]["objc_type"] else "void" if not constructor else "instancetype" + if "v_type" in type_dict[ret_type]: + objc_ret_type = "NSArray<" + objc_ret_type + ">*" + elif "v_v_type" in type_dict[ret_type]: + objc_ret_type = "NSArray*>*" + + prototype = Template("$static ($objc_ret_type)$objc_name$objc_args").substitute( + static = "+" if static else "-", + objc_ret_type = objc_ret_type, + objc_args = " ".join(objc_args), + objc_name = fi.objc_name if not constructor else ("init" + ("With" + (args[0].name[0].upper() + args[0].name[1:]) if len(args) > 0 else "")) + ) + + method_declarations.write( Template( +"""$prototype$swift_name$deprecation_decl; + +""" + ).substitute( + prototype = prototype, + swift_name = " NS_SWIFT_NAME(" + fi.swift_name + "(" + build_swift_signature(args) + "))" if not constructor else "", + deprecation_decl = " DEPRECATED_ATTRIBUTE" if fi.deprecated else "" + ) + ) + + method_implementations.write( Template( +"""$prototype {$prologue + $ret_val$obj_deref$cv_name($cv_args)$tail;$epilogue$ret +} + +""" + ).substitute( + prototype = prototype, + ret = "\n " + ret if ret else "", + ret_val = ret_val, + prologue = "\n " + "\n ".join(prologue) if prologue else "", + epilogue = "\n " + "\n ".join(epilogue) if epilogue else "", + static = "+" if static else "-", + obj_deref = ("MAKE_PTR(" + fi.fullClass(isCPP=True) + ")->" if not ci.is_base_class else "_nativePtr->") if not static and not constructor else "", + cv_name = fi.cv_name if static else fi.fullClass(isCPP=True) if constructor else fi.name, + cv_args = ", ".join(cv_args), + tail = tail + ) + ) + # adding method signature to dictionary + objc_signatures.append(objc_signature) + + # processing args with default values + if args and args[-1].defval: + args.pop() + else: + break + + def gen_class(self, ci): + logging.info("%s", ci) + if ci.name in AdditionalImports: + ci.additionalImports.write("\n".join(["#import %s" % h for h in AdditionalImports[ci.name]])) + + # constants + wrote_consts_pragma = False + consts_map = {c.name: c for c in ci.private_consts} + consts_map.update({c.name: c for c in ci.consts}) + def const_value(v): + if v in consts_map: + target = consts_map[v] + assert target.value != v + return const_value(target.value) + return v + if ci.consts: + enumTypes = set(map(lambda c: c.enumType, ci.consts)) + grouped_consts = {enumType: [c for c in ci.consts if c.enumType == enumType] for enumType in enumTypes} + for typeName, consts in grouped_consts.items(): + logging.info("%s", consts) + if typeName: + typeName = typeName.rsplit(".", 1)[-1] + if enum_fix.has_key(ci.cname): + typeName = enum_fix[ci.cname].get(typeName, typeName) + + ci.enum_declarations.write(""" +// C++: enum {1} +typedef NS_ENUM(int, {2}) {{ + {0}\n}};\n\n""".format(",\n ".join(["%s = %s" % (c.name, c.value) for c in consts]), typeName, typeName) + ) + else: + if not wrote_consts_pragma: + ci.method_declarations.write("#pragma mark - Class Constants\n\n") + wrote_consts_pragma = True + ci.method_declarations.write(""" +{0}\n\n""".format("\n".join(["@property (class, readonly) int %s NS_SWIFT_NAME(%s);" % (c.name, c.name) for c in consts])) + ) + declared_consts = [] + match_alphabet = re.compile("[a-zA-Z]") + for c in consts: + value = str(c.value) + if match_alphabet.search(value): + for declared_const in sorted(declared_consts, key=len, reverse=True): + regex = re.compile("(?" if not ci.is_base_class else "_nativePtr->" + if type_data.has_key("v_type"): + vector_type = type_data["v_type"] + full_cpp_type = (self.get_namespace_prefix(vector_type) if (vector_type.find("::") == -1) else "") + vector_type + ret_val = "std::vector<" + full_cpp_type + "> retValVector = " + ci.method_implementations.write("-(NSArray<" + objc_type + ">*)" + pi.name + "{\n") + ci.method_implementations.write("\tNSMutableArray<" + objc_type + ">* retVal = [NSMutableArray new];\n") + ci.method_implementations.write("\t" + ret_val + ptr_ref + pi.name + ";\n") + epilogue = [] + self.build_cv2objc_epilogue(epilogue, vector_type, full_cpp_type, objc_type, "retValVector", "retVal") + ci.method_implementations.write("\t" + ("\n\t".join(epilogue)) + "\n") + ci.method_implementations.write("\treturn retVal;\n}\n\n") + else: + from_cpp = type_data.get("from_cpp", "%(n)s") + retVal = from_cpp % {"n": (ptr_ref + pi.name)} + ci.method_implementations.write("-(" + objc_type + ")" + pi.name + "{\n\treturn " + retVal + ";\n}\n\n") + if pi.rw: + if type_data.has_key("v_type"): + vector_type = type_data["v_type"] + full_cpp_type = (self.get_namespace_prefix(vector_type) if (vector_type.find("::") == -1) else "") + vector_type + ci.method_implementations.write("-(void)set" + pi.name[0].upper() + pi.name[1:] + ":(NSArray<" + objc_type + ">*)" + pi.name + "{\n") + prologue = [] + self.build_objc2cv_prologue(prologue, vector_type, full_cpp_type, objc_type, "valVector", pi.name) + ci.method_implementations.write("\t" + ("\n\t".join(prologue)) + "\n") + ci.method_implementations.write("\t" + ptr_ref + pi.name + " = valVector;\n}\n\n") + else: + to_cpp = type_data.get("to_cpp", "%(n)s") + val = to_cpp % {"n": pi.name} + ci.method_implementations.write("-(void)set" + pi.name[0].upper() + pi.name[1:] + ":(" + objc_type + ")" + pi.name + " {\n\t" + ptr_ref + pi.name + " = " + val + ";\n}\n\n") + + # manual ports + if ci.name in ManualFuncs: + for func in ManualFuncs[ci.name].keys(): + ci.method_declarations.write( "\n".join(ManualFuncs[ci.name][func]["declaration"]) ) + ci.method_implementations.write( "\n".join(ManualFuncs[ci.name][func]["implementation"]) ) + + def getClass(self, classname): + return self.classes[classname or self.Module] + + def isWrapped(self, classname): + name = classname or self.Module + return name in self.classes + + def isSmartClass(self, ci): + ''' + Check if class stores Ptr* instead of T* in nativeObj field + ''' + if ci.smart != None: + return ci.smart + + # if parents are smart (we hope) then children are! + # if not we believe the class is smart if it has "create" method + ci.smart = False + if ci.base or ci.name == 'Algorithm': + ci.smart = True + else: + for fi in ci.methods: + if fi.name == "create": + ci.smart = True + break + + return ci.smart + + def smartWrap(self, ci, fullname): + ''' + Wraps fullname with Ptr<> if needed + ''' + if self.isSmartClass(ci): + return "Ptr<" + fullname + ">" + return fullname + + def finalize(self, output_objc_path): + opencv_header_file = os.path.join(output_objc_path, framework_name + ".h") + self.save(opencv_header_file, '\n'.join(['#import "%s"' % os.path.basename(f) for f in self.header_files if os.path.basename(f) != "CVObjcUtil.h"])) + cmakelist_template = read_contents(os.path.join(SCRIPT_DIR, 'templates/cmakelists.template')) + cmakelist = Template(cmakelist_template).substitute(modules = ";".join(modules), framework = framework_name) + self.save(os.path.join(dstdir, "CMakeLists.txt"), cmakelist) + mkdir_p("./framework_build") + mkdir_p("./test_build") + mkdir_p("./doc_build") + copyfile(os.path.join(SCRIPT_DIR, '../doc/README.md'), "./doc_build/README.md") + if framework_name != "OpenCV": + for dirname, dirs, files in os.walk(os.path.join(testdir, "test")): + for filename in files: + filepath = os.path.join(dirname, filename) + with open(filepath) as file: + body = file.read() + body = body.replace("import OpenCV", "import " + framework_name) + body = body.replace("#import ", "#import <" + framework_name + "/" + framework_name + ".h>") + with open(filepath, "w") as file: + file.write(body) + + +def copy_objc_files(objc_files_dir, objc_base_path, module_path, include = False): + global total_files, updated_files + objc_files = [] + re_filter = re.compile(r'^.+\.(h|m|mm|swift)$') + for root, dirnames, filenames in os.walk(objc_files_dir): + objc_files += [os.path.join(root, filename) for filename in filenames if re_filter.match(filename)] + objc_files = [f.replace('\\', '/') for f in objc_files] + + re_prefix = re.compile(r'^.+/(.+)\.(h|m|mm|swift)$') + for objc_file in objc_files: + src = objc_file + m = re_prefix.match(objc_file) + target_fname = (m.group(1) + '.' + m.group(2)) if m else os.path.basename(objc_file) + dest = os.path.join(objc_base_path, os.path.join(module_path, target_fname)) + mkdir_p(os.path.dirname(dest)) + total_files += 1 + if include and m.group(2) == 'h': + generator.header_files.append(dest) + if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1): + copyfile(src, dest) + updated_files += 1 + +def unescape(str): + return str.replace("<", "<").replace(">", ">").replace("&", "&") + +def escape_underscore(str): + return str.replace('_', '\\_') + +def escape_texttt(str): + return re.sub(re.compile('texttt{(.*?)\}', re.DOTALL), lambda x: 'texttt{' + escape_underscore(x.group(1)) + '}', str) + +def get_macros(tex): + out = "" + if re.search("\\\\fork\s*{", tex): + out += "\\newcommand{\\fork}[4]{ \\left\\{ \\begin{array}{l l} #1 & \\text{#2}\\\\\\\\ #3 & \\text{#4}\\\\\\\\ \\end{array} \\right.} " + if re.search("\\\\vecthreethree\s*{", tex): + out += "\\newcommand{\\vecthreethree}[9]{ \\begin{bmatrix} #1 & #2 & #3\\\\\\\\ #4 & #5 & #6\\\\\\\\ #7 & #8 & #9 \\end{bmatrix} } " + return out + +def fix_tex(tex): + macros = get_macros(tex) + fix_escaping = escape_texttt(unescape(tex)) + return macros + fix_escaping + +def sanitize_documentation_string(doc, type): + if type == "class": + doc = doc.replace("@param ", "") + + doc = re.sub(re.compile('`\\$\\$(.*?)\\$\\$`', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc) + doc = re.sub(re.compile('\\\\f\\{align\\*\\}\\{?(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$\\begin{aligned} ' + fix_tex(x.group(1)) + ' \\end{aligned}$$`', doc) + doc = re.sub(re.compile('\\\\f\\{equation\\*\\}\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$\\begin{aligned} ' + fix_tex(x.group(1)) + ' \\end{aligned}$$`', doc) + doc = re.sub(re.compile('\\\\f\\$(.*?)\\\\f\\$', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc) + doc = re.sub(re.compile('\\\\f\\[(.*?)\\\\f\\]', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc) + doc = re.sub(re.compile('\\\\f\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc) + + doc = doc.replace("@anchor", "") \ + .replace("@brief ", "").replace("\\brief ", "") \ + .replace("@cite", "CITE:") \ + .replace("@code{.cpp}", "") \ + .replace("@code{.txt}", "") \ + .replace("@code", "") \ + .replace("@copydoc", "") \ + .replace("@copybrief", "") \ + .replace("@date", "") \ + .replace("@defgroup", "") \ + .replace("@details ", "") \ + .replace("@endcode", "") \ + .replace("@endinternal", "") \ + .replace("@file", "") \ + .replace("@include", "INCLUDE:") \ + .replace("@ingroup", "") \ + .replace("@internal", "") \ + .replace("@overload", "") \ + .replace("@param[in]", "@param") \ + .replace("@param[out]", "@param") \ + .replace("@ref", "REF:") \ + .replace("@returns", "@return") \ + .replace("@sa ", "@see ") \ + .replace("@snippet", "SNIPPET:") \ + .replace("@todo", "TODO:") \ + + lines = doc.splitlines() + + in_code = False + for i,line in enumerate(lines): + if line.find("") != -1: + in_code = False + lines[i] = line.replace("", "") + if in_code: + lines[i] = unescape(line) + if line.find("") != -1: + in_code = True + lines[i] = line.replace("", "") + + lines = list(map(lambda x: x[x.find('*'):].strip() if x.lstrip().startswith("*") else x, lines)) + lines = list(map(lambda x: "* " + x[1:].strip() if x.startswith("*") and x != "*" else x, lines)) + lines = list(map(lambda x: x if x.startswith("*") else "* " + x if x and x != "*" else "*", lines)) + + hasValues = False + for line in lines: + if line != "*": + hasValues = True + break + return "/**\n " + "\n ".join(lines) + "\n */" if hasValues else "" + +if __name__ == "__main__": + # initialize logger + logging.basicConfig(filename='gen_objc.log', format=None, filemode='w', level=logging.INFO) + handler = logging.StreamHandler() + handler.setLevel(logging.WARNING) + logging.getLogger().addHandler(handler) + + # parse command line parameters + import argparse + arg_parser = argparse.ArgumentParser(description='OpenCV Objective-C Wrapper Generator') + arg_parser.add_argument('-p', '--parser', required=True, help='OpenCV header parser') + arg_parser.add_argument('-c', '--config', required=True, help='OpenCV modules config') + arg_parser.add_argument('-t', '--target', required=True, help='Target (either ios or osx)') + arg_parser.add_argument('-f', '--framework', required=True, help='Framework name') + + args=arg_parser.parse_args() + + # import header parser + hdr_parser_path = os.path.abspath(args.parser) + if hdr_parser_path.endswith(".py"): + hdr_parser_path = os.path.dirname(hdr_parser_path) + sys.path.append(hdr_parser_path) + import hdr_parser + + with open(args.config) as f: + config = json.load(f) + + ROOT_DIR = config['rootdir']; assert os.path.exists(ROOT_DIR) + + dstdir = "./gen" + testdir = "./test" + objc_base_path = os.path.join(dstdir, 'objc'); mkdir_p(objc_base_path) + objc_test_base_path = testdir; mkdir_p(objc_test_base_path) + copy_objc_files(os.path.join(SCRIPT_DIR, '../test/test'), objc_test_base_path, 'test', False) + copy_objc_files(os.path.join(SCRIPT_DIR, '../test/dummy'), objc_test_base_path, 'dummy', False) + copyfile(os.path.join(SCRIPT_DIR, '../test/cmakelists.template'), os.path.join(objc_test_base_path, 'CMakeLists.txt')) + + # launch Objective-C Wrapper generator + generator = ObjectiveCWrapperGenerator() + + gen_dict_files = [] + modules = [] + framework_name = args.framework + + print("Objective-C: Processing OpenCV modules: %d" % len(config['modules'])) + for e in config['modules']: + (module, module_location) = (e['name'], os.path.join(ROOT_DIR, e['location'])) + logging.info("\n=== MODULE: %s (%s) ===\n" % (module, module_location)) + modules.append(module) + + module_imports = [] + srcfiles = [] + common_headers = [] + + misc_location = os.path.join(module_location, 'misc/objc') + + srcfiles_fname = os.path.join(misc_location, 'filelist') + if os.path.exists(srcfiles_fname): + with open(srcfiles_fname) as f: + srcfiles = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()] + else: + re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.details.hpp$|_winrt.hpp$|/cuda/|/legacy/)') + # .h files before .hpp + h_files = [] + hpp_files = [] + for root, dirnames, filenames in os.walk(os.path.join(module_location, 'include')): + h_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.h')] + hpp_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.hpp')] + srcfiles = h_files + hpp_files + srcfiles = [f for f in srcfiles if not re_bad.search(f.replace('\\', '/'))] + logging.info("\nFiles (%d):\n%s", len(srcfiles), pformat(srcfiles)) + + common_headers_fname = os.path.join(misc_location, 'filelist_common') + if os.path.exists(common_headers_fname): + with open(common_headers_fname) as f: + common_headers = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()] + logging.info("\nCommon headers (%d):\n%s", len(common_headers), pformat(common_headers)) + + gendict_fname = os.path.join(misc_location, 'gen_dict.json') + if os.path.exists(gendict_fname): + with open(gendict_fname) as f: + gen_type_dict = json.load(f) + class_ignore_list += gen_type_dict.get("class_ignore_list", []) + const_ignore_list += gen_type_dict.get("const_ignore_list", []) + const_private_list += gen_type_dict.get("const_private_list", []) + missing_consts.update(gen_type_dict.get("missing_consts", {})) + type_dict.update(gen_type_dict.get("type_dict", {})) + AdditionalImports.update(gen_type_dict.get("AdditionalImports", {})) + ManualFuncs.update(gen_type_dict.get("ManualFuncs", {})) + func_arg_fix.update(gen_type_dict.get("func_arg_fix", {})) + enum_fix.update(gen_type_dict.get("enum_fix", {})) + namespaces_dict.update(gen_type_dict.get("namespaces_dict", {})) + module_imports += gen_type_dict.get("module_imports", []) + + objc_files_dir = os.path.join(misc_location, 'common') + if os.path.exists(objc_files_dir): + copy_objc_files(objc_files_dir, objc_base_path, module, True) + if args.target == 'ios': + ios_files_dir = os.path.join(misc_location, 'ios') + if os.path.exists(ios_files_dir): + copy_objc_files(ios_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) + objc_test_resources_dir = os.path.join(objc_test_files_dir, 'resources') + if os.path.exists(objc_test_resources_dir): + copy_tree(objc_test_resources_dir, os.path.join(objc_test_base_path, 'test', 'resources')) + + if len(srcfiles) > 0: + generator.gen(srcfiles, module, dstdir, objc_base_path, common_headers) + else: + logging.info("No generated code for module: %s", module) + generator.finalize(objc_base_path) + + print('Generated files: %d (updated %d)' % (total_files, updated_files)) diff --git a/modules/objc/generator/templates/cmakelists.template b/modules/objc/generator/templates/cmakelists.template new file mode 100644 index 0000000000..d6fab2cede --- /dev/null +++ b/modules/objc/generator/templates/cmakelists.template @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.15) + +project($framework) + +set(MODULES "$modules") + +# Enable C++11 +set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD_REQUIRED TRUE) + +set (OBJC_COMPILE_FLAGS "-fobjc-arc -fobjc-weak -fvisibility=hidden -D__OPENCV_BUILD=1") +set (SUPPRESS_WARNINGS_FLAGS "-Wno-incomplete-umbrella") +set (CMAKE_CXX_FLAGS "$${CMAKE_CXX_FLAGS} $${OBJC_COMPILE_FLAGS} $${SUPPRESS_WARNINGS_FLAGS}") + +# grab the files +file(GLOB_RECURSE objc_sources "objc/*\.h" "objc/*\.m" "objc/*\.mm" "objc/*\.swift") +file(GLOB_RECURSE objc_headers "*\.h") + +add_library(opencv_objc_framework STATIC $${objc_sources}) + +set_target_properties(opencv_objc_framework PROPERTIES LINKER_LANGUAGE CXX) + +target_include_directories(opencv_objc_framework PRIVATE "$${BUILD_ROOT}") +target_include_directories(opencv_objc_framework PRIVATE "$${BUILD_ROOT}/install/include") +target_include_directories(opencv_objc_framework PRIVATE "$${BUILD_ROOT}/install/include/opencv2") +foreach(m $${MODULES}) + target_include_directories(opencv_objc_framework PRIVATE "$${BUILD_ROOT}/modules/objc/gen/objc/$${m}") +endforeach() + +install(TARGETS opencv_objc_framework LIBRARY DESTINATION lib) + +enable_language(Swift) + +# Additional target properties +set_target_properties(opencv_objc_framework PROPERTIES + OUTPUT_NAME "$framework" + ARCHIVE_OUTPUT_DIRECTORY "$${BUILD_ROOT}/lib" + XCODE_ATTRIBUTE_SWIFT_VERSION 5.0 + XCODE_ATTRIBUTE_OTHER_SWIFT_FLAGS "-Xcc $${SUPPRESS_WARNINGS_FLAGS}" + FRAMEWORK TRUE + MACOSX_FRAMEWORK_IDENTIFIER org.opencv.$framework + PUBLIC_HEADER "$${objc_headers}" + DEFINE_SYMBOL CVAPI_EXPORTS + ) + +find_program(JAZZY jazzy) + +if(JAZZY) + add_custom_command( + OUTPUT "$${BUILD_ROOT}/modules/objc/doc_build/doc/index.html" + COMMAND $${JAZZY} --objc --author OpenCV --author_url http://opencv.org --github_url https://github.com/opencv/opencv --umbrella-header "$${BUILD_ROOT}/lib/$${CMAKE_BUILD_TYPE}/$framework.framework/Headers/$framework.h" --framework-root "$${BUILD_ROOT}/lib/$${CMAKE_BUILD_TYPE}/$framework.framework" --module $framework --sdk iphonesimulator --undocumented-text \"\" + WORKING_DIRECTORY "$${BUILD_ROOT}/modules/objc/doc_build" + COMMENT "Generating Documentation" + ) + add_custom_target(opencv_objc_doc ALL DEPENDS "$${BUILD_ROOT}/modules/objc/doc_build/doc/index.html") + add_dependencies(opencv_objc_doc opencv_objc_framework) +else() + message("jazzy not found - documentation will not be generated!") +endif() diff --git a/modules/objc/generator/templates/objc_class_body.template b/modules/objc/generator/templates/objc_class_body.template new file mode 100644 index 0000000000..7db209da1b --- /dev/null +++ b/modules/objc/generator/templates/objc_class_body.template @@ -0,0 +1,16 @@ +// +// This file is auto-generated. Please don't modify it! +// + +#import "$objcName.h" +#import "CVObjcUtil.h" + +$imports + +@implementation $objcName + +$nativePointerHandling + +$methodImplementations + +@end diff --git a/modules/objc/generator/templates/objc_class_header.template b/modules/objc/generator/templates/objc_class_header.template new file mode 100644 index 0000000000..fae6b48dd6 --- /dev/null +++ b/modules/objc/generator/templates/objc_class_header.template @@ -0,0 +1,29 @@ +// +// This file is auto-generated. Please don't modify it! +// +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +$additionalImports +#endif + +#import +$importBaseClass + +$forwardDeclarations + +$enumDeclarations + +NS_ASSUME_NONNULL_BEGIN + +$docs +@interface $objcName : $base + +$nativePointerHandling + +$methodDeclarations + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/objc/generator/templates/objc_module_body.template b/modules/objc/generator/templates/objc_module_body.template new file mode 100644 index 0000000000..a11d2404f3 --- /dev/null +++ b/modules/objc/generator/templates/objc_module_body.template @@ -0,0 +1,14 @@ +// +// This file is auto-generated. Please don't modify it! +// + +#import "$module.h" +#import "CVObjcUtil.h" + +$imports + +@implementation $module + +$methodImplementations + +@end diff --git a/modules/objc/generator/templates/objc_module_header.template b/modules/objc/generator/templates/objc_module_header.template new file mode 100644 index 0000000000..9c7f14bdf1 --- /dev/null +++ b/modules/objc/generator/templates/objc_module_header.template @@ -0,0 +1,26 @@ +// +// This file is auto-generated. Please don't modify it! +// +#pragma once + +#ifdef __cplusplus +#import "opencv.hpp" +$additionalImports +#endif + +#import + +$forwardDeclarations + +$enumDeclarations + +NS_ASSUME_NONNULL_BEGIN + +$docs +@interface $module : $base + +$methodDeclarations + +@end + +NS_ASSUME_NONNULL_END diff --git a/modules/objc/test/cmakelists.template b/modules/objc/test/cmakelists.template new file mode 100644 index 0000000000..48578645b8 --- /dev/null +++ b/modules/objc/test/cmakelists.template @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.15) + +project(OpenCVTest) + +enable_testing() + +find_package(XCTest REQUIRED) + +# Enable C++11 +set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD_REQUIRED TRUE) + +set (OBJC_COMPILE_FLAGS "-fobjc-arc -fobjc-weak -fvisibility=hidden") +set (SUPPRESS_WARNINGS_FLAGS "-Wno-incomplete-umbrella") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OBJC_COMPILE_FLAGS} ${SUPPRESS_WARNINGS_FLAGS}") +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OBJC_ARC_COMPILE_FLAGS} ${SUPPRESS_WARNINGS_FLAGS}") + +# grab the files +file(GLOB_RECURSE test_sources "test/*\.h" "test/*\.m" "test/*\.mm" "test/*\.swift") + +add_library(OpenCVTest STATIC dummy/dummy.mm) + +enable_language(Swift) + +# XCTest for Framework +xctest_add_bundle(OpenCVTestTests OpenCVTest ${test_sources} test/resources ${FRAMEWORK_DIR}/${FRAMEWORK_NAME}.framework) +set_target_properties(OpenCVTestTests PROPERTIES + XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "${FRAMEWORK_DIR}" + XCODE_ATTRIBUTE_OTHER_SWIFT_FLAGS "-Xcc ${SUPPRESS_WARNINGS_FLAGS}" + ) + +# link necessary Frameworks +target_link_libraries(OpenCVTestTests PRIVATE "-framework Accelerate") +target_link_libraries(OpenCVTestTests PRIVATE "-framework AVFoundation") +target_link_libraries(OpenCVTestTests PRIVATE "-framework CoreMedia") +if (NOT DEFINED IOS_ARCH) + target_link_libraries(OpenCVTestTests PRIVATE "-framework OpenCL") +endif() + +# its OK to ignore stuff we don't know about since there will be no tests relying on it +set_target_properties(OpenCVTestTests PROPERTIES LINK_FLAGS -Wl,-undefined,dynamic_lookup) + +set_source_files_properties(test/resources PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + +xctest_add_test(XCTest.OpenCVTest OpenCVTestTests) diff --git a/modules/objc/test/dummy/dummy.mm b/modules/objc/test/dummy/dummy.mm new file mode 100644 index 0000000000..a35e92fe25 --- /dev/null +++ b/modules/objc/test/dummy/dummy.mm @@ -0,0 +1,8 @@ +// +// dummy.mm +// +// Created by Giles Payne on 2019/10/13. +// + +void dummy() { +} diff --git a/modules/objc/test/test/OpenCVTestCase.swift b/modules/objc/test/test/OpenCVTestCase.swift new file mode 100644 index 0000000000..8712fd18aa --- /dev/null +++ b/modules/objc/test/test/OpenCVTestCase.swift @@ -0,0 +1,226 @@ +// +// OpenCVTestCase.swift +// +// Created by Giles Payne on 2020/01/19. +// + +import XCTest +import OpenCV + +enum OpenCVTestError: Error { + case unsupportedOperationError(String) +} + +open class OpenCVTestCase: XCTestCase { + + //change to 'true' to unblock fail on fail("Not yet implemented") + static let passNYI = true + + static let isTestCaseEnabled = true + + static let XFEATURES2D = "xfeatures2d" + static let DEFAULT_FACTORY = "create" + + static let matSize: Int32 = 10 + static let EPS = 0.001 + static let FEPS:Float = 0.001 + static let weakEPS = 0.5 + static let weakFEPS:Float = 0.5 + + var dst: Mat = Mat() + var truth: Mat? = nil + + let colorBlack = Scalar(0) + let colorWhite = Scalar(255, 255, 255) + + // Naming notation: _[depth]_[dimensions]_value + // examples: gray0 - single channel 8U 2d Mat filled with 0 + // grayRnd - single channel 8U 2d Mat filled with random numbers + // gray0_32f_1d + + let gray0 = Mat(rows:matSize, cols:matSize, type:CvType.CV_8U, scalar:Scalar(0)) + let gray1 = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U, scalar: Scalar(1)) + let gray2 = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U, scalar: Scalar(2)) + let gray3 = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U, scalar: Scalar(3)) + let gray9 = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U, scalar: Scalar(9)) + let gray127 = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U, scalar: Scalar(127)) + let gray128 = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U, scalar: Scalar(128)) + let gray255 = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U, scalar: Scalar(255)) + let grayRnd = Mat(rows:matSize, cols:matSize, type: CvType.CV_8U) + + let gray_16u_256 = Mat(rows: matSize, cols: matSize, type: CvType.CV_16U, scalar: Scalar(256)) + let gray_16s_1024 = Mat(rows: matSize, cols: matSize, type: CvType.CV_16S, scalar: Scalar(1024)) + + let gray0_32f = Mat(rows: matSize, cols: matSize, type: CvType.CV_32F, scalar: Scalar(0.0)) + let gray1_32f = Mat(rows: matSize, cols: matSize, type: CvType.CV_32F, scalar: Scalar(1.0)) + let gray3_32f = Mat(rows: matSize, cols: matSize, type: CvType.CV_32F, scalar: Scalar(3.0)) + let gray9_32f = Mat(rows: matSize, cols: matSize, type: CvType.CV_32F, scalar: Scalar(9.0)) + let gray255_32f = Mat(rows: matSize, cols: matSize, type: CvType.CV_32F, scalar: Scalar(255.0)) + let grayE_32f = Mat.eye(rows: matSize, cols: matSize, type: CvType.CV_32FC1) + let grayRnd_32f = Mat(rows: matSize, cols: matSize, type: CvType.CV_32F) + + let gray0_64f = Mat(rows: matSize, cols: matSize, type: CvType.CV_64F, scalar: Scalar(0.0)) + let gray0_32f_1d = Mat(rows: 1, cols: matSize, type: CvType.CV_32F, scalar: Scalar(0.0)) + let gray0_64f_1d = Mat(rows: 1, cols: matSize, type: CvType.CV_64F, scalar: Scalar(0.0)) + + let rgba0 = Mat(rows: matSize, cols: matSize, type: CvType.CV_8UC4, scalar: Scalar.all(0)) + let rgba128 = Mat(rows: matSize, cols: matSize, type: CvType.CV_8UC4, scalar: Scalar.all(128)) + + let rgbLena: Mat = { + return Imgcodecs.imread(filename: Bundle(for: OpenCVTestCase.self).path(forResource:"lena", ofType:"png", inDirectory:"resources")!) + }() + + let grayChess: Mat = { + return Imgcodecs.imread(filename: Bundle(for: OpenCVTestCase.self).path(forResource:"chessboard", ofType:"jpg", inDirectory:"resources")!, flags: ImreadModes.IMREAD_GRAYSCALE.rawValue) + }() + + let gray255_32f_3d = Mat(sizes: [matSize, matSize, matSize] as [NSNumber], type: CvType.CV_32F, scalar: Scalar(255.0)) + + let v1 = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + let v2 = Mat(rows: 1, cols: 3, type: CvType.CV_32F) + + override open func setUp() { + //Core.setErrorVerbosity(false) + Core.randu(dst: grayRnd, low: 0, high: 255) + Core.randu(dst: grayRnd_32f, low:0, high: 255) + do { + try v1.put(row: 0,col: 0, data: [1.0, 3.0, 2.0]) + try v2.put(row: 0,col: 0, data: [2.0, 1.0, 3.0]) + } catch { + print("Fatal error in start-up") + } + } + + override open func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func assertMatEqual(_ expected:Mat, _ actual:Mat, file: StaticString = #file, line: UInt = #line) throws { + try compareMats(expected, actual, true, file:file, line:line) + } + + func assertMatNotEqual(_ expected:Mat, _ actual:Mat, file: StaticString = #file, line: UInt = #line) throws { + try compareMats(expected, actual, false, file:file, line:line) + } + + func assertMatEqual(_ expected:Mat, _ actual:Mat, _ eps:Double, file: StaticString = #file, line: UInt = #line) throws { + try compareMats(expected, actual, eps, true, file:file, line:line); + } + + func assertMatNotEqual(_ expected:Mat, _ actual:Mat, _ eps:Double, file: StaticString = #file, line: UInt = #line) throws { + try compareMats(expected, actual, eps, false, file:file, line:line); + } + + func assertSizeEquals(_ expected:Size,_ actual: Size, file: StaticString = #file, line: UInt = #line) { + let msg = "expected:<\(expected)> but was:<\(actual)>" + XCTAssertEqual(expected.width, actual.width, msg, file:file, line:line) + XCTAssertEqual(expected.height, actual.height, msg, file:file, line:line) + } + + func assertSize2fEquals(_ expected:Size2f,_ actual: Size2f,_ eps: Float, file: StaticString = #file, line: UInt = #line) { + let msg = "expected:<\(expected)> but was:<\(actual)>" + XCTAssertEqual(expected.width, actual.width, accuracy:eps, msg, file:file, line:line) + XCTAssertEqual(expected.height, actual.height, accuracy:eps, msg, file:file, line:line) + } + + func assertSize2dEquals(_ expected:Size2d,_ actual: Size2d,_ eps: Double, file: StaticString = #file, line: UInt = #line) { + let msg = "expected:<\(expected)> but was:<\(actual)>" + XCTAssertEqual(expected.width, actual.width, accuracy:eps, msg, file:file, line:line) + XCTAssertEqual(expected.height, actual.height, accuracy:eps, msg, file:file, line:line) + } + + func assertPointEquals(_ expected:Point, _ actual: Point, file: StaticString = #file, line: UInt = #line) { + let msg = "expected:<\(expected)> but was:<\(actual)>" + XCTAssertEqual(expected.x, actual.x, msg, file:file, line:line) + XCTAssertEqual(expected.y, actual.y, msg, file:file, line:line) + } + + func assertPoint2dEquals(_ expected:Point2d, _ actual: Point2d, _ eps: Double, file: StaticString = #file, line: UInt = #line) { + let msg = "expected:<\(expected)> but was:<\(actual)>" + XCTAssertEqual(expected.x, actual.x, accuracy:eps, msg, file:file, line:line) + XCTAssertEqual(expected.y, actual.y, accuracy:eps, msg, file:file, line:line) + } + + func assertPoint2fEquals(_ expected:Point2f, _ actual: Point2f, _ eps: Float, file: StaticString = #file, line: UInt = #line) { + let msg = "expected:<\(expected)> but was:<\(actual)>" + XCTAssertEqual(expected.x, actual.x, accuracy:eps, msg, file:file, line:line) + XCTAssertEqual(expected.y, actual.y, accuracy:eps, msg, file:file, line:line) + } + + func assertScalarEqual(_ expected:Scalar, _ actual:Scalar, _ eps: Double, file: StaticString = #file, line: UInt = #line) { + let msg = "expected:<\(expected)> but was:<\(actual)>" + XCTAssertEqual(expected.val[0] as! Double, actual.val[0] as! Double, accuracy:eps, msg, file:file, line:line) + XCTAssertEqual(expected.val[1] as! Double, actual.val[1] as! Double, accuracy:eps, msg, file:file, line:line) + XCTAssertEqual(expected.val[2] as! Double, actual.val[2] as! Double, accuracy:eps, msg, file:file, line:line) + XCTAssertEqual(expected.val[3] as! Double, actual.val[3] as! Double, accuracy:eps, msg, file:file, line:line) + } + + func assertArrayEquals(_ expected:[NSNumber], _ actual:[NSNumber], _ eps: Double, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(expected.count, actual.count, "Arrays have different sizes.", file:file, line:line) + + for i in 0.. Bool { + if expected.dims() != actual.dims() { + return false + } + if expected.dims() > 2 { + return (0.. Mat { + return Mat(rows:OpenCVTestCase.matSize, cols:OpenCVTestCase.matSize, type:type, scalar:Scalar(vals: vals)) + } + + func makeMask(_ mat:Mat, vals:[Double] = []) -> Mat { + mat.submat(rowStart: 0, rowEnd: mat.rows(), colStart: 0, colEnd: mat.cols() / 2).setTo(scalar: Scalar(vals: vals as [NSNumber])) + return mat + } +} diff --git a/modules/objdetect/CMakeLists.txt b/modules/objdetect/CMakeLists.txt index 414e578099..3fa0c5d33b 100644 --- a/modules/objdetect/CMakeLists.txt +++ b/modules/objdetect/CMakeLists.txt @@ -1,5 +1,5 @@ set(the_description "Object Detection") -ocv_define_module(objdetect opencv_core opencv_imgproc opencv_calib3d WRAP java python js) +ocv_define_module(objdetect opencv_core opencv_imgproc opencv_calib3d WRAP java objc python js) if(HAVE_QUIRC) get_property(QUIRC_INCLUDE GLOBAL PROPERTY QUIRC_INCLUDE_DIR) diff --git a/modules/photo/CMakeLists.txt b/modules/photo/CMakeLists.txt index 15de722f15..34e3741601 100644 --- a/modules/photo/CMakeLists.txt +++ b/modules/photo/CMakeLists.txt @@ -4,4 +4,4 @@ if(HAVE_CUDA) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wmissing-declarations -Wshadow) endif() -ocv_define_module(photo opencv_imgproc OPTIONAL opencv_cudaarithm opencv_cudaimgproc WRAP java python js) +ocv_define_module(photo opencv_imgproc OPTIONAL opencv_cudaarithm opencv_cudaimgproc WRAP java objc python js) diff --git a/modules/photo/misc/objc/gen_dict.json b/modules/photo/misc/objc/gen_dict.json new file mode 100644 index 0000000000..bcec59f0e1 --- /dev/null +++ b/modules/photo/misc/objc/gen_dict.json @@ -0,0 +1,6 @@ +{ + "func_arg_fix" : { + "(void)fastNlMeansDenoising:(Mat*)src dst:(Mat*)dst h:(FloatVector*)h templateWindowSize:(int)templateWindowSize searchWindowSize:(int)searchWindowSize normType:(int)normType" : { "h" : { "name" : "hVector" } }, + "(void)fastNlMeansDenoisingMulti:(NSArray*)srcImgs dst:(Mat*)dst imgToDenoiseIndex:(int)imgToDenoiseIndex temporalWindowSize:(int)temporalWindowSize h:(FloatVector*)h templateWindowSize:(int)templateWindowSize searchWindowSize:(int)searchWindowSize normType:(int)normType" : { "h" : { "name" : "hVector" } } + } +} diff --git a/modules/video/CMakeLists.txt b/modules/video/CMakeLists.txt index 015c95ca96..e25f0b7e0e 100644 --- a/modules/video/CMakeLists.txt +++ b/modules/video/CMakeLists.txt @@ -1,2 +1,2 @@ set(the_description "Video Analysis") -ocv_define_module(video opencv_imgproc OPTIONAL opencv_calib3d WRAP java python js) +ocv_define_module(video opencv_imgproc OPTIONAL opencv_calib3d WRAP java objc python js) diff --git a/modules/videoio/CMakeLists.txt b/modules/videoio/CMakeLists.txt index bf82bb0eb1..12ff992294 100644 --- a/modules/videoio/CMakeLists.txt +++ b/modules/videoio/CMakeLists.txt @@ -10,7 +10,7 @@ if(NOT TARGET opencv_videoio_plugins) add_custom_target(opencv_videoio_plugins ALL) endif() -ocv_add_module(videoio opencv_imgproc opencv_imgcodecs WRAP java python) +ocv_add_module(videoio opencv_imgproc opencv_imgcodecs WRAP java objc python) set(videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/precomp.hpp) diff --git a/modules/videoio/misc/objc/gen_dict.json b/modules/videoio/misc/objc/gen_dict.json new file mode 100644 index 0000000000..902856b0fe --- /dev/null +++ b/modules/videoio/misc/objc/gen_dict.json @@ -0,0 +1,17 @@ +{ + "AdditionalImports" : { + "Videoio" : + [ "\"videoio/registry.hpp\"" ] + }, + "ManualFuncs" : { + "VideoCapture" : { + "release" : {"declaration" : [""], "implementation" : [""] } + }, + "VideoWriter" : { + "release" : {"declaration" : [""], "implementation" : [""] } + } + }, + "func_arg_fix" : { + "(BOOL)open:(int)index apiPreference:(int)apiPreference" : { "open" : {"name" : "openWithIndex"} } + } +} diff --git a/modules/videoio/misc/objc/ios/CvAbstractCamera2.mm b/modules/videoio/misc/objc/ios/CvAbstractCamera2.mm new file mode 100644 index 0000000000..0aebc13e82 --- /dev/null +++ b/modules/videoio/misc/objc/ios/CvAbstractCamera2.mm @@ -0,0 +1,445 @@ +// +// CvAbstractCamera2.mm +// +// Created by Giles Payne on 2020/04/01. +// + +#import "CvCamera2.h" + +#pragma mark - Private Interface + +@interface CvAbstractCamera2 () + +@property (nonatomic, strong) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer; + +- (void)deviceOrientationDidChange:(NSNotification*)notification; +- (void)startCaptureSession; + +- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition; + +- (void)updateSize; + +@end + + +#pragma mark - Implementation + + +@implementation CvAbstractCamera2 + +#pragma mark - Constructors + +- (id)init; +{ + self = [super init]; + if (self) { + // react to device orientation notifications + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deviceOrientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + self.currentDeviceOrientation = [[UIDevice currentDevice] orientation]; + + + // check if camera available + self.cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; + NSLog(@"camera available: %@", (self.cameraAvailable ? @"YES" : @"NO") ); + + _running = NO; + + // set camera default configuration + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront; + self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft; + self.defaultFPS = 15; + self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288; + + self.parentView = nil; + self.useAVCaptureVideoPreviewLayer = NO; + } + return self; +} + + + +- (id)initWithParentView:(UIView*)parent; +{ + self = [super init]; + if (self) { + // react to device orientation notifications + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deviceOrientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + self.currentDeviceOrientation = [[UIDevice currentDevice] orientation]; + + + // check if camera available + self.cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; + NSLog(@"camera available: %@", (self.cameraAvailable ? @"YES" : @"NO") ); + + _running = NO; + + // set camera default configuration + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront; + self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft; + self.defaultFPS = 15; + self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480; + + self.parentView = parent; + self.useAVCaptureVideoPreviewLayer = YES; + } + return self; +} + +- (void)dealloc; +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; +} + +#pragma mark - Public interface + + +- (void)start; +{ + if (![NSThread isMainThread]) { + NSLog(@"[Camera] Warning: Call start only from main thread"); + [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO]; + return; + } + + if (self.running == YES) { + return; + } + _running = YES; + + // TODO: update image size data before actually starting (needed for recording) + [self updateSize]; + + if (self.cameraAvailable) { + [self startCaptureSession]; + } +} + + +- (void)pause; +{ + _running = NO; + [self.captureSession stopRunning]; +} + + + +- (void)stop; +{ + _running = NO; + + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; + if (self.captureSession) { + for (AVCaptureInput *input in self.captureSession.inputs) { + [self.captureSession removeInput:input]; + } + + for (AVCaptureOutput *output in self.captureSession.outputs) { + [self.captureSession removeOutput:output]; + } + + [self.captureSession stopRunning]; + } + + _captureSessionLoaded = NO; +} + + + +// use front/back camera +- (void)switchCameras; +{ + BOOL was_running = self.running; + if (was_running) { + [self stop]; + } + if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) { + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; + } else { + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront; + } + if (was_running) { + [self start]; + } +} + + + +#pragma mark - Device Orientation Changes + + +- (void)deviceOrientationDidChange:(NSNotification*)notification +{ + (void)notification; + UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; + + switch (orientation) + { + case UIDeviceOrientationPortrait: + case UIDeviceOrientationPortraitUpsideDown: + case UIDeviceOrientationLandscapeLeft: + case UIDeviceOrientationLandscapeRight: + self.currentDeviceOrientation = orientation; + break; + + case UIDeviceOrientationFaceUp: + case UIDeviceOrientationFaceDown: + default: + break; + } + NSLog(@"deviceOrientationDidChange: %d", (int)orientation); + + [self updateOrientation]; +} + + + +#pragma mark - Private Interface + +- (void)createCaptureSession; +{ + // set a av capture session preset + self.captureSession = [[AVCaptureSession alloc] init]; + if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) { + [self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset]; + } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) { + [self.captureSession setSessionPreset:AVCaptureSessionPresetLow]; + } else { + NSLog(@"[Camera] Error: could not set session preset"); + } +} + +- (void)createCaptureDevice; +{ + // setup the device + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + [self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition]; + NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO"); + NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front"); +} + + +- (void)createVideoPreviewLayer; +{ + self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; + + if ([self.captureVideoPreviewLayer.connection isVideoOrientationSupported]) + { + [self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation]; + } + + if (self.parentView != nil) { + self.captureVideoPreviewLayer.frame = self.parentView.bounds; + self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + [self.parentView.layer addSublayer:self.captureVideoPreviewLayer]; + } + NSLog(@"[Camera] created AVCaptureVideoPreviewLayer"); +} + +- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition; +{ + for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + if ([device position] == desiredPosition) { + [self.captureSession beginConfiguration]; + + NSError* error = nil; + AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; + if (!input) { + NSLog(@"error creating input %@", [error description]); + } + + // support for autofocus + if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { + error = nil; + if ([device lockForConfiguration:&error]) { + device.focusMode = AVCaptureFocusModeContinuousAutoFocus; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for autofocus configuration %@", [error description]); + } + } + [self.captureSession addInput:input]; + + for (AVCaptureInput *oldInput in self.captureSession.inputs) { + [self.captureSession removeInput:oldInput]; + } + [self.captureSession addInput:input]; + [self.captureSession commitConfiguration]; + + break; + } + } +} + + + +- (void)startCaptureSession +{ + if (!self.cameraAvailable) { + return; + } + + if (self.captureSessionLoaded == NO) { + [self createCaptureSession]; + [self createCaptureDevice]; + [self createCaptureOutput]; + + // setup preview layer + if (self.useAVCaptureVideoPreviewLayer) { + [self createVideoPreviewLayer]; + } else { + [self createCustomVideoPreview]; + } + + _captureSessionLoaded = YES; + } + + [self.captureSession startRunning]; +} + + +- (void)createCaptureOutput; +{ + [NSException raise:NSInternalInconsistencyException + format:@"You must override %s in a subclass", __FUNCTION__]; +} + +- (void)createCustomVideoPreview; +{ + [NSException raise:NSInternalInconsistencyException + format:@"You must override %s in a subclass", __FUNCTION__]; +} + +- (void)updateOrientation; +{ + // nothing to do here +} + + +- (void)updateSize; +{ + if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) { + self.imageWidth = 352; + self.imageHeight = 288; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) { + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) { + self.imageWidth = 1280; + self.imageHeight = 720; + } else { + self.imageWidth = 640; + self.imageHeight = 480; + } +} + +- (void)lockFocus; +{ + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) { + NSError *error = nil; + if ([device lockForConfiguration:&error]) { + device.focusMode = AVCaptureFocusModeLocked; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for locked focus configuration %@", [error description]); + } + } +} + +- (void) unlockFocus; +{ + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { + NSError *error = nil; + if ([device lockForConfiguration:&error]) { + device.focusMode = AVCaptureFocusModeContinuousAutoFocus; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for autofocus configuration %@", [error description]); + } + } +} + +- (void)lockExposure; +{ + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) { + NSError *error = nil; + if ([device lockForConfiguration:&error]) { + device.exposureMode = AVCaptureExposureModeLocked; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for locked exposure configuration %@", [error description]); + } + } +} + +- (void) unlockExposure; +{ + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { + NSError *error = nil; + if ([device lockForConfiguration:&error]) { + device.exposureMode = AVCaptureExposureModeContinuousAutoExposure; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for autoexposure configuration %@", [error description]); + } + } +} + +- (void)lockBalance; +{ + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) { + NSError *error = nil; + if ([device lockForConfiguration:&error]) { + device.whiteBalanceMode = AVCaptureWhiteBalanceModeLocked; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for locked white balance configuration %@", [error description]); + } + } +} + +- (void) unlockBalance; +{ + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) { + NSError *error = nil; + if ([device lockForConfiguration:&error]) { + device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for auto white balance configuration %@", [error description]); + } + } +} + +@end diff --git a/modules/videoio/misc/objc/ios/CvCamera2.h b/modules/videoio/misc/objc/ios/CvCamera2.h new file mode 100644 index 0000000000..0127116f86 --- /dev/null +++ b/modules/videoio/misc/objc/ios/CvCamera2.h @@ -0,0 +1,84 @@ +// +// CvCamera2.h +// +// Created by Giles Payne on 2020/03/11. +// + +#import +#import +#import +#import + +@class Mat; + +@class CvAbstractCamera2; + +@interface CvAbstractCamera2 : NSObject + +@property UIDeviceOrientation currentDeviceOrientation; +@property BOOL cameraAvailable; +@property (nonatomic, strong) AVCaptureSession* captureSession; +@property (nonatomic, strong) AVCaptureConnection* videoCaptureConnection; + +@property (nonatomic, readonly) BOOL running; +@property (nonatomic, readonly) BOOL captureSessionLoaded; + +@property (nonatomic, assign) int defaultFPS; +@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer; +@property (nonatomic, assign) AVCaptureDevicePosition defaultAVCaptureDevicePosition; +@property (nonatomic, assign) AVCaptureVideoOrientation defaultAVCaptureVideoOrientation; +@property (nonatomic, assign) BOOL useAVCaptureVideoPreviewLayer; +@property (nonatomic, strong) NSString *const defaultAVCaptureSessionPreset; +@property (nonatomic, assign) int imageWidth; +@property (nonatomic, assign) int imageHeight; +@property (nonatomic, strong) UIView* parentView; + +- (void)start; +- (void)stop; +- (void)switchCameras; +- (id)initWithParentView:(UIView*)parent; +- (void)createCaptureOutput; +- (void)createVideoPreviewLayer; +- (void)updateOrientation; +- (void)lockFocus; +- (void)unlockFocus; +- (void)lockExposure; +- (void)unlockExposure; +- (void)lockBalance; +- (void)unlockBalance; +@end + +///////////////////////////////// CvVideoCamera /////////////////////////////////////////// +@class CvVideoCamera2; + +@protocol CvVideoCameraDelegate2 +- (void)processImage:(Mat*)image; +@end + +@interface CvVideoCamera2 : CvAbstractCamera2 +@property (nonatomic, weak) id delegate; +@property (nonatomic, assign) BOOL grayscaleMode; +@property (nonatomic, assign) BOOL recordVideo; +@property (nonatomic, assign) BOOL rotateVideo; +@property (nonatomic, strong) AVAssetWriterInput* recordAssetWriterInput; +@property (nonatomic, strong) AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor; +@property (nonatomic, strong) AVAssetWriter* recordAssetWriter; +- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; +- (void)layoutPreviewLayer; +- (void)saveVideo; +- (NSURL *)videoFileURL; +- (NSString *)videoFileString; +@end + +///////////////////////////////// CvPhotoCamera /////////////////////////////////////////// +@class CvPhotoCamera2; + +@protocol CvPhotoCameraDelegate2 +- (void)photoCamera:(CvPhotoCamera2*)photoCamera capturedImage:(UIImage*)image; +- (void)photoCameraCancel:(CvPhotoCamera2*)photoCamera; +@end + +@interface CvPhotoCamera2 : CvAbstractCamera2 +@property (nonatomic, weak) id delegate; +- (void)takePicture; +@end diff --git a/modules/videoio/misc/objc/ios/CvPhotoCamera2.m b/modules/videoio/misc/objc/ios/CvPhotoCamera2.m new file mode 100644 index 0000000000..460cce6d32 --- /dev/null +++ b/modules/videoio/misc/objc/ios/CvPhotoCamera2.m @@ -0,0 +1,138 @@ +// +// CvPhotoCamera2.mm +// +// Created by Giles Payne on 2020/04/01. +// + +#import "CvCamera2.h" + +#pragma mark - Private Interface + +@interface CvPhotoCamera2 () +{ + id _delegate; +} + +@property (nonatomic, strong) AVCaptureStillImageOutput* stillImageOutput; + +@end + + +#pragma mark - Implementation + +@implementation CvPhotoCamera2 + + +#pragma mark Public + +- (void)setDelegate:(id)newDelegate { + _delegate = newDelegate; +} + +- (id)delegate { + return _delegate; +} + +#pragma mark - Public interface + +- (void)takePicture +{ + if (self.cameraAvailable == NO) { + return; + } + self.cameraAvailable = NO; + + [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.videoCaptureConnection + completionHandler: + ^(CMSampleBufferRef imageSampleBuffer, NSError *error) + { + if (error == nil && imageSampleBuffer != NULL) + { + // TODO check + // NSNumber* imageOrientation = [UIImage cgImageOrientationForUIDeviceOrientation:currentDeviceOrientation]; + // CMSetAttachment(imageSampleBuffer, kCGImagePropertyOrientation, imageOrientation, 1); + + NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.captureSession stopRunning]; + + // Make sure we create objects on the main thread in the main context + UIImage* newImage = [UIImage imageWithData:jpegData]; + + //UIImageOrientation orientation = [newImage imageOrientation]; + + // TODO: only apply rotation, don't scale, since we can set this directly in the camera + /* + switch (orientation) { + case UIImageOrientationUp: + case UIImageOrientationDown: + newImage = [newImage imageWithAppliedRotationAndMaxSize:CGSizeMake(640.0, 480.0)]; + break; + case UIImageOrientationLeft: + case UIImageOrientationRight: + newImage = [newImage imageWithMaxSize:CGSizeMake(640.0, 480.0)]; + default: + break; + } + */ + + // We have captured the image, we can allow the user to take another picture + self.cameraAvailable = YES; + + NSLog(@"CvPhotoCamera2 captured image"); + [self.delegate photoCamera:self capturedImage:newImage]; + + [self.captureSession startRunning]; + }); + } + }]; + + +} + +- (void)stop; +{ + [super stop]; + self.stillImageOutput = nil; +} + + +#pragma mark - Private Interface + + +- (void)createStillImageOutput; +{ + // setup still image output with jpeg codec + self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; + NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil]; + [self.stillImageOutput setOutputSettings:outputSettings]; + [self.captureSession addOutput:self.stillImageOutput]; + + for (AVCaptureConnection *connection in self.stillImageOutput.connections) { + for (AVCaptureInputPort *port in [connection inputPorts]) { + if ([port.mediaType isEqual:AVMediaTypeVideo]) { + self.videoCaptureConnection = connection; + break; + } + } + if (self.videoCaptureConnection) { + break; + } + } + NSLog(@"[Camera] still image output created"); +} + + +- (void)createCaptureOutput; +{ + [self createStillImageOutput]; +} + +- (void)createCustomVideoPreview; +{ + //do nothing, always use AVCaptureVideoPreviewLayer +} + + +@end diff --git a/modules/videoio/misc/objc/ios/CvVideoCamera2.mm b/modules/videoio/misc/objc/ios/CvVideoCamera2.mm new file mode 100644 index 0000000000..7f4abdb578 --- /dev/null +++ b/modules/videoio/misc/objc/ios/CvVideoCamera2.mm @@ -0,0 +1,575 @@ +// +// CvVideoCamera2.mm +// +// Created by Giles Payne on 2020/03/11. +// + +#import "Mat.h" +#import "CvCamera2.h" +#import + +static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;} + +#pragma mark - Private Interface + +@interface CvVideoCamera2 () { + int recordingCountDown; +} + +- (void)createVideoDataOutput; +- (void)createVideoFileOutput; + +@property (nonatomic, strong) CALayer *customPreviewLayer; +@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput; + +@end + + +#pragma mark - Implementation + +@implementation CvVideoCamera2 +{ + id _delegate; + dispatch_queue_t videoDataOutputQueue; + CMTime lastSampleTime; +} + +- (void)setDelegate:(id)newDelegate { + _delegate = newDelegate; +} + +- (id)delegate { + return _delegate; +} + +#pragma mark - Constructors + +- (id)initWithParentView:(UIView*)parent { + self = [super initWithParentView:parent]; + if (self) { + parent.contentMode = UIViewContentModeScaleAspectFill; + self.useAVCaptureVideoPreviewLayer = NO; + self.recordVideo = NO; + self.rotateVideo = NO; + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; + self.defaultAVCaptureSessionPreset = AVCaptureSessionPresetHigh; + self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; + self.defaultFPS = 30; + self.grayscaleMode = NO; + } + return self; +} + +#pragma mark - Public interface + + +- (void)start { + if (self.running == YES) { + return; + } + + recordingCountDown = 10; + [super start]; + + if (self.recordVideo == YES) { + NSError* error = nil; + if ([[NSFileManager defaultManager] fileExistsAtPath:[self videoFileString]]) { + [[NSFileManager defaultManager] removeItemAtPath:[self videoFileString] error:&error]; + } + if (error == nil) { + NSLog(@"[Camera] Delete file %@", [self videoFileString]); + } + } +} + +- (void)stop { + if (self.running == YES) { + [super stop]; + + if (self.recordVideo == YES) { + if (self.recordAssetWriter) { + if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { + [self.recordAssetWriter finishWritingWithCompletionHandler:^void() { + NSLog(@"[Camera] recording stopped"); + }]; + } else { + NSLog(@"[Camera] Recording Error: asset writer status is not writing"); + } + } + } + + if (self.customPreviewLayer) { + [self.customPreviewLayer removeFromSuperlayer]; + self.customPreviewLayer = nil; + } + } +} + +// TODO fix +- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + NSLog(@"layout preview layer"); + if (self.parentView != nil) { + + CALayer* layer = self.customPreviewLayer; + CGRect bounds = self.customPreviewLayer.bounds; + int rotation_angle = 0; + bool flip_bounds = false; + + switch (interfaceOrientation) { + case UIInterfaceOrientationPortrait: + NSLog(@"to Portrait"); + rotation_angle = 270; + break; + case UIInterfaceOrientationPortraitUpsideDown: + rotation_angle = 90; + NSLog(@"to UpsideDown"); + break; + case UIInterfaceOrientationLandscapeLeft: + rotation_angle = 0; + NSLog(@"to LandscapeLeft"); + break; + case UIInterfaceOrientationLandscapeRight: + rotation_angle = 180; + NSLog(@"to LandscapeRight"); + break; + default: + break; // leave the layer in its last known orientation + } + + switch (self.defaultAVCaptureVideoOrientation) { + case AVCaptureVideoOrientationLandscapeRight: + rotation_angle += 180; + break; + case AVCaptureVideoOrientationPortraitUpsideDown: + rotation_angle += 270; + break; + case AVCaptureVideoOrientationPortrait: + rotation_angle += 90; + case AVCaptureVideoOrientationLandscapeLeft: + break; + default: + break; + } + rotation_angle = rotation_angle % 360; + + if (rotation_angle == 90 || rotation_angle == 270) { + flip_bounds = true; + } + + if (flip_bounds) { + NSLog(@"flip bounds"); + bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width); + } + + layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.); + self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); + + layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) ); + layer.bounds = bounds; + } + +} + +// TODO fix +- (void)layoutPreviewLayer { + NSLog(@"layout preview layer"); + if (self.parentView != nil) { + + CALayer* layer = self.customPreviewLayer; + CGRect bounds = self.customPreviewLayer.bounds; + int rotation_angle = 0; + bool flip_bounds = false; + + switch (self.currentDeviceOrientation) { + case UIDeviceOrientationPortrait: + rotation_angle = 270; + break; + case UIDeviceOrientationPortraitUpsideDown: + rotation_angle = 90; + break; + case UIDeviceOrientationLandscapeLeft: + NSLog(@"left"); + rotation_angle = 180; + break; + case UIDeviceOrientationLandscapeRight: + NSLog(@"right"); + rotation_angle = 0; + break; + case UIDeviceOrientationFaceUp: + case UIDeviceOrientationFaceDown: + default: + break; // leave the layer in its last known orientation + } + + switch (self.defaultAVCaptureVideoOrientation) { + case AVCaptureVideoOrientationLandscapeRight: + rotation_angle += 180; + break; + case AVCaptureVideoOrientationPortraitUpsideDown: + rotation_angle += 270; + break; + case AVCaptureVideoOrientationPortrait: + rotation_angle += 90; + case AVCaptureVideoOrientationLandscapeLeft: + break; + default: + break; + } + rotation_angle = rotation_angle % 360; + + if (rotation_angle == 90 || rotation_angle == 270) { + flip_bounds = true; + } + + if (flip_bounds) { + NSLog(@"flip bounds"); + bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width); + } + + layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.); + layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) ); + layer.bounds = bounds; + } + +} + +#pragma mark - Private Interface + +- (void)createVideoDataOutput { + // Make a video data output + self.videoDataOutput = [AVCaptureVideoDataOutput new]; + + // In grayscale mode we want YUV (YpCbCr 4:2:0) so we can directly access the graylevel intensity values (Y component) + // In color mode we, BGRA format is used + OSType format = self.grayscaleMode ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA; + + self.videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:format] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + + // discard if the data output queue is blocked (as we process the still image) + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + + if ( [self.captureSession canAddOutput:self.videoDataOutput] ) { + [self.captureSession addOutput:self.videoDataOutput]; + } + [[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setEnabled:YES]; + + + // set default FPS + AVCaptureDeviceInput *currentInput = [self.captureSession.inputs objectAtIndex:0]; + AVCaptureDevice *device = currentInput.device; + + NSError *error = nil; + [device lockForConfiguration:&error]; + + float maxRate = ((AVFrameRateRange*) [device.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0]).maxFrameRate; + if (maxRate > self.defaultFPS - 1 && error == nil) { + [device setActiveVideoMinFrameDuration:CMTimeMake(1, self.defaultFPS)]; + [device setActiveVideoMaxFrameDuration:CMTimeMake(1, self.defaultFPS)]; + NSLog(@"[Camera] FPS set to %d", self.defaultFPS); + } else { + NSLog(@"[Camera] unable to set defaultFPS at %d FPS, max is %f FPS", self.defaultFPS, maxRate); + } + + if (error != nil) { + NSLog(@"[Camera] unable to set defaultFPS: %@", error); + } + + [device unlockForConfiguration]; + + // set video mirroring for front camera (more intuitive) + if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMirroring) { + if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = YES; + } else { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = NO; + } + } + + // set default video orientation + if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoOrientation) { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation = self.defaultAVCaptureVideoOrientation; + } + + + // create a custom preview layer + self.customPreviewLayer = [CALayer layer]; + self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); + self.customPreviewLayer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.); + [self updateOrientation]; + + // create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured + // a serial dispatch queue must be used to guarantee that video frames will be delivered in order + // see the header doc for setSampleBufferDelegate:queue: for more information + videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL); + [self.videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue]; + + + NSLog(@"[Camera] created AVCaptureVideoDataOutput"); +} + +- (void)createVideoFileOutput { + /* Video File Output in H.264, via AVAsserWriter */ + NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight); + + NSDictionary *outputSettings + = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.imageWidth], AVVideoWidthKey, + [NSNumber numberWithInt:self.imageHeight], AVVideoHeightKey, + AVVideoCodecH264, AVVideoCodecKey, + nil + ]; + + + self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings]; + + + int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA; + + self.recordPixelBufferAdaptor = + [[AVAssetWriterInputPixelBufferAdaptor alloc] + initWithAssetWriterInput:self.recordAssetWriterInput + sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey, nil]]; + + NSError* error = nil; + NSLog(@"Create AVAssetWriter with url: %@", [self videoFileURL]); + self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self videoFileURL] + fileType:AVFileTypeMPEG4 + error:&error]; + if (error != nil) { + NSLog(@"[Camera] Unable to create AVAssetWriter: %@", error); + } + + [self.recordAssetWriter addInput:self.recordAssetWriterInput]; + self.recordAssetWriterInput.expectsMediaDataInRealTime = YES; + + NSLog(@"[Camera] created AVAssetWriter"); +} + +- (void)createCaptureOutput { + [self createVideoDataOutput]; + if (self.recordVideo == YES) { + [self createVideoFileOutput]; + } +} + +- (void)createCustomVideoPreview { + [self.parentView.layer addSublayer:self.customPreviewLayer]; +} + +- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image { + + CGSize frameSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image)); + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:NO], kCVPixelBufferCGImageCompatibilityKey, + [NSNumber numberWithBool:NO], kCVPixelBufferCGBitmapContextCompatibilityKey, + nil]; + CVPixelBufferRef pxbuffer = NULL; + CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width, + frameSize.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) CFBridgingRetain(options), + &pxbuffer); + NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); + + CVPixelBufferLockBaseAddress(pxbuffer, 0); + void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); + + + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(pxdata, frameSize.width, + frameSize.height, 8, 4*frameSize.width, rgbColorSpace, + kCGImageAlphaPremultipliedFirst); + + CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), + CGImageGetHeight(image)), image); + CGColorSpaceRelease(rgbColorSpace); + CGContextRelease(context); + + CVPixelBufferUnlockBaseAddress(pxbuffer, 0); + + return pxbuffer; +} + +#pragma mark - Protocol AVCaptureVideoDataOutputSampleBufferDelegate + +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection +{ + (void)captureOutput; + (void)connection; + auto strongDelegate = self.delegate; + if (strongDelegate) { + + // convert from Core Media to Core Video + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer, 0); + + void* bufferAddress; + size_t width; + size_t height; + size_t bytesPerRow; + + CGColorSpaceRef colorSpace; + CGContextRef context; + + int format_opencv; + + OSType format = CVPixelBufferGetPixelFormatType(imageBuffer); + if (format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) { + + format_opencv = CV_8UC1; + + bufferAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); + width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0); + height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0); + bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0); + + } else { // expect kCVPixelFormatType_32BGRA + + format_opencv = CV_8UC4; + + bufferAddress = CVPixelBufferGetBaseAddress(imageBuffer); + width = CVPixelBufferGetWidth(imageBuffer); + height = CVPixelBufferGetHeight(imageBuffer); + bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + + } + + // delegate image processing to the delegate + cv::Mat image((int)height, (int)width, format_opencv, bufferAddress, bytesPerRow); + + CGImage* dstImage; + + if ([strongDelegate respondsToSelector:@selector(processImage:)]) { + [strongDelegate processImage:[Mat fromNative:image]]; + } + + // check if matrix data pointer or dimensions were changed by the delegate + bool iOSimage = false; + if (height == (size_t)image.rows && width == (size_t)image.cols && format_opencv == image.type() && bufferAddress == image.data && bytesPerRow == image.step) { + iOSimage = true; + } + + + // (create color space, create graphics context, render buffer) + CGBitmapInfo bitmapInfo; + + // basically we decide if it's a grayscale, rgb or rgba image + if (image.channels() == 1) { + colorSpace = CGColorSpaceCreateDeviceGray(); + bitmapInfo = kCGImageAlphaNone; + } else if (image.channels() == 3) { + colorSpace = CGColorSpaceCreateDeviceRGB(); + bitmapInfo = kCGImageAlphaNone; + if (iOSimage) { + bitmapInfo |= kCGBitmapByteOrder32Little; + } else { + bitmapInfo |= kCGBitmapByteOrder32Big; + } + } else { + colorSpace = CGColorSpaceCreateDeviceRGB(); + bitmapInfo = kCGImageAlphaPremultipliedFirst; + if (iOSimage) { + bitmapInfo |= kCGBitmapByteOrder32Little; + } else { + bitmapInfo |= kCGBitmapByteOrder32Big; + } + } + + if (iOSimage) { + context = CGBitmapContextCreate(bufferAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo); + dstImage = CGBitmapContextCreateImage(context); + CGContextRelease(context); + } else { + + NSData *data = [NSData dataWithBytes:image.data length:image.elemSize()*image.total()]; + CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + + // Creating CGImage from cv::Mat + dstImage = CGImageCreate(image.cols, // width + image.rows, // height + 8, // bits per component + 8 * image.elemSize(), // bits per pixel + image.step, // bytesPerRow + colorSpace, // colorspace + bitmapInfo, // bitmap info + provider, // CGDataProviderRef + NULL, // decode + false, // should interpolate + kCGRenderingIntentDefault // intent + ); + + CGDataProviderRelease(provider); + } + + + // render buffer + dispatch_sync(dispatch_get_main_queue(), ^{ + self.customPreviewLayer.contents = (__bridge id)dstImage; + }); + + + recordingCountDown--; + if (self.recordVideo == YES && recordingCountDown < 0) { + lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); +// CMTimeShow(lastSampleTime); + if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) { + [self.recordAssetWriter startWriting]; + [self.recordAssetWriter startSessionAtSourceTime:lastSampleTime]; + if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) { + NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error); + return; + } else { + NSLog(@"[Camera] Video recording started"); + } + } + + if (self.recordAssetWriterInput.readyForMoreMediaData) { + CVImageBufferRef pixelBuffer = [self pixelBufferFromCGImage:dstImage]; + if (! [self.recordPixelBufferAdaptor appendPixelBuffer:pixelBuffer + withPresentationTime:lastSampleTime] ) { + NSLog(@"Video Writing Error"); + } + if (pixelBuffer != nullptr) + CVPixelBufferRelease(pixelBuffer); + } + + } + + // cleanup + CGImageRelease(dstImage); + CGColorSpaceRelease(colorSpace); + CVPixelBufferUnlockBaseAddress(imageBuffer, 0); + } +} + +- (void)updateOrientation { + if (self.rotateVideo == YES) + { + NSLog(@"rotate.."); + self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); + [self layoutPreviewLayer]; + } +} + +- (void)saveVideo { + if (self.recordVideo == NO) { + return; + } + + UISaveVideoAtPathToSavedPhotosAlbum([self videoFileString], nil, nil, NULL); +} + +- (NSURL *)videoFileURL { + NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; + NSURL *outputURL = [NSURL fileURLWithPath:outputPath]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:outputPath]) { + NSLog(@"file exists"); + } + return outputURL; +} + +- (NSString *)videoFileString { + NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; + return outputPath; +} + +@end diff --git a/platforms/ios/build_framework.py b/platforms/ios/build_framework.py index 9135ecf7b2..d0abf7038a 100755 --- a/platforms/ios/build_framework.py +++ b/platforms/ios/build_framework.py @@ -17,19 +17,20 @@ Script will create , if it's missing, and a few its subdirectories: [cmake-generated build tree for an iOS device target] iPhoneSimulator-*/ [cmake-generated build tree for iOS simulator] - opencv2.framework/ + {framework_name}.framework/ [the framework content] The script should handle minor OpenCV updates efficiently - it does not recompile the library from scratch each time. -However, opencv2.framework directory is erased and recreated on each run. +However, {framework_name}.framework directory is erased and recreated on each run. -Adding --dynamic parameter will build opencv2.framework as App Store dynamic framework. Only iOS 8+ versions are supported. +Adding --dynamic parameter will build {framework_name}.framework as App Store dynamic framework. Only iOS 8+ versions are supported. """ from __future__ import print_function import glob, re, os, os.path, shutil, string, sys, argparse, traceback, multiprocessing from subprocess import check_call, check_output, CalledProcessError +from distutils.dir_util import copy_tree IPHONEOS_DEPLOYMENT_TARGET='8.0' # default, can be changed via command line options or environment variable @@ -49,7 +50,7 @@ def getXCodeMajor(): raise Exception("Failed to parse Xcode version") class Builder: - def __init__(self, opencv, contrib, dynamic, bitcodedisabled, exclude, disable, enablenonfree, targets, debug, debug_info): + def __init__(self, opencv, contrib, dynamic, bitcodedisabled, exclude, disable, enablenonfree, targets, debug, debug_info, framework_name): self.opencv = os.path.abspath(opencv) self.contrib = None if contrib: @@ -61,11 +62,13 @@ class Builder: self.dynamic = dynamic self.bitcodedisabled = bitcodedisabled self.exclude = exclude + self.build_objc_wrapper = not "objc" in self.exclude self.disable = disable self.enablenonfree = enablenonfree self.targets = targets self.debug = debug self.debug_info = debug_info + self.framework_name = framework_name def getBD(self, parent, t): @@ -114,6 +117,10 @@ class Builder: if self.dynamic == False: self.mergeLibs(mainBD) self.makeFramework(outdir, dirs) + if self.build_objc_wrapper: + print("To run tests call:") + print(sys.argv[0].replace("build_framework", "run_tests") + " --framework_dir=" + outdir + " --framework_name=" + self.framework_name + " " + dirs[0] + "/modules/objc/test") + self.copy_samples(outdir) def build(self, outdir): try: @@ -140,7 +147,8 @@ class Builder: "-DCMAKE_INSTALL_PREFIX=install", "-DCMAKE_BUILD_TYPE=%s" % self.getConfiguration(), "-DOPENCV_INCLUDE_INSTALL_PATH=include", - "-DOPENCV_3P_LIB_INSTALL_PATH=lib/3rdparty" + "-DOPENCV_3P_LIB_INSTALL_PATH=lib/3rdparty", + "-DFRAMEWORK_NAME=%s" % self.framework_name, ] + ([ "-DBUILD_SHARED_LIBS=ON", "-DCMAKE_MACOSX_BUNDLE=ON", @@ -197,15 +205,26 @@ class Builder: def getInfoPlist(self, builddirs): return os.path.join(builddirs[0], "ios", "Info.plist") - def buildOne(self, arch, target, builddir, cmakeargs = []): - # Run cmake + def makeCMakeCmd(self, arch, target, dir, cmakeargs = []): toolchain = self.getToolchain(arch, target) cmakecmd = self.getCMakeArgs(arch, target) + \ (["-DCMAKE_TOOLCHAIN_FILE=%s" % toolchain] if toolchain is not None else []) if target.lower().startswith("iphoneos"): cmakecmd.append("-DCPU_BASELINE=DETECT") - cmakecmd.append(self.opencv) + cmakecmd.append(dir) cmakecmd.extend(cmakeargs) + return cmakecmd + + def buildOne(self, arch, target, builddir, cmakeargs = []): + # Run cmake + #toolchain = self.getToolchain(arch, target) + #cmakecmd = self.getCMakeArgs(arch, target) + \ + # (["-DCMAKE_TOOLCHAIN_FILE=%s" % toolchain] if toolchain is not None else []) + #if target.lower().startswith("iphoneos"): + # cmakecmd.append("-DCPU_BASELINE=DETECT") + #cmakecmd.append(self.opencv) + #cmakecmd.extend(cmakeargs) + cmakecmd = self.makeCMakeCmd(arch, target, self.opencv, cmakeargs) execute(cmakecmd, cwd = builddir) # Clean and build @@ -215,16 +234,27 @@ class Builder: buildcmd = self.getBuildCommand(arch, target) execute(buildcmd + ["-target", "ALL_BUILD", "build"], cwd = builddir) execute(["cmake", "-DBUILD_TYPE=%s" % self.getConfiguration(), "-P", "cmake_install.cmake"], cwd = builddir) + if self.build_objc_wrapper: + cmakecmd = self.makeCMakeCmd(arch, target, builddir + "/modules/objc/gen", cmakeargs) + cmakecmd.append("-DBUILD_ROOT=%s" % builddir) + cmakecmd.append("-DCMAKE_INSTALL_NAME_TOOL=install_name_tool") + cmakecmd.append("--no-warn-unused-cli") + execute(cmakecmd, cwd = builddir + "/modules/objc/framework_build") + + execute(buildcmd + ["-target", "ALL_BUILD", "build"], cwd = builddir + "/modules/objc/framework_build") + execute(["cmake", "-DBUILD_TYPE=%s" % self.getConfiguration(), "-DCMAKE_INSTALL_PREFIX=%s" % (builddir + "/install"), "-P", "cmake_install.cmake"], cwd = builddir + "/modules/objc/framework_build") def mergeLibs(self, builddir): res = os.path.join(builddir, "lib", self.getConfiguration(), "libopencv_merged.a") libs = glob.glob(os.path.join(builddir, "install", "lib", "*.a")) + module = [os.path.join(builddir, "install", "lib", self.framework_name + ".framework", self.framework_name)] if self.build_objc_wrapper else [] + libs3 = glob.glob(os.path.join(builddir, "install", "lib", "3rdparty", "*.a")) - print("Merging libraries:\n\t%s" % "\n\t".join(libs + libs3), file=sys.stderr) - execute(["libtool", "-static", "-o", res] + libs + libs3) + print("Merging libraries:\n\t%s" % "\n\t".join(libs + libs3 + module), file=sys.stderr) + execute(["libtool", "-static", "-o", res] + libs + libs3 + module) def makeFramework(self, outdir, builddirs): - name = "opencv2" + name = self.framework_name # set the current dir to the dst root framework_dir = os.path.join(outdir, "%s.framework" % name) @@ -234,13 +264,41 @@ class Builder: if self.dynamic: dstdir = framework_dir - libname = "opencv2.framework/opencv2" + libname = name + ".framework/" + name else: dstdir = os.path.join(framework_dir, "Versions", "A") libname = "libopencv_merged.a" # copy headers from one of build folders shutil.copytree(os.path.join(builddirs[0], "install", "include", "opencv2"), os.path.join(dstdir, "Headers")) + if name != "opencv2": + for dirname, dirs, files in os.walk(os.path.join(dstdir, "Headers")): + for filename in files: + filepath = os.path.join(dirname, filename) + with open(filepath) as file: + body = file.read() + body = body.replace("include \"opencv2/", "include \"" + name + "/") + body = body.replace("include ", "#import <" + self.framework_name + "/" + self.framework_name + ".h>") + body = body.replace("OpenCV.framework", self.framework_name + ".framework") + body = body.replace("../../OpenCV/**", "../../" + self.framework_name + "/**") + with open(filepath, "w") as file: + file.write(body) + if __name__ == "__main__": folder = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "../..")) @@ -304,6 +389,8 @@ if __name__ == "__main__": parser.add_argument('--enable_nonfree', default=False, dest='enablenonfree', action='store_true', help='enable non-free modules (disabled by default)') parser.add_argument('--debug', default=False, dest='debug', action='store_true', help='Build "Debug" binaries (disabled by default)') parser.add_argument('--debug_info', default=False, dest='debug_info', action='store_true', help='Build with debug information (useful for Release mode: BUILD_WITH_DEBUG_INFO=ON)') + parser.add_argument('--framework_name', default='opencv2', dest='framework_name', action='store_true', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)') + parser.add_argument('--legacy_build', default=False, dest='legacy_build', action='store_true', help='Build legacy opencv2 framework (default: False, equivalent to "--framework_name=opencv2 --without=objc")') args = parser.parse_args() os.environ['IPHONEOS_DEPLOYMENT_TARGET'] = args.iphoneos_deployment_target @@ -312,6 +399,10 @@ if __name__ == "__main__": print('Using iPhoneOS ARCHS=' + str(iphoneos_archs)) iphonesimulator_archs = args.iphonesimulator_archs.split(',') print('Using iPhoneSimulator ARCHS=' + str(iphonesimulator_archs)) + if args.legacy_build: + args.framework_name = "opencv2" + if not "objc" in args.without: + args.without.append("objc") b = iOSBuilder(args.opencv, args.contrib, args.dynamic, args.bitcodedisabled, args.without, args.disable, args.enablenonfree, [ @@ -320,5 +411,5 @@ if __name__ == "__main__": [ (iphoneos_archs, "iPhoneOS"), (iphonesimulator_archs, "iPhoneSimulator"), - ], args.debug, args.debug_info) + ], args.debug, args.debug_info, args.framework_name) b.build(args.out) diff --git a/platforms/ios/run_tests.py b/platforms/ios/run_tests.py new file mode 100755 index 0000000000..de302c2bfe --- /dev/null +++ b/platforms/ios/run_tests.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +""" +This script runs OpenCV.framework tests for iOS. +""" + +from __future__ import print_function +import glob, re, os, os.path, shutil, string, sys, argparse, traceback, multiprocessing +from subprocess import check_call, check_output, CalledProcessError + +IPHONEOS_DEPLOYMENT_TARGET='8.0' # default, can be changed via command line options or environment variable + +def execute(cmd, cwd = None): + print("Executing: %s in %s" % (cmd, cwd), file=sys.stderr) + print('Executing: ' + ' '.join(cmd)) + retcode = check_call(cmd, cwd = cwd) + if retcode != 0: + raise Exception("Child returned:", retcode) + +class TestRunner: + def __init__(self, script_dir, tests_dir, build_dir, framework_dir, framework_name, arch, target, platform): + self.script_dir = script_dir + self.tests_dir = tests_dir + self.build_dir = build_dir + self.framework_dir = framework_dir + self.framework_name = framework_name + self.arch = arch + self.target = target + self.platform = platform + + def _run(self): + if not os.path.isdir(self.build_dir): + os.makedirs(self.build_dir) + + self.runTest() + + def run(self): + try: + self._run() + except Exception as e: + print("="*60, file=sys.stderr) + print("ERROR: %s" % e, file=sys.stderr) + print("="*60, file=sys.stderr) + traceback.print_exc(file=sys.stderr) + sys.exit(1) + + def getToolchain(self): + return None + + def getCMakeArgs(self): + args = [ + "cmake", + "-GXcode", + "-DFRAMEWORK_DIR=%s" % self.framework_dir, + "-DFRAMEWORK_NAME=%s" % self.framework_name, + ] + return args + + def makeCMakeCmd(self): + toolchain = self.getToolchain() + cmakecmd = self.getCMakeArgs() + \ + (["-DCMAKE_TOOLCHAIN_FILE=%s" % toolchain] if toolchain is not None else []) + \ + ["-DCMAKE_INSTALL_NAME_TOOL=install_name_tool"] + cmakecmd.append(self.tests_dir) + return cmakecmd + + def runTest(self): + cmakecmd = self.makeCMakeCmd() + execute(cmakecmd, cwd = self.build_dir) + buildcmd = self.getTestCommand() + execute(buildcmd, cwd = self.build_dir) + + def getTestCommand(self): + testcmd = [ + "xcodebuild", + "test", + "-project", "OpenCVTest.xcodeproj", + "-scheme", "OpenCVTestTests", + "-destination", "platform=%s" % self.platform + ] + return testcmd + +class iOSTestRunner(TestRunner): + + def getToolchain(self): + toolchain = os.path.join(self.script_dir, "cmake", "Toolchains", "Toolchain-%s_Xcode.cmake" % self.target) + return toolchain + + def getCMakeArgs(self): + args = TestRunner.getCMakeArgs(self) + args = args + [ + "-DIOS_ARCH=%s" % self.arch, + "-DIPHONEOS_DEPLOYMENT_TARGET=%s" % os.environ['IPHONEOS_DEPLOYMENT_TARGET'], + ] + return args + +if __name__ == "__main__": + script_dir = os.path.abspath(os.path.dirname(sys.argv[0])) + parser = argparse.ArgumentParser(description='The script builds OpenCV.framework for iOS.') + parser.add_argument('tests_dir', metavar='TEST_DIR', help='folder where test files are located') + parser.add_argument('--build_dir', default=None, help='folder where test will be built (default is "../test_build" relative to tests_dir)') + parser.add_argument('--framework_dir', default=None, help='folder where OpenCV framework is located') + parser.add_argument('--framework_name', default='opencv2', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)') + parser.add_argument('--iphoneos_deployment_target', default=os.environ.get('IPHONEOS_DEPLOYMENT_TARGET', IPHONEOS_DEPLOYMENT_TARGET), help='specify IPHONEOS_DEPLOYMENT_TARGET') + parser.add_argument('--platform', default='iOS Simulator,name=iPhone 11', help='xcodebuild platform parameter (default is iOS 11 simulator)') + args = parser.parse_args() + + os.environ['IPHONEOS_DEPLOYMENT_TARGET'] = args.iphoneos_deployment_target + print('Using IPHONEOS_DEPLOYMENT_TARGET=' + os.environ['IPHONEOS_DEPLOYMENT_TARGET']) + arch = "x86_64" + target = "iPhoneSimulator" + print('Using iPhoneSimulator ARCH=' + arch) + + r = iOSTestRunner(script_dir, args.tests_dir, args.build_dir if args.build_dir else os.path.join(args.tests_dir, "../test_build"), args.framework_dir, args.framework_name, arch, target, args.platform) + r.run() diff --git a/platforms/osx/build_framework.py b/platforms/osx/build_framework.py index 1c2b3cdb92..73ab6c39e8 100755 --- a/platforms/osx/build_framework.py +++ b/platforms/osx/build_framework.py @@ -45,14 +45,20 @@ if __name__ == "__main__": parser.add_argument('--macosx_deployment_target', default=os.environ.get('MACOSX_DEPLOYMENT_TARGET', MACOSX_DEPLOYMENT_TARGET), help='specify MACOSX_DEPLOYMENT_TARGET') parser.add_argument('--debug', action='store_true', help='Build "Debug" binaries (CMAKE_BUILD_TYPE=Debug)') parser.add_argument('--debug_info', action='store_true', help='Build with debug information (useful for Release mode: BUILD_WITH_DEBUG_INFO=ON)') + parser.add_argument('--framework_name', default='opencv2', dest='framework_name', action='store_true', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)') + parser.add_argument('--legacy_build', default=False, dest='legacy_build', action='store_true', help='Build legacy framework (default: False, equivalent to "--framework_name=opencv2 --without=objc")') args = parser.parse_args() os.environ['MACOSX_DEPLOYMENT_TARGET'] = args.macosx_deployment_target print('Using MACOSX_DEPLOYMENT_TARGET=' + os.environ['MACOSX_DEPLOYMENT_TARGET']) + if args.legacy_build: + args.framework_name = "opencv2" + if not "objc" in args.without: + args.without.append("objc") b = OSXBuilder(args.opencv, args.contrib, False, False, args.without, args.disable, args.enablenonfree, [ (["x86_64"], "MacOSX") - ], args.debug, args.debug_info) + ], args.debug, args.debug_info, args.framework_name) b.build(args.out) diff --git a/platforms/osx/run_tests.py b/platforms/osx/run_tests.py new file mode 100755 index 0000000000..afdcf8426a --- /dev/null +++ b/platforms/osx/run_tests.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" +This script runs OpenCV.framework tests for OSX. +""" + +from __future__ import print_function +import os, os.path, sys, argparse, traceback, multiprocessing + +# import common code +sys.path.insert(0, os.path.abspath(os.path.abspath(os.path.dirname(__file__))+'/../ios')) +from run_tests import TestRunner + +MACOSX_DEPLOYMENT_TARGET='10.12' # default, can be changed via command line options or environment variable + +class OSXTestRunner(TestRunner): + + def getToolchain(self): + return None + + def getCMakeArgs(self): + args = TestRunner.getCMakeArgs(self) + args = args + [ + '-DMACOSX_DEPLOYMENT_TARGET=%s' % os.environ['MACOSX_DEPLOYMENT_TARGET'] + ] + return args + + +if __name__ == "__main__": + script_dir = os.path.abspath(os.path.dirname(sys.argv[0])) + parser = argparse.ArgumentParser(description='The script builds OpenCV.framework for OSX.') + parser.add_argument('tests_dir', metavar='TEST_DIR', help='folder where test files are located') + parser.add_argument('--build_dir', default=None, help='folder where test will be built (default is "../test_build" relative to tests_dir)') + parser.add_argument('--framework_dir', default=None, help='folder where OpenCV framework is located') + parser.add_argument('--framework_name', default='opencv2', help='Name of OpenCV framework (default: opencv2, will change to OpenCV in future version)') + parser.add_argument('--macosx_deployment_target', default=os.environ.get('MACOSX_DEPLOYMENT_TARGET', MACOSX_DEPLOYMENT_TARGET), help='specify MACOSX_DEPLOYMENT_TARGET') + parser.add_argument('--platform', default='macOS,arch=x86_64', help='xcodebuild platform parameter (default is macOS)') + + args = parser.parse_args() + os.environ['MACOSX_DEPLOYMENT_TARGET'] = args.macosx_deployment_target + arch = "x86_64" + target = "macOS" + + r = OSXTestRunner(script_dir, args.tests_dir, args.build_dir if args.build_dir else os.path.join(args.tests_dir, "../test_build"), args.framework_dir, args.framework_name, arch, target, args.platform) + r.run() diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection.xcodeproj/project.pbxproj b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..be696a84b6 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection.xcodeproj/project.pbxproj differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..3d4c1e5525 --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/AppDelegate.swift b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/AppDelegate.swift new file mode 100644 index 0000000000..9965cc5086 --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/AppDelegate.swift @@ -0,0 +1,19 @@ +// +// AppDelegate.swift +// +// Created by Giles Payne on 2020/03/02. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + +} diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/100.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000000..1e700dd332 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/114.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000000..f4312fc9c5 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/120.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000000..3b0a91b6a6 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/144.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000000..a5660b329c Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/152.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000000..99e2a23b4a Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/167.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000000..ea93ffc144 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/180.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000000..6d525448c3 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/20.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000000..d06f341d74 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/29.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000000..cee7ce05b5 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/40.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000000..0340a78dee Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/50.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/50.png new file mode 100644 index 0000000000..d143fdcce4 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/57.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000000..55d5723144 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/58.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000000..e2653bcfde Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/60.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000000..908cc0692c Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/72.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000000..d33f50de34 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/76.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000000..407d815ebb Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/80.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000000..6f3ad42bfc Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/87.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000000..767bb5810a Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..859fae7f90 --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,158 @@ +{ + "images" : [ + { + "filename" : "40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "57.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "114.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "50.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "100.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "72.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "144.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "appstore.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/appstore.png b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/appstore.png new file mode 100644 index 0000000000..772f3ff185 Binary files /dev/null and b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/AppIcon.appiconset/appstore.png differ diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/Contents.json b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Base.lproj/LaunchScreen.storyboard b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..865e9329f3 --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Base.lproj/Main.storyboard b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..071ca8c392 --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Base.lproj/Main.storyboard @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/ColorBlobDetector.swift b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/ColorBlobDetector.swift new file mode 100644 index 0000000000..82c34ab58d --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/ColorBlobDetector.swift @@ -0,0 +1,75 @@ +// +// ColorBlobDetector.swift +// +// Created by Giles Payne on 2020/04/04. +// + +import OpenCV + +public class ColorBlobDetector { + // Lower and Upper bounds for range checking in HSV color space + var lowerBound = Scalar(0.0) + var upperBound = Scalar(0.0) + // Minimum contour area in percent for contours filtering + static let minContourArea = 0.1 + // Color radius for range checking in HSV color space + var colorRadius = Scalar(25.0, 50.0, 50.0, 0.0) + let spectrum = Mat() + let contours = NSMutableArray() + + // Cache + let pyrDownMat = Mat() + let hsvMat = Mat() + let mask = Mat() + let dilatedMask = Mat() + let hierarchy = Mat() + + func setHsvColor(hsvColor:Scalar) { + let minH = (hsvColor.val[0].doubleValue >= colorRadius.val[0].doubleValue) ? hsvColor.val[0].doubleValue - colorRadius.val[0].doubleValue : 0 + let maxH = (hsvColor.val[0].doubleValue + colorRadius.val[0].doubleValue <= 255) ? hsvColor.val[0].doubleValue + colorRadius.val[0].doubleValue : 255 + + lowerBound = Scalar(minH, hsvColor.val[1].doubleValue - colorRadius.val[1].doubleValue, hsvColor.val[2].doubleValue - colorRadius.val[2].doubleValue, 0) + upperBound = Scalar(maxH, hsvColor.val[1].doubleValue + colorRadius.val[1].doubleValue, hsvColor.val[2].doubleValue + colorRadius.val[2].doubleValue, 255) + + let spectrumHsv = Mat(rows: 1, cols: (Int32)(maxH-minH), type:CvType.CV_8UC3); + + for j:Int32 in 0.. ColorBlobDetector.minContourArea * maxArea) { + Core.multiply(src1: contourMat, srcScalar: Scalar(4.0,4.0), dst: contourMat) + contours.add(NSMutableArray(array: contourMat.toArray())) + } + } + } +} diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Info.plist b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Info.plist new file mode 100644 index 0000000000..19b473c26f --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/Info.plist @@ -0,0 +1,49 @@ + + + + + NSCameraUsageDescription + Please allow access to the camera for image processing + UIUserInterfaceStyle + Light + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/ViewController.swift b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/ViewController.swift new file mode 100644 index 0000000000..4d2975dd56 --- /dev/null +++ b/samples/swift/ios/ColorBlobDetection/ColorBlobDetection/ViewController.swift @@ -0,0 +1,124 @@ +// +// ViewController.swift +// +// Created by Giles Payne on 2020/03/02. +// + +import UIKit +import OpenCV + +class ViewController: UIViewController, CvVideoCameraDelegate2 { + + var isColorSelected = false + var rgba: Mat? = nil + let detector = ColorBlobDetector() + let spectrum = Mat() + var blobColorRgba = Scalar(255.0) + var blobColorHsv = Scalar(255.0) + let SPECTRUM_SIZE = Size(width: 200, height: 64) + let CONTOUR_COLOR = Scalar(255.0, 0.0, 0.0, 255.0) + var cameraHolderWidth: CGFloat = 0 + var cameraHolderHeight: CGFloat = 0 + + func processImage(_ image: Mat!) { + rgba = image + if isColorSelected { + detector.process(rgbaImage: image) + let contours = detector.contours + NSLog("Contours count: \(contours.count))") + Imgproc.drawContours(image: image, contours: contours as! [[Point]], contourIdx: -1, color: CONTOUR_COLOR) + + let colorLabel = image.submat(rowStart: 4, rowEnd: 68, colStart: 4, colEnd: 68) + colorLabel.setTo(scalar: blobColorRgba) + + let spectrumLabel = image.submat(rowStart: 4, rowEnd: 4 + spectrum.rows(), colStart: 70, colEnd: 70 + spectrum.cols()) + spectrum.copy(to: spectrumLabel) + } + } + + var camera: CvVideoCamera2? = nil + + @IBOutlet weak var cameraHolder: UIView! + override func viewDidLoad() { + super.viewDidLoad() + camera = CvVideoCamera2(parentView: cameraHolder) + camera?.rotateVideo = true + camera?.delegate = self + camera?.start() + } + + override func viewDidLayoutSubviews() { + if UIDevice.current.orientation.isLandscape { + cameraHolderWidth = cameraHolder.bounds.height + cameraHolderHeight = cameraHolder.bounds.width + } else { + cameraHolderWidth = cameraHolder.bounds.width + cameraHolderHeight = cameraHolder.bounds.height + } + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + if let aRgba = rgba, touches.count == 1 { + let touch = touches.first! + let cols = CGFloat(aRgba.cols()) + let rows = CGFloat(aRgba.rows()) + + let orientation = UIDevice.current.orientation + var x = touch.location(in: cameraHolder).x + var y = touch.location(in: cameraHolder).y + if orientation == .landscapeLeft { + let tempX = x + x = cameraHolder.bounds.height - y + y = tempX + } else if orientation == .landscapeRight { + let tempY = y + y = cameraHolder.bounds.width - x + x = tempY + } + + x = x * (cols / cameraHolderWidth) + y = y * (rows / cameraHolderHeight) + + if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) { + return + } + + let touchedRect = Rect() + + touchedRect.x = (x>4) ? Int32(x)-4 : 0; + touchedRect.y = (y>4) ? Int32(y)-4 : 0; + + touchedRect.width = (x+4 < cols) ? Int32(x) + 4 - touchedRect.x : Int32(cols) - touchedRect.x; + touchedRect.height = (y+4 < rows) ? Int32(y) + 4 - touchedRect.y : Int32(rows) - touchedRect.y; + + let touchedRegionRgba = aRgba.submat(roi: touchedRect) + + let touchedRegionHsv = Mat() + Imgproc.cvtColor(src: touchedRegionRgba, dst: touchedRegionHsv, code: .COLOR_RGB2HSV_FULL) + + // Calculate average color of touched region + blobColorHsv = Core.sum(src: touchedRegionHsv) + let pointCount = touchedRect.width*touchedRect.height + blobColorHsv = blobColorHsv.mul(Scalar.all(1.0/Double(pointCount))) + + blobColorRgba = converScalarHsv2Rgba(hsvColor: blobColorHsv) + + NSLog("Touched rgba color: (\(blobColorRgba.val[0]), \(blobColorRgba.val[1]), \( blobColorRgba.val[2]), \(blobColorRgba.val[3])") + + detector.setHsvColor(hsvColor: blobColorHsv) + + Imgproc.resize(src: detector.spectrum, dst: spectrum, dsize: SPECTRUM_SIZE, fx: 0, fy: 0, interpolation: InterpolationFlags.INTER_LINEAR_EXACT.rawValue) + + isColorSelected = true + } + } + + func converScalarHsv2Rgba(hsvColor:Scalar) -> Scalar { + let pointMatRgba = Mat() + let pointMatHsv = Mat(rows: 1, cols: 1, type: CvType.CV_8UC3, scalar: hsvColor) + Imgproc.cvtColor(src: pointMatHsv, dst: pointMatRgba, code: .COLOR_HSV2RGB_FULL, dstCn: 4) + let elementData = pointMatRgba.get(row: 0, col: 0) + return Scalar(vals: elementData as [NSNumber]) + } + +} diff --git a/samples/swift/ios/FaceDetection/FaceDetection.xcodeproj/project.pbxproj b/samples/swift/ios/FaceDetection/FaceDetection.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..bc07ef8910 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection.xcodeproj/project.pbxproj differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/samples/swift/ios/FaceDetection/FaceDetection.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..3d4c1e5525 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/samples/swift/ios/FaceDetection/FaceDetection/AppDelegate.swift b/samples/swift/ios/FaceDetection/FaceDetection/AppDelegate.swift new file mode 100644 index 0000000000..9965cc5086 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/AppDelegate.swift @@ -0,0 +1,19 @@ +// +// AppDelegate.swift +// +// Created by Giles Payne on 2020/03/02. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + +} diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/100.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000000..1e700dd332 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/114.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000000..f4312fc9c5 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/120.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000000..3b0a91b6a6 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/144.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000000..a5660b329c Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/152.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000000..99e2a23b4a Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/167.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000000..ea93ffc144 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/180.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000000..6d525448c3 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/20.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000000..d06f341d74 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/29.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000000..cee7ce05b5 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/40.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000000..0340a78dee Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/50.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/50.png new file mode 100644 index 0000000000..d143fdcce4 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/57.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000000..55d5723144 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/58.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000000..e2653bcfde Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/60.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000000..908cc0692c Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/72.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000000..d33f50de34 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/76.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000000..407d815ebb Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/80.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000000..6f3ad42bfc Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/87.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000000..767bb5810a Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..859fae7f90 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,158 @@ +{ + "images" : [ + { + "filename" : "40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "57.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "114.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "50.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "100.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "72.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "144.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "appstore.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/appstore.png b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/appstore.png new file mode 100644 index 0000000000..772f3ff185 Binary files /dev/null and b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/AppIcon.appiconset/appstore.png differ diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/Contents.json b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Base.lproj/LaunchScreen.storyboard b/samples/swift/ios/FaceDetection/FaceDetection/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..865e9329f3 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Base.lproj/Main.storyboard b/samples/swift/ios/FaceDetection/FaceDetection/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..071ca8c392 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/Base.lproj/Main.storyboard @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/swift/ios/FaceDetection/FaceDetection/DetectionBasedTracker.h b/samples/swift/ios/FaceDetection/FaceDetection/DetectionBasedTracker.h new file mode 100644 index 0000000000..ee9156076c --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/DetectionBasedTracker.h @@ -0,0 +1,32 @@ +// +// DetectionBasedTracker.h +// +// Created by Giles Payne on 2020/04/05. +// + +#pragma once + +#ifdef __cplusplus +#import +#endif + +#import + +@class Rect2i; +@class Mat; + +@interface DetectionBasedTracker : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithCascadeName:(NSString*)cascadeName minFaceSize:(int)minFaceSize; + +- (void)start; + +- (void)stop; + +- (void)setFaceSize:(int)size; + +- (void)detect:(Mat*)imageGray faces:(NSMutableArray*)faces; + +@end diff --git a/samples/swift/ios/FaceDetection/FaceDetection/DetectionBasedTracker.mm b/samples/swift/ios/FaceDetection/FaceDetection/DetectionBasedTracker.mm new file mode 100644 index 0000000000..2bc5a22956 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/DetectionBasedTracker.mm @@ -0,0 +1,85 @@ +// +// DetectionBasedTracker.mm +// +// Created by Giles Payne on 2020/04/05. +// + +#import "DetectionBasedTracker.h" +#import "Mat.h" +#import "Rect2i.h" +#import "CVObjcUtil.h" + +class CascadeDetectorAdapter: public cv::DetectionBasedTracker::IDetector +{ +public: + CascadeDetectorAdapter(cv::Ptr detector):IDetector(), Detector(detector) {} + + void detect(const cv::Mat &Image, std::vector &objects) + { + Detector->detectMultiScale(Image, objects, scaleFactor, minNeighbours, 0, minObjSize, maxObjSize); + } + + virtual ~CascadeDetectorAdapter() {} + +private: + CascadeDetectorAdapter(); + cv::Ptr Detector; +}; + + +struct DetectorAgregator +{ + cv::Ptr mainDetector; + cv::Ptr trackingDetector; + cv::Ptr tracker; + DetectorAgregator(cv::Ptr& _mainDetector, cv::Ptr& _trackingDetector):mainDetector(_mainDetector), trackingDetector(_trackingDetector) { + CV_Assert(_mainDetector); + CV_Assert(_trackingDetector); + cv::DetectionBasedTracker::Parameters DetectorParams; + tracker = cv::makePtr(mainDetector, trackingDetector, DetectorParams); + } +}; + +@implementation DetectionBasedTracker { + DetectorAgregator* agregator; +} + +- (instancetype)initWithCascadeName:(NSString*)cascadeName minFaceSize:(int)faceSize { + self = [super init]; + if (self) { + auto mainDetector = cv::makePtr(cv::makePtr(cascadeName.UTF8String)); + auto trackingDetector = cv::makePtr( + cv::makePtr(cascadeName.UTF8String)); + agregator = new DetectorAgregator(mainDetector, trackingDetector); + if (faceSize > 0) { + agregator->mainDetector->setMinObjectSize(cv::Size(faceSize, faceSize)); + } + } + return self; +} + +- (void)dealloc +{ + delete agregator; +} + +- (void)start { + agregator->tracker->run(); +} + +- (void)stop { + agregator->tracker->stop(); +} + +- (void)setFaceSize:(int)size { + agregator->mainDetector->setMinObjectSize(cv::Size(size, size)); +} + +- (void)detect:(Mat*)imageGray faces:(NSMutableArray*)faces { + std::vector rectFaces; + agregator->tracker->process(*((cv::Mat*)imageGray.nativePtr)); + agregator->tracker->getObjects(rectFaces); + CV2OBJC(cv::Rect, Rect2i, rectFaces, faces); +} + +@end diff --git a/samples/swift/ios/FaceDetection/FaceDetection/FaceDetection-Bridging-Header.h b/samples/swift/ios/FaceDetection/FaceDetection/FaceDetection-Bridging-Header.h new file mode 100644 index 0000000000..260d556fa5 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/FaceDetection-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "DetectionBasedTracker.h" diff --git a/samples/swift/ios/FaceDetection/FaceDetection/Info.plist b/samples/swift/ios/FaceDetection/FaceDetection/Info.plist new file mode 100644 index 0000000000..19b473c26f --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/Info.plist @@ -0,0 +1,49 @@ + + + + + NSCameraUsageDescription + Please allow access to the camera for image processing + UIUserInterfaceStyle + Light + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/samples/swift/ios/FaceDetection/FaceDetection/ViewController.swift b/samples/swift/ios/FaceDetection/FaceDetection/ViewController.swift new file mode 100644 index 0000000000..2fc6415b0c --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/ViewController.swift @@ -0,0 +1,90 @@ +// +// ViewController.swift +// +// Created by Giles Payne on 2020/03/02. +// + +import UIKit +import OpenCV + +extension Rect { + func rotateClockwise(parentHeight:Int32) { + let tmpX = self.x + self.x = parentHeight - (self.y + self.height) + self.y = tmpX + swapDims() + } + + func rotateCounterclockwise(parentWidth:Int32) { + let tmpY = self.y + self.y = parentWidth - (self.x + self.width) + self.x = tmpY + swapDims() + } + + func swapDims() { + let tmpWidth = self.width + self.width = self.height + self.height = tmpWidth + } +} + +class ViewController: UIViewController, CvVideoCameraDelegate2 { + + let swiftDetector = CascadeClassifier(filename: Bundle(for: ViewController.self).path(forResource:"lbpcascade_frontalface", ofType:"xml")!) + let nativeDetector = DetectionBasedTracker(cascadeName: Bundle(for: ViewController.self).path(forResource:"lbpcascade_frontalface", ofType:"xml")!, minFaceSize: 0) + var rgba: Mat? = nil + var gray: Mat = Mat() + var relativeFaceSize: Float = 0.2 + var absoluteFaceSize: Int32 = 0 + let FACE_RECT_COLOR = Scalar(0.0, 255.0, 0.0, 255.0) + let FACE_RECT_THICKNESS: Int32 = 4 + + func processImage(_ image: Mat!) { + let orientation = UIDevice.current.orientation + switch orientation { + case .landscapeLeft: + rgba = Mat() + Core.rotate(src: image, dst: rgba!, rotateCode: .ROTATE_90_COUNTERCLOCKWISE) + case .landscapeRight: + rgba = Mat() + Core.rotate(src: image, dst: rgba!, rotateCode: .ROTATE_90_CLOCKWISE) + default: + rgba = image + } + + Imgproc.cvtColor(src: rgba!, dst: gray, code: .COLOR_RGB2GRAY) + + if (absoluteFaceSize == 0) { + let height = gray.rows() + if (round(Float(height) * relativeFaceSize) > 0) { + absoluteFaceSize = Int32(round(Float(height) * relativeFaceSize)) + } + } + + let faces = NSMutableArray() + + swiftDetector.detectMultiScale(image: gray, objects: faces, scaleFactor: 1.1, minNeighbors: Int32(2), flags: Int32(2), minSize: Size(width: absoluteFaceSize, height: absoluteFaceSize), maxSize: Size()) + //nativeDetector!.detect(gray, faces: faces) + + for face in faces as! [Rect] { + if orientation == .landscapeLeft { + face.rotateClockwise(parentHeight: gray.rows()) + } else if orientation == .landscapeRight { + face.rotateCounterclockwise(parentWidth: gray.cols()) + } + Imgproc.rectangle(img: image, pt1: face.tl(), pt2: face.br(), color: FACE_RECT_COLOR, thickness: FACE_RECT_THICKNESS) + } + } + + var camera: CvVideoCamera2? = nil + + @IBOutlet weak var cameraHolder: UIView! + override func viewDidLoad() { + super.viewDidLoad() + camera = CvVideoCamera2(parentView: cameraHolder) + camera?.rotateVideo = true + camera?.delegate = self + camera?.start() + } +} diff --git a/samples/swift/ios/FaceDetection/FaceDetection/lbpcascade_frontalface.xml b/samples/swift/ios/FaceDetection/FaceDetection/lbpcascade_frontalface.xml new file mode 100644 index 0000000000..fc7648ef51 --- /dev/null +++ b/samples/swift/ios/FaceDetection/FaceDetection/lbpcascade_frontalface.xml @@ -0,0 +1,1505 @@ + + + + + BOOST + LBP + 24 + 24 + + GAB + 0.9950000047683716 + 0.5000000000000000 + 0.9500000000000000 + 1 + 100 + + 256 + 20 + + + <_> + 3 + -0.7520892024040222 + + + <_> + + 0 -1 46 -67130709 -21569 -1426120013 -1275125205 -21585 + -16385 587145899 -24005 + + -0.6543210148811340 0.8888888955116272 + + <_> + + 0 -1 13 -163512766 -769593758 -10027009 -262145 -514457854 + -193593353 -524289 -1 + + -0.7739216089248657 0.7278633713722229 + + <_> + + 0 -1 2 -363936790 -893203669 -1337948010 -136907894 + 1088782736 -134217726 -741544961 -1590337 + + -0.7068563103675842 0.6761534214019775 + + <_> + 4 + -0.4872078299522400 + + + <_> + + 0 -1 84 2147483647 1946124287 -536870913 2147450879 + 738132490 1061101567 243204619 2147446655 + + -0.8083735704421997 0.7685696482658386 + + <_> + + 0 -1 21 2147483647 263176079 1879048191 254749487 1879048191 + -134252545 -268435457 801111999 + + -0.7698410153388977 0.6592915654182434 + + <_> + + 0 -1 106 -98110272 1610939566 -285484400 -850010381 + -189334372 -1671954433 -571026695 -262145 + + -0.7506558895111084 0.5444605946540833 + + <_> + + 0 -1 48 -798690576 -131075 1095771153 -237144073 -65569 -1 + -216727745 -69206049 + + -0.7775990366935730 0.5465461611747742 + + <_> + 4 + -1.1592328548431396 + + + <_> + + 0 -1 47 -21585 -20549 -100818262 -738254174 -20561 -36865 + -151016790 -134238549 + + -0.5601882934570313 0.7743113040924072 + + <_> + + 0 -1 12 -286003217 183435247 -268994614 -421330945 + -402686081 1090387966 -286785545 -402653185 + + -0.6124526262283325 0.6978127956390381 + + <_> + + 0 -1 26 -50347012 970882927 -50463492 -1253377 -134218251 + -50364513 -33619992 -172490753 + + -0.6114496588706970 0.6537628173828125 + + <_> + + 0 -1 8 -273 -135266321 1877977738 -2088243418 -134217987 + 2146926575 -18910642 1095231247 + + -0.6854077577590942 0.5403239130973816 + + <_> + 5 + -0.7562355995178223 + + + <_> + + 0 -1 96 -1273 1870659519 -20971602 -67633153 -134250731 + 2004875127 -250 -150995969 + + -0.4051094949245453 0.7584033608436585 + + <_> + + 0 -1 33 -868162224 -76810262 -4262145 -257 1465211989 + -268959873 -2656269 -524289 + + -0.7388162612915039 0.5340843200683594 + + <_> + + 0 -1 57 -12817 -49 -541103378 -152950 -38993 -20481 -1153876 + -72478976 + + -0.6582943797111511 0.5339496731758118 + + <_> + + 0 -1 125 -269484161 -452984961 -319816180 -1594032130 -2111 + -990117891 -488975296 -520947741 + + -0.5981323719024658 0.5323504805564880 + + <_> + + 0 -1 53 557787431 670265215 -1342193665 -1075892225 + 1998528318 1056964607 -33570977 -1 + + -0.6498787999153137 0.4913350641727448 + + <_> + 5 + -0.8085358142852783 + + + <_> + + 0 -1 60 -536873708 880195381 -16842788 -20971521 -176687276 + -168427659 -16777260 -33554626 + + -0.5278195738792419 0.6946372389793396 + + <_> + + 0 -1 7 -1 -62981529 -1090591130 805330978 -8388827 -41945787 + -39577 -531118985 + + -0.5206505060195923 0.6329920291900635 + + <_> + + 0 -1 98 -725287348 1347747543 -852489 -16809993 1489881036 + -167903241 -1 -1 + + -0.7516061067581177 0.4232024252414703 + + <_> + + 0 -1 44 -32777 1006582562 -65 935312171 -8388609 -1078198273 + -1 733886267 + + -0.7639313936233521 0.4123568832874298 + + <_> + + 0 -1 24 -85474705 2138828511 -1036436754 817625855 + 1123369029 -58796809 -1013468481 -194513409 + + -0.5123769044876099 0.5791834592819214 + + <_> + 5 + -0.5549971461296082 + + + <_> + + 0 -1 42 -17409 -20481 -268457797 -134239493 -17473 -1 -21829 + -21846 + + -0.3763174116611481 0.7298233509063721 + + <_> + + 0 -1 6 -805310737 -2098262358 -269504725 682502698 + 2147483519 1740574719 -1090519233 -268472385 + + -0.5352765917778015 0.5659480094909668 + + <_> + + 0 -1 61 -67109678 -6145 -8 -87884584 -20481 -1073762305 + -50856216 -16849696 + + -0.5678374171257019 0.4961479902267456 + + <_> + + 0 -1 123 -138428633 1002418167 -1359008245 -1908670465 + -1346685918 910098423 -1359010520 -1346371657 + + -0.5706262588500977 0.4572288393974304 + + <_> + + 0 -1 9 -89138513 -4196353 1256531674 -1330665426 1216308261 + -36190633 33498198 -151796633 + + -0.5344601869583130 0.4672054052352905 + + <_> + 5 + -0.8776460289955139 + + + <_> + + 0 -1 105 1073769576 206601725 -34013449 -33554433 -789514004 + -101384321 -690225153 -264193 + + -0.7700348496437073 0.5943940877914429 + + <_> + + 0 -1 30 -1432340997 -823623681 -49153 -34291724 -269484035 + -1342767105 -1078198273 -1277955 + + -0.5043668746948242 0.6151274442672730 + + <_> + + 0 -1 35 -1067385040 -195758209 -436748425 -134217731 + -50855988 -129 -1 -1 + + -0.6808040738105774 0.4667325913906097 + + <_> + + 0 -1 119 832534325 -34111555 -26050561 -423659521 -268468364 + 2105014143 -2114244 -17367185 + + -0.4927591383457184 0.5401885509490967 + + <_> + + 0 -1 82 -1089439888 -1080524865 2143059967 -1114121 + -1140949004 -3 -2361356 -739516 + + -0.6445107460021973 0.4227822124958038 + + <_> + 6 + -1.1139287948608398 + + + <_> + + 0 -1 52 -1074071553 -1074003969 -1 -1280135430 -5324817 -1 + -335548482 582134442 + + -0.5307556986808777 0.6258179545402527 + + <_> + + 0 -1 99 -706937396 -705364068 -540016724 -570495027 + -570630659 -587857963 -33628164 -35848193 + + -0.5227634310722351 0.5049746036529541 + + <_> + + 0 -1 18 -2035630093 42119158 -268503053 -1671444 261017599 + 1325432815 1954394111 -805306449 + + -0.4983572661876679 0.5106441378593445 + + <_> + + 0 -1 111 -282529488 -1558073088 1426018736 -170526448 + -546832487 -5113037 -34243375 -570427929 + + -0.4990860521793366 0.5060507059097290 + + <_> + + 0 -1 92 1016332500 -606301707 915094269 -1080086049 + -1837027144 -1361600280 2147318747 1067975613 + + -0.5695009231567383 0.4460467398166657 + + <_> + + 0 -1 51 -656420166 -15413034 -141599534 -603435836 + 1505950458 -787556946 -79823438 -1326199134 + + -0.6590405106544495 0.3616424500942230 + + <_> + 7 + -0.8243625760078430 + + + <_> + + 0 -1 28 -901591776 -201916417 -262 -67371009 -143312112 + -524289 -41943178 -1 + + -0.4972776770591736 0.6027074456214905 + + <_> + + 0 -1 112 -4507851 -411340929 -268437513 -67502145 -17350859 + -32901 -71344315 -29377 + + -0.4383158981800079 0.5966237187385559 + + <_> + + 0 -1 69 -75894785 -117379438 -239063587 -12538500 1485072126 + 2076233213 2123118847 801906927 + + -0.6386105418205261 0.3977999985218048 + + <_> + + 0 -1 19 -823480413 786628589 -16876049 -1364262914 242165211 + 1315930109 -696268833 -455082829 + + -0.5512794256210327 0.4282079637050629 + + <_> + + 0 -1 73 -521411968 6746762 -1396236286 -2038436114 + -185612509 57669627 -143132877 -1041235973 + + -0.6418755054473877 0.3549866080284119 + + <_> + + 0 -1 126 -478153869 1076028979 -1645895615 1365298272 + -557859073 -339771473 1442574528 -1058802061 + + -0.4841901361942291 0.4668019413948059 + + <_> + + 0 -1 45 -246350404 -1650402048 -1610612745 -788400696 + 1467604861 -2787397 1476263935 -4481349 + + -0.5855734348297119 0.3879135847091675 + + <_> + 7 + -1.2237116098403931 + + + <_> + + 0 -1 114 -24819 1572863935 -16809993 -67108865 2146778388 + 1433927541 -268608444 -34865205 + + -0.2518476545810700 0.7088654041290283 + + <_> + + 0 -1 97 -1841359 -134271049 -32769 -5767369 -1116675 -2185 + -8231 -33603327 + + -0.4303432404994965 0.5283288359642029 + + <_> + + 0 -1 25 -1359507589 -1360593090 -1073778729 -269553812 + -809512977 1744707583 -41959433 -134758978 + + -0.4259553551673889 0.5440809130668640 + + <_> + + 0 -1 34 729753407 -134270989 -1140907329 -235200777 + 658456383 2147467263 -1140900929 -16385 + + -0.5605589151382446 0.4220733344554901 + + <_> + + 0 -1 134 -310380553 -420675595 -193005472 -353568129 + 1205338070 -990380036 887604324 -420544526 + + -0.5192656517028809 0.4399855434894562 + + <_> + + 0 -1 16 -1427119361 1978920959 -287119734 -487068946 + 114759245 -540578051 -707510259 -671660453 + + -0.5013077259063721 0.4570254683494568 + + <_> + + 0 -1 74 -738463762 -889949281 -328301948 -121832450 + -1142658284 -1863576559 2146417353 -263185 + + -0.4631414115428925 0.4790246188640595 + + <_> + 7 + -0.5544230937957764 + + + <_> + + 0 -1 113 -76228780 -65538 -1 -67174401 -148007 -33 -221796 + -272842924 + + -0.3949716091156006 0.6082032322883606 + + <_> + + 0 -1 110 369147696 -1625232112 2138570036 -1189900 790708019 + -1212613127 799948719 -4456483 + + -0.4855885505676270 0.4785369932651520 + + <_> + + 0 -1 37 784215839 -290015241 536832799 -402984963 + -1342414991 -838864897 -176769 -268456129 + + -0.4620285332202911 0.4989669024944305 + + <_> + + 0 -1 41 -486418688 -171915327 -340294900 -21938 -519766032 + -772751172 -73096060 -585322623 + + -0.6420643329620361 0.3624351918697357 + + <_> + + 0 -1 117 -33554953 -475332625 -1423463824 -2077230421 + -4849669 -2080505925 -219032928 -1071915349 + + -0.4820112884044647 0.4632140696048737 + + <_> + + 0 -1 65 -834130468 -134217476 -1349314083 -1073803559 + -619913764 -1449131844 -1386890321 -1979118423 + + -0.4465552568435669 0.5061788558959961 + + <_> + + 0 -1 56 -285249779 1912569855 -16530 -1731022870 -1161904146 + -1342177297 -268439634 -1464078708 + + -0.5190586447715759 0.4441480338573456 + + <_> + 7 + -0.7161560654640198 + + + <_> + + 0 -1 20 1246232575 1078001186 -10027057 60102 -277348353 + -43646987 -1210581153 1195769615 + + -0.4323809444904327 0.5663768053054810 + + <_> + + 0 -1 15 -778583572 -612921106 -578775890 -4036478 + -1946580497 -1164766570 -1986687009 -12103599 + + -0.4588732719421387 0.4547033011913300 + + <_> + + 0 -1 129 -1073759445 2013231743 -1363169553 -1082459201 + -1414286549 868185983 -1356133589 -1077936257 + + -0.5218553543090820 0.4111092388629913 + + <_> + + 0 -1 102 -84148365 -2093417722 -1204850272 564290299 + -67121221 -1342177350 -1309195902 -776734797 + + -0.4920000731945038 0.4326725304126740 + + <_> + + 0 -1 88 -25694458 67104495 -290216278 -168563037 2083877442 + 1702788383 -144191964 -234882162 + + -0.4494568109512329 0.4448510706424713 + + <_> + + 0 -1 59 -857980836 904682741 -1612267521 232279415 + 1550862252 -574825221 -357380888 -4579409 + + -0.5180826783180237 0.3888972699642181 + + <_> + + 0 -1 27 -98549440 -137838400 494928389 -246013630 939541351 + -1196072350 -620603549 2137216273 + + -0.6081240773200989 0.3333222270011902 + + <_> + 8 + -0.6743940711021423 + + + <_> + + 0 -1 29 -150995201 2071191945 -1302151626 536934335 + -1059008937 914128709 1147328110 -268369925 + + -0.1790193915367127 0.6605972051620483 + + <_> + + 0 -1 128 -134509479 1610575703 -1342177289 1861484541 + -1107833788 1577058173 -333558568 -136319041 + + -0.3681024610996246 0.5139749646186829 + + <_> + + 0 -1 70 -1 1060154476 -1090984524 -630918524 -539492875 + 779616255 -839568424 -321 + + -0.3217232525348663 0.6171553134918213 + + <_> + + 0 -1 4 -269562385 -285029906 -791084350 -17923776 235286671 + 1275504943 1344390399 -966276889 + + -0.4373284578323364 0.4358185231685638 + + <_> + + 0 -1 76 17825984 -747628419 595427229 1474759671 575672208 + -1684005538 872217086 -1155858277 + + -0.4404836893081665 0.4601220190525055 + + <_> + + 0 -1 124 -336593039 1873735591 -822231622 -355795238 + -470820869 -1997537409 -1057132384 -1015285005 + + -0.4294152259826660 0.4452161788940430 + + <_> + + 0 -1 54 -834212130 -593694721 -322142257 -364892500 + -951029539 -302125121 -1615106053 -79249765 + + -0.3973052501678467 0.4854526817798615 + + <_> + + 0 -1 95 1342144479 2147431935 -33554561 -47873 -855685912 -1 + 1988052447 536827383 + + -0.7054683566093445 0.2697997391223908 + + <_> + 9 + -1.2042298316955566 + + + <_> + + 0 -1 39 1431368960 -183437936 -537002499 -137497097 + 1560590321 -84611081 -2097193 -513 + + -0.5905947685241699 0.5101932883262634 + + <_> + + 0 -1 120 -1645259691 2105491231 2130706431 1458995007 + -8567536 -42483883 -33780003 -21004417 + + -0.4449204802513123 0.4490709304809570 + + <_> + + 0 -1 89 -612381022 -505806938 -362027516 -452985106 + 275854917 1920431639 -12600561 -134221825 + + -0.4693818688392639 0.4061094820499420 + + <_> + + 0 -1 14 -805573153 -161 -554172679 -530519488 -16779441 + 2000682871 -33604275 -150997129 + + -0.3600351214408875 0.5056326985359192 + + <_> + + 0 -1 67 6192 435166195 1467449341 2046691505 -1608493775 + -4755729 -1083162625 -71365637 + + -0.4459891915321350 0.4132415652275085 + + <_> + + 0 -1 86 -41689215 -3281034 1853357967 -420712635 -415924289 + -270209208 -1088293113 -825311232 + + -0.4466069042682648 0.4135067760944367 + + <_> + + 0 -1 80 -117391116 -42203396 2080374461 -188709 -542008165 + -356831940 -1091125345 -1073796897 + + -0.3394956290721893 0.5658645033836365 + + <_> + + 0 -1 75 -276830049 1378714472 -1342181951 757272098 + 1073740607 -282199241 -415761549 170896931 + + -0.5346512198448181 0.3584479391574860 + + <_> + + 0 -1 55 -796075825 -123166849 2113667055 -217530421 + -1107432194 -16385 -806359809 -391188771 + + -0.4379335641860962 0.4123645126819611 + + <_> + 10 + -0.8402050137519836 + + + <_> + + 0 -1 71 -890246622 15525883 -487690486 47116238 -1212319899 + -1291847681 -68159890 -469829921 + + -0.2670986354351044 0.6014143228530884 + + <_> + + 0 -1 31 -1361180685 -1898008841 -1090588811 -285410071 + -1074016265 -840443905 2147221487 -262145 + + -0.4149844348430634 0.4670888185501099 + + <_> + + 0 -1 40 1426190596 1899364271 2142731795 -142607505 + -508232452 -21563393 -41960001 -65 + + -0.4985891580581665 0.3719584941864014 + + <_> + + 0 -1 109 -201337965 10543906 -236498096 -746195597 + 1974565825 -15204415 921907633 -190058309 + + -0.4568729996681213 0.3965812027454376 + + <_> + + 0 -1 130 -595026732 -656401928 -268649235 -571490699 + -440600392 -133131 -358810952 -2004088646 + + -0.4770836830139160 0.3862601518630981 + + <_> + + 0 -1 66 941674740 -1107882114 1332789109 -67691015 + -1360463693 -1556612430 -609108546 733546933 + + -0.4877715110778809 0.3778986334800720 + + <_> + + 0 -1 49 -17114945 -240061474 1552871558 -82775604 -932393844 + -1308544889 -532635478 -99042357 + + -0.3721654713153839 0.4994400143623352 + + <_> + + 0 -1 133 -655906006 1405502603 -939205164 1884929228 + -498859222 559417357 -1928559445 -286264385 + + -0.3934195041656494 0.4769641458988190 + + <_> + + 0 -1 0 -335837777 1860677295 -90 -1946186226 931096183 + 251612987 2013265917 -671232197 + + -0.4323300719261169 0.4342164099216461 + + <_> + + 0 -1 103 37769424 -137772680 374692301 2002666345 -536176194 + -1644484728 807009019 1069089930 + + -0.4993278682231903 0.3665378093719482 + + <_> + 9 + -1.1974394321441650 + + + <_> + + 0 -1 43 -5505 2147462911 2143265466 -4511070 -16450 -257 + -201348440 -71333206 + + -0.3310225307941437 0.5624626278877258 + + <_> + + 0 -1 90 -136842268 -499330741 2015250980 -87107126 + -641665744 -788524639 -1147864792 -134892563 + + -0.5266560912132263 0.3704403042793274 + + <_> + + 0 -1 104 -146800880 -1780368555 2111170033 -140904684 + -16777551 -1946681885 -1646463595 -839131947 + + -0.4171888828277588 0.4540435671806335 + + <_> + + 0 -1 85 -832054034 -981663763 -301990281 -578814081 + -932319000 -1997406723 -33555201 -69206017 + + -0.4556705355644226 0.3704262077808380 + + <_> + + 0 -1 24 -118492417 -1209026825 1119023838 -1334313353 + 1112948738 -297319313 1378887291 -139469193 + + -0.4182529747486115 0.4267231225967407 + + <_> + + 0 -1 78 -1714382628 -2353704 -112094959 -549613092 + -1567058760 -1718550464 -342315012 -1074972227 + + -0.3625369668006897 0.4684656262397766 + + <_> + + 0 -1 5 -85219702 316836394 -33279 1904970288 2117267315 + -260901769 -621461759 -88607770 + + -0.4742925167083740 0.3689507246017456 + + <_> + + 0 -1 11 -294654041 -353603585 -1641159686 -50331921 + -2080899877 1145569279 -143132713 -152044037 + + -0.3666271567344666 0.4580127298831940 + + <_> + + 0 -1 32 1887453658 -638545712 -1877976819 -34320972 + -1071067983 -661345416 -583338277 1060190561 + + -0.4567637443542481 0.3894708156585693 + + <_> + 9 + -0.5733128190040588 + + + <_> + + 0 -1 122 -994063296 1088745462 -318837116 -319881377 + 1102566613 1165490103 -121679694 -134744129 + + -0.4055117964744568 0.5487945079803467 + + <_> + + 0 -1 68 -285233233 -538992907 1811935199 -369234005 -529 + -20593 -20505 -1561401854 + + -0.3787897229194641 0.4532003402709961 + + <_> + + 0 -1 58 -1335245632 1968917183 1940861695 536816369 + -1226071367 -570908176 457026619 1000020667 + + -0.4258328974246979 0.4202791750431061 + + <_> + + 0 -1 94 -1360318719 -1979797897 -50435249 -18646473 + -608879292 -805306691 -269304244 -17840167 + + -0.4561023116111755 0.4002747833728790 + + <_> + + 0 -1 87 2062765935 -16449 -1275080721 -16406 45764335 + -1090552065 -772846337 -570464322 + + -0.4314672648906708 0.4086346626281738 + + <_> + + 0 -1 127 -536896021 1080817663 -738234288 -965478709 + -2082767969 1290855887 1993822934 -990381609 + + -0.4174543321132660 0.4249868988990784 + + <_> + + 0 -1 3 -818943025 168730891 -293610428 -79249354 669224671 + 621166734 1086506807 1473768907 + + -0.4321364760398865 0.4090838730335236 + + <_> + + 0 -1 79 -68895696 -67107736 -1414315879 -841676168 + -619843344 -1180610531 -1081990469 1043203389 + + -0.5018386244773865 0.3702533841133118 + + <_> + + 0 -1 116 -54002134 -543485719 -2124882422 -1437445858 + -115617074 -1195787391 -1096024366 -2140472445 + + -0.5037505626678467 0.3564981222152710 + + <_> + 9 + -0.4892596900463104 + + + <_> + + 0 -1 132 -67113211 2003808111 1862135111 846461923 -2752 + 2002237273 -273154752 1937223539 + + -0.2448196411132813 0.5689709186553955 + + <_> + + 0 -1 62 1179423888 -78064940 -611839555 -539167899 + -1289358360 -1650810108 -892540499 -1432827684 + + -0.4633283913135529 0.3587929606437683 + + <_> + + 0 -1 23 -285212705 -78450761 -656212031 -264050110 -27787425 + -1334349961 -547662981 -135796924 + + -0.3731099069118500 0.4290455579757690 + + <_> + + 0 -1 77 341863476 403702016 -550588417 1600194541 + -1080690735 951127993 -1388580949 -1153717473 + + -0.3658909499645233 0.4556473195552826 + + <_> + + 0 -1 22 -586880702 -204831512 -100644596 -39319550 + -1191150794 705692513 457203315 -75806957 + + -0.5214384198188782 0.3221037387847900 + + <_> + + 0 -1 72 -416546870 545911370 -673716192 -775559454 + -264113598 139424 -183369982 -204474641 + + -0.4289036989212036 0.4004956185817719 + + <_> + + 0 -1 50 -1026505020 -589692154 -1740499937 -1563770497 + 1348491006 -60710713 -1109853489 -633909413 + + -0.4621542394161224 0.3832748532295227 + + <_> + + 0 -1 108 -1448872304 -477895040 -1778390608 -772418127 + -1789923416 -1612057181 -805306693 -1415842113 + + -0.3711548447608948 0.4612701535224915 + + <_> + + 0 -1 92 407905424 -582449988 52654751 -1294472 -285103725 + -74633006 1871559083 1057955850 + + -0.5180652141571045 0.3205870389938355 + + <_> + 10 + -0.5911940932273865 + + + <_> + + 0 -1 81 4112 -1259563825 -846671428 -100902460 1838164148 + -74153752 -90653988 -1074263896 + + -0.2592592537403107 0.5873016119003296 + + <_> + + 0 -1 1 -285216785 -823206977 -1085589 -1081346 1207959293 + 1157103471 2097133565 -2097169 + + -0.3801195919513702 0.4718827307224274 + + <_> + + 0 -1 121 -12465 -536875169 2147478367 2130706303 -37765492 + -866124467 -318782328 -1392509185 + + -0.3509117066860199 0.5094807147979736 + + <_> + + 0 -1 38 2147449663 -20741 -16794757 1945873146 -16710 -1 + -8406341 -67663041 + + -0.4068757295608521 0.4130136370658875 + + <_> + + 0 -1 17 -155191713 866117231 1651407483 548272812 -479201468 + -447742449 1354229504 -261884429 + + -0.4557141065597534 0.3539792001247406 + + <_> + + 0 -1 100 -225319378 -251682065 -492783986 -792341777 + -1287261695 1393643841 -11274182 -213909521 + + -0.4117803275585175 0.4118592441082001 + + <_> + + 0 -1 63 -382220122 -2002072729 -51404800 -371201558 + -923011069 -2135301457 -2066104743 -1042557441 + + -0.4008397758007050 0.4034757018089294 + + <_> + + 0 -1 101 -627353764 -48295149 1581203952 -436258614 + -105268268 -1435893445 -638126888 -1061107126 + + -0.5694189667701721 0.2964762747287750 + + <_> + + 0 -1 118 -8399181 1058107691 -621022752 -251003468 -12582915 + -574619739 -994397789 -1648362021 + + -0.3195341229438782 0.5294018983840942 + + <_> + + 0 -1 92 -348343812 -1078389516 1717960437 364735981 + -1783841602 -4883137 -457572354 -1076950384 + + -0.3365339040756226 0.5067458748817444 + + <_> + 10 + -0.7612916231155396 + + + <_> + + 0 -1 10 -1976661318 -287957604 -1659497122 -782068 43591089 + -453637880 1435470000 -1077438561 + + -0.4204545319080353 0.5165745615959168 + + <_> + + 0 -1 131 -67110925 14874979 -142633168 -1338923040 + 2046713291 -2067933195 1473503712 -789579837 + + -0.3762553930282593 0.4075302779674530 + + <_> + + 0 -1 83 -272814301 -1577073 -1118685 -305156120 -1052289 + -1073813756 -538971154 -355523038 + + -0.4253497421741486 0.3728055357933044 + + <_> + + 0 -1 135 -2233 -214486242 -538514758 573747007 -159390971 + 1994225489 -973738098 -203424005 + + -0.3601998090744019 0.4563256204128265 + + <_> + + 0 -1 115 -261031688 -1330369299 -641860609 1029570301 + -1306461192 -1196149518 -1529767778 683139823 + + -0.4034293889999390 0.4160816967487335 + + <_> + + 0 -1 64 -572993608 -34042628 -417865 -111109 -1433365268 + -19869715 -1920939864 -1279457063 + + -0.3620899617671967 0.4594142735004425 + + <_> + + 0 -1 36 -626275097 -615256993 1651946018 805366393 + 2016559730 -430780849 -799868165 -16580645 + + -0.3903816640377045 0.4381459355354309 + + <_> + + 0 -1 93 1354797300 -1090957603 1976418270 -1342502178 + -1851873892 -1194637077 -1153521668 -1108399474 + + -0.3591445386409760 0.4624078869819641 + + <_> + + 0 -1 91 68157712 1211368313 -304759523 1063017136 798797750 + -275513546 648167355 -1145357350 + + -0.4297670423984528 0.4023293554782867 + + <_> + + 0 -1 107 -546318240 -1628569602 -163577944 -537002306 + -545456389 -1325465645 -380446736 -1058473386 + + -0.5727006793022156 0.2995934784412384 + + <_> + + 0 0 3 5 + <_> + + 0 0 4 2 + <_> + + 0 0 6 3 + <_> + + 0 1 2 3 + <_> + + 0 1 3 3 + <_> + + 0 1 3 7 + <_> + + 0 4 3 3 + <_> + + 0 11 3 4 + <_> + + 0 12 8 4 + <_> + + 0 14 4 3 + <_> + + 1 0 5 3 + <_> + + 1 1 2 2 + <_> + + 1 3 3 1 + <_> + + 1 7 4 4 + <_> + + 1 12 2 2 + <_> + + 1 13 4 1 + <_> + + 1 14 4 3 + <_> + + 1 17 3 2 + <_> + + 2 0 2 3 + <_> + + 2 1 2 2 + <_> + + 2 2 4 6 + <_> + + 2 3 4 4 + <_> + + 2 7 2 1 + <_> + + 2 11 2 3 + <_> + + 2 17 3 2 + <_> + + 3 0 2 2 + <_> + + 3 1 7 3 + <_> + + 3 7 2 1 + <_> + + 3 7 2 4 + <_> + + 3 18 2 2 + <_> + + 4 0 2 3 + <_> + + 4 3 2 1 + <_> + + 4 6 2 1 + <_> + + 4 6 2 5 + <_> + + 4 7 5 2 + <_> + + 4 8 4 3 + <_> + + 4 18 2 2 + <_> + + 5 0 2 2 + <_> + + 5 3 4 4 + <_> + + 5 6 2 5 + <_> + + 5 9 2 2 + <_> + + 5 10 2 2 + <_> + + 6 3 4 4 + <_> + + 6 4 4 3 + <_> + + 6 5 2 3 + <_> + + 6 5 2 5 + <_> + + 6 5 4 3 + <_> + + 6 6 4 2 + <_> + + 6 6 4 4 + <_> + + 6 18 1 2 + <_> + + 6 21 2 1 + <_> + + 7 0 3 7 + <_> + + 7 4 2 3 + <_> + + 7 9 5 1 + <_> + + 7 21 2 1 + <_> + + 8 0 1 4 + <_> + + 8 5 2 2 + <_> + + 8 5 3 2 + <_> + + 8 17 3 1 + <_> + + 8 18 1 2 + <_> + + 9 0 5 3 + <_> + + 9 2 2 6 + <_> + + 9 5 1 1 + <_> + + 9 11 1 1 + <_> + + 9 16 1 1 + <_> + + 9 16 2 1 + <_> + + 9 17 1 1 + <_> + + 9 18 1 1 + <_> + + 10 5 1 2 + <_> + + 10 5 3 3 + <_> + + 10 7 1 5 + <_> + + 10 8 1 1 + <_> + + 10 9 1 1 + <_> + + 10 10 1 1 + <_> + + 10 10 1 2 + <_> + + 10 14 3 3 + <_> + + 10 15 1 1 + <_> + + 10 15 2 1 + <_> + + 10 16 1 1 + <_> + + 10 16 2 1 + <_> + + 10 17 1 1 + <_> + + 10 21 1 1 + <_> + + 11 3 2 2 + <_> + + 11 5 1 2 + <_> + + 11 5 3 3 + <_> + + 11 5 4 6 + <_> + + 11 6 1 1 + <_> + + 11 7 2 2 + <_> + + 11 8 1 2 + <_> + + 11 10 1 1 + <_> + + 11 10 1 2 + <_> + + 11 15 1 1 + <_> + + 11 17 1 1 + <_> + + 11 18 1 1 + <_> + + 12 0 2 2 + <_> + + 12 1 2 5 + <_> + + 12 2 4 1 + <_> + + 12 3 1 3 + <_> + + 12 7 3 4 + <_> + + 12 10 3 2 + <_> + + 12 11 1 1 + <_> + + 12 12 3 2 + <_> + + 12 14 4 3 + <_> + + 12 17 1 1 + <_> + + 12 21 2 1 + <_> + + 13 6 2 5 + <_> + + 13 7 3 5 + <_> + + 13 11 3 2 + <_> + + 13 17 2 2 + <_> + + 13 17 3 2 + <_> + + 13 18 1 2 + <_> + + 13 18 2 2 + <_> + + 14 0 2 2 + <_> + + 14 1 1 3 + <_> + + 14 2 3 2 + <_> + + 14 7 2 1 + <_> + + 14 13 2 1 + <_> + + 14 13 3 3 + <_> + + 14 17 2 2 + <_> + + 15 0 2 2 + <_> + + 15 0 2 3 + <_> + + 15 4 3 2 + <_> + + 15 4 3 6 + <_> + + 15 6 3 2 + <_> + + 15 11 3 4 + <_> + + 15 13 3 2 + <_> + + 15 17 2 2 + <_> + + 15 17 3 2 + <_> + + 16 1 2 3 + <_> + + 16 3 2 4 + <_> + + 16 6 1 1 + <_> + + 16 16 2 2 + <_> + + 17 1 2 2 + <_> + + 17 1 2 5 + <_> + + 17 12 2 2 + <_> + + 18 0 2 2 +