mobile_local_restore/ios/Wrapper.mm

459 lines
16 KiB
Plaintext

//
// Wrapper.mm
// ARTagTest
//
// Created by Raghulraj Palanisamy on 19/05/24.
//
#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#import <opencv2/core.hpp>
#import <opencv2/videoio/cap_ios.h>
#import <opencv2/imgcodecs/ios.h>
#import <opencv2/imgproc/imgproc.hpp>
#import <opencv2/aruco.h>
#include <opencv2/imgproc.hpp>
#include <opencv2/core/types.hpp>
// #include "aruco.hpp"
#import <opencv2/calib3d.hpp>
#endif
#import "Wrapper.h"
#import <UIKit/UIKit.h>
#import <SceneKit/SceneKit.h>
#import <CoreVideo/CoreVideo.h>
#import <CoreMedia/CoreMedia.h>
using namespace cv;
using namespace std;
@implementation Wrapper {
cv::Mat gtpl;
}
+ (NSString *)openCVVersionString {
return [NSString stringWithFormat:@"ARTAG Library Version %s", CV_VERSION];
}
+ (UIImage *)processImage:(UIImage *)image {
cv::Mat cvImage;
UIImageToMat(image, cvImage);
// Check if the image is valid
if (cvImage.empty()) {
NSLog(@"Error: Image is empty");
return image;
}
// Perform OpenCV operations on cvImage
cv::Mat grayImage;
cv::cvtColor(cvImage, grayImage, cv::COLOR_BGR2GRAY);
// ArUco marker detection
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners, rejectedCandidates;
cv::aruco::DetectorParameters detectorParams = cv::aruco::DetectorParameters();
cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_7X7_1000);
cv::aruco::ArucoDetector detector(dictionary, detectorParams);
detector.detectMarkers(grayImage, markerCorners, markerIds, rejectedCandidates);
if (!markerIds.empty()) {
NSLog(@"Detected ArTag IDs:");
for (int markerId : markerIds) {
NSLog(@"%d", markerId);
}
} else {
}
cv::Mat outputImage = grayImage.clone();
// Draw detected markers
if (!markerIds.empty()) {
//cv::aruco::drawDetectedMarkers(cvImage, markerCorners, markerIds);
cv::aruco::drawDetectedMarkers(outputImage, markerCorners, markerIds);
NSLog(@"Detected ArTag Marker");
// Draw custom text above each marker
for (size_t i = 0; i < markerIds.size(); i++) {
cv::Point2f corner = markerCorners[i][0]; // Use the first corner for positioning the text
std::string text = std::to_string(markerIds[i]); // Your custom text here
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
double fontScale = 1.0;
int thickness = 2;
cv::Point textPosition(corner.x, corner.y - 20); // Adjust the Y-coordinate as needed
cv::Scalar textColor(0, 0, 255); // Red color for text
cv::putText(outputImage, text, textPosition, fontFace, fontScale, textColor, thickness);
}
}
// Example: Save the processed image
//UIImage *processedImage = MatToUIImage(grayImage);
// UIImageWriteToSavedPhotosAlbum(processedImage, nil, nil, nil);
// Convert processed Mat back to UIImage
UIImage *processedImage = MatToUIImage(outputImage);
return processedImage;
}
+ (NSArray *)detectMarkers:(UIImage *)image {
cv::Mat cvImage;
UIImageToMat(image, cvImage);
// Check if the image is valid
if (cvImage.empty()) {
NSLog(@"Error: Image is empty");
return nil;
}
// Convert the image to grayscale
cv::Mat grayImage;
cv::cvtColor(cvImage, grayImage, cv::COLOR_BGR2GRAY);
// ArUco marker detection
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners, rejectedCandidates;
// Set up the ArUco detector
cv::aruco::DetectorParameters detectorParams = cv::aruco::DetectorParameters();
cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_7X7_1000);
cv::aruco::ArucoDetector detector(dictionary, detectorParams);
detector.detectMarkers(grayImage, markerCorners, markerIds, rejectedCandidates);
cv::Mat outputImage = grayImage.clone();
cv::aruco::drawDetectedMarkers(outputImage, markerCorners, markerIds);
if (!markerIds.empty()) {
NSLog(@"Detected ArTag IDs:");
for (int markerId : markerIds) {
NSLog(@"%d", markerId);
}
}
NSMutableArray *result = [NSMutableArray array];
for (size_t i = 0; i < markerIds.size(); i++) {
NSMutableDictionary *marker = [NSMutableDictionary dictionary];
marker[@"id"] = @(markerIds[i]);
NSMutableArray *corners = [NSMutableArray array];
for (const auto &corner : markerCorners[i]) {
[corners addObject:[NSValue valueWithCGPoint:CGPointMake(corner.x, corner.y)]];
}
marker[@"corners"] = corners;
[result addObject:marker];
}
return result;
}
+ (NSDictionary *)drawDetectedMarkersOnImage:(UIImage *)image startScan:(BOOL)startScan CARD_SCANNING_TYPE:(int)CARD_SCANNING_TYPE{
cv::Mat colorImageRGBA;
cv::Mat colorImage;
cv::Mat grayImage;
cv::Mat camMatrix = cv::Mat::eye(3, 3, CV_64FC1);;
cv::Mat distCoeffs = cv::Mat::zeros(5, 1, CV_64FC1);
float markerLength = 0.1; // Adjust marker length based on your setup
// Set coordinate system
cv::Mat objPoints(4, 1, CV_32FC3);
objPoints.ptr<Vec3f>(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0);
UIImageToMat(image, colorImageRGBA);
NSMutableArray *markerIdsArray = [NSMutableArray array];
NSMutableArray *resultsArray = [NSMutableArray array];
// Check if the image is valid
if (colorImageRGBA.empty()) {
NSLog(@"Error: Image is empty");
//return image;
return @{@"image": image, @"markerIds": markerIdsArray};
}
// Convert the image to grayscale
cv::cvtColor(colorImageRGBA, grayImage, cv::COLOR_BGR2GRAY);
cv::cvtColor(colorImageRGBA, colorImage, cv::COLOR_RGBA2RGB);
// ArUco marker detection
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners, rejectedCandidates;
// Set up the ArUco detector
cv::aruco::DetectorParameters detectorParams = cv::aruco::DetectorParameters();
cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_7X7_1000);
cv::aruco::ArucoDetector detector(dictionary, detectorParams);
detector.detectMarkers(colorImage, markerCorners, markerIds, rejectedCandidates);
size_t nMarkers = markerCorners.size();
//vector<Vec3d> rvecs(nMarkers), tvecs(nMarkers);
if (!markerIds.empty() && startScan) {
// for (size_t i = 0; i < nMarkers; i++) {
// solvePnP(objPoints, markerCorners.at(i), camMatrix, distCoeffs, rvecs.at(i), tvecs.at(i));
// }
cv::aruco::drawDetectedMarkers(colorImage, markerCorners, markerIds);
for (int markerId : markerIds) {
[markerIdsArray addObject:@(markerId)];
}
vector<cv::Vec3d> rvecs, tvecs;
// NSLog(@"Detected ArTag IDs:");
// for (int markerId : markerIds) {
// NSLog(@"%d", markerId);
// }
if (!camMatrix.empty() && !distCoeffs.empty()) {
cv::aruco::estimatePoseSingleMarkers(markerCorners, markerLength, camMatrix, distCoeffs, rvecs, tvecs);
if (markerCorners.size() > 0) {
for (size_t i =0; i< markerCorners.size(); i++ ){
std::vector<cv::Point2f>& corners = markerCorners[i]; // Reference to the i-th marker's corners
CGPoint topLeft = topLeftCorner(corners);
CGPoint topRight = topRightCorner(corners);
CGPoint bottomLeft = bottomLeftCorner(corners);
CGPoint bottomRight = bottomRightCorner(corners);
// Example printing results
// NSLog(@"Top Left: (%f, %f)", topLeft.x, topLeft.y);
// NSLog(@"Top Right: (%f, %f)", topRight.x, topRight.y);
// NSLog(@"Bottom Left: (%f, %f)", bottomLeft.x, bottomLeft.y);
// NSLog(@"Bottom Right: (%f, %f)", bottomRight.x, bottomRight.y);
// Calculate center points
double centerX = findCenterX(topLeft, topRight, bottomLeft, bottomRight);
double centerY = findCenterY(topLeft, topRight, bottomLeft, bottomRight);
int angle = onCalculateAngle(corners, 0);
NSLog(@"Angle: %d", angle);
NSString* options = onFindObjectiveOptions(angle, markerIds[i]); // to find the Objective Options from single marker
NSLog(@"Objective Options: %@", options);
NSLog(@"Objective ID: %d", markerIds[i]);
NSDictionary *markerDict = @{
@"card_id": @(markerIds[i]),
@"answer":options
};
NSLog(@"markerDict: %@", markerDict);
[resultsArray addObject:markerDict];
// Display options on the image
if (CARD_SCANNING_TYPE == 1 || CARD_SCANNING_TYPE == 2){
displayOptions(colorImage, options, centerX, centerY);
}
displayMarkerID(colorImage, topLeft, markerIds[i]);
// Iterate through corners of the i-th marker
for (size_t j = 0; j < corners.size(); j++) {
const cv::Point2f& corner = corners[j]; // Access the j-th corner point
// Convert cv::Point2f to CGPoint using helper function
CGPoint point = convertPoint2fToCGPoint(corner);
// Use the CGPoint as needed (e.g., store in an NSArray, output to console, etc.)
// NSLog(@"Marker %zu, Point %zu: (%f, %f)", i, j, point.x, point.y);
}
}
for (int i = 0; i < markerIds.size(); i++) {
// NSLog(@"Marker ID %d: rvec = (%f, %f, %f), tvec = (%f, %f, %f)",
// markerIds[i], rvecs[i][0], rvecs[i][1], rvecs[i][2],
// tvecs[i][0], tvecs[i][1], tvecs[i][2]);
// cv::drawFrameAxes(colorImage, camMatrix, distCoeffs, rvecs[i], tvecs[i], markerLength * 1.5f, 2);
}
}
}
}
UIImage *processedImage = MatToUIImage(colorImage);
//return processedImage;
return @{@"image": processedImage, @"markerIds": markerIdsArray, @"resultsArray": resultsArray};
}
// Function to convert cv::Point2f to CGPoint
CGPoint convertPoint2fToCGPoint(const cv::Point2f& point2f) {
float x = point2f.x;
float y = point2f.y;
return CGPointMake(x, y);
}
int onCalculateAngle(const std::vector<cv::Point2f>& corners, int trigger) {
if (corners.size() >= 2) {
cv::Point2f pt1 = corners[0];
cv::Point2f pt2 = corners[1];
double x = pt2.x - pt1.x;
double y = pt2.y - pt1.y;
double angle = atan2(y, x) * 180 / M_PI; // Convert radians to degrees
int angleInt = 0;
if (angle > 0) {
angleInt = (int)angle; // Angle from 0 to 180 degrees
} else if (angle < 0) {
angleInt = 180 + (180 + (int)angle); // Angle from -180 to -1 degrees
}
return angleInt;
} else {
NSLog(@"Insufficient corners provided");
return 0; // Return default angle or handle error
}
}
NSString* onFindObjectiveOptions(int angle, int id) {
if (angle > 46 && angle < 135) { // find the East
return @"B"; // B East
} else if (angle > 136 && angle < 225) { // find the North
return @"C"; //C North
} else if (angle > 226 && angle < 315) { // find the West
return @"D"; // D West
} else if (angle > 316 || angle < 45) { // find the South
return @"A"; // A South
}
return @""; // Default empty string if no condition is met
}
// Function to find the top left corner from marker corners
CGPoint topLeftCorner(const std::vector<cv::Point2f>& corners) {
if (corners.size() > 0) {
float x = corners[0].x;
float y = corners[0].y;
NSLog(@"Top Left Corner: (%f, %f)", x, y);
return CGPointMake(x, y);
} else {
NSLog(@"Invalid corner data");
return CGPointMake(0, 0); // Return default point or handle error
}
}
// Function to find the top right corner from marker corners
CGPoint topRightCorner(const std::vector<cv::Point2f>& corners) {
if (corners.size() > 1) {
float x = corners[1].x;
float y = corners[1].y;
NSLog(@"Top Right Corner: (%f, %f)", x, y);
return CGPointMake(x, y);
} else {
NSLog(@"Invalid corner data");
return CGPointMake(0, 0); // Return default point or handle error
}
}
// Function to find the bottom left corner from marker corners
CGPoint bottomLeftCorner(const std::vector<cv::Point2f>& corners) {
if (corners.size() > 2) {
float x = corners[2].x;
float y = corners[2].y;
NSLog(@"Bottom Left Corner: (%f, %f)", x, y);
return CGPointMake(x, y);
} else {
NSLog(@"Invalid corner data");
return CGPointMake(0, 0); // Return default point or handle error
}
}
// Function to find the bottom right corner from marker corners
CGPoint bottomRightCorner(const std::vector<cv::Point2f>& corners) {
if (corners.size() > 3) {
float x = corners[3].x;
float y = corners[3].y;
NSLog(@"Bottom Right Corner: (%f, %f)", x, y);
return CGPointMake(x, y);
} else {
NSLog(@"Invalid corner data");
return CGPointMake(0, 0); // Return default point or handle error
}
}
// Function to find the centerX based on the corners
double findCenterX(CGPoint topLeft, CGPoint topRight, CGPoint bottomLeft, CGPoint bottomRight) {
double centerXTotal = topLeft.x + topRight.x + bottomLeft.x + bottomRight.x;
return centerXTotal / 4;
}
// Function to find the centerY based on the corners
double findCenterY(CGPoint topLeft, CGPoint topRight, CGPoint bottomLeft, CGPoint bottomRight) {
double centerYTotal = topLeft.y + topRight.y + bottomLeft.y + bottomRight.y;
return centerYTotal / 4;
}
void displayMarkerID(cv::Mat& image, CGPoint topLeftCorner, int markerID) {
//cv::Point2f topLeftCorner = corners[0];
int fontFace = cv::FONT_HERSHEY_SIMPLEX;
double fontScale = 1;
int thickness = 2;
cv::Scalar color(0, 255, 0); // Green color for the text
// Draw the text
std::string text = std::to_string(markerID);
int baseline;
cv::Size textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);
// Position the text at the top-left corner of the marker
cv::Point textOrg(topLeftCorner.x, topLeftCorner.y - textSize.height);
// Draw the text on the image
cv::putText(image, text, textOrg, fontFace, fontScale, color, thickness);
}
// Function to display options on the image
void displayOptions(cv::Mat &rgb, NSString *onFindObjectiveAnswer, double centerX, double centerY) {
// Convert NSString to std::string
std::string text([onFindObjectiveAnswer UTF8String]);
// Position for the text
cv::Point textOrg(centerX - 15, centerY + 15);
// Font parameters
int fontFace = cv::FONT_HERSHEY_COMPLEX;
double fontScale = 2;
cv::Scalar color(0, 255, 0); // Green color
int thickness = 2;
// Put the text on the image
cv::putText(rgb, text, textOrg, fontFace, fontScale, color, thickness);
}
@end