iOS:Retrieve rectangle shaped image from the background image

Here is a full answer using a small wrapper class to separate the c++ from objective-c code.

I had to raise another question on stackoverflow to deal with my poor c++ knowledge – but I have worked out everything we need to interface c++ cleanly with objective-c code, using the squares.cpp sample code as an example. The aim is to keep the original c++ code as pristine as possible, and to keep the bulk of the work with openCV in pure c++ files for (im)portability.

I have left my original answer in place as this seems to go beyond an edit. The complete demo project is on github

CVViewController.h / CVViewController.m

  • pure Objective-C

  • communicates with openCV c++ code via a WRAPPER… it neither knows nor cares that c++ is processing these method calls behind the wrapper.

CVWrapper.h / CVWrapper.mm

  • objective-C++

does as little as possible, really only two things…

  • calls to UIImage objC++ categories to convert to and from UIImage <> cv::Mat
  • mediates between CVViewController’s obj-C methods and CVSquares c++ (class) function calls

CVSquares.h / CVSquares.cpp

  • pure C++
  • CVSquares.cpp declares public functions inside a class definition (in this case, one static function).
    This replaces the work of main{} in the original file.
  • We try to keep CVSquares.cpp as close as possible to the C++ original for portability.

CVViewController.m

//remove 'magic numbers' from original C++ source so we can manipulate them from obj-C
#define TOLERANCE 0.01
#define THRESHOLD 50
#define LEVELS 9

UIImage* image =
        [CVSquaresWrapper detectedSquaresInImage:self.image
                                       tolerance:TOLERANCE
                                       threshold:THRESHOLD
                                          levels:LEVELS];

CVSquaresWrapper.h

//  CVSquaresWrapper.h

#import <Foundation/Foundation.h>

@interface CVSquaresWrapper : NSObject

+ (UIImage*) detectedSquaresInImage:(UIImage*)image
                          tolerance:(CGFloat)tolerance
                          threshold:(NSInteger)threshold
                             levels:(NSInteger)levels;

@end

CVSquaresWrapper.mm

//  CVSquaresWrapper.mm
//  wrapper that talks to c++ and to obj-c classes

#import "CVSquaresWrapper.h"
#import "CVSquares.h"
#import "UIImage+OpenCV.h"

@implementation CVSquaresWrapper

+ (UIImage*) detectedSquaresInImage:(UIImage*) image
                          tolerance:(CGFloat)tolerance
                          threshold:(NSInteger)threshold
                             levels:(NSInteger)levels
{
    UIImage* result = nil;

        //convert from UIImage to cv::Mat openCV image format
        //this is a category on UIImage
    cv::Mat matImage = [image CVMat]; 


        //call the c++ class static member function
        //we want this function signature to exactly 
        //mirror the form of the calling method 
    matImage = CVSquares::detectedSquaresInImage (matImage, tolerance, threshold, levels);


        //convert back from cv::Mat openCV image format
        //to UIImage image format (category on UIImage)
    result = [UIImage imageFromCVMat:matImage]; 

    return result;
}

@end

CVSquares.h

//  CVSquares.h

#ifndef __OpenCVClient__CVSquares__
#define __OpenCVClient__CVSquares__

    //class definition
    //in this example we do not need a class 
    //as we have no instance variables and just one static function. 
    //We could instead just declare the function but this form seems clearer

class CVSquares
{
public:
    static cv::Mat detectedSquaresInImage (cv::Mat image, float tol, int threshold, int levels);
};

#endif /* defined(__OpenCVClient__CVSquares__) */

CVSquares.cpp

//  CVSquares.cpp

#include "CVSquares.h"

using namespace std;
using namespace cv;

static int thresh = 50, N = 11;
static float tolerance = 0.01;

    //declarations added so that we can move our 
    //public function to the top of the file
static void findSquares(  const Mat& image,   vector<vector<Point> >& squares );
static void drawSquares( Mat& image, vector<vector<Point> >& squares );

    //this public function performs the role of 
    //main{} in the original file (main{} is deleted)
cv::Mat CVSquares::detectedSquaresInImage (cv::Mat image, float tol, int threshold, int levels)
{
    vector<vector<Point> > squares;

    if( image.empty() )
        {
        cout << "Couldn't load " << endl;
        }

    tolerance = tol;
    thresh = threshold;
    N = levels;
    findSquares(image, squares);
    drawSquares(image, squares);

    return image;
}


// the rest of this file is identical to the original squares.cpp except:
// main{} is removed
// this line is removed from drawSquares: 
// imshow(wndname, image); 
// (obj-c will do the drawing)

UIImage+OpenCV.h

The UIImage category is an objC++ file containing the code to convert between UIImage and cv::Mat image formats. This is where you move your two methods -(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat and - (cv::Mat)cvMatWithImage:(UIImage *)image

//UIImage+OpenCV.h

#import <UIKit/UIKit.h>

@interface UIImage (UIImage_OpenCV)

    //cv::Mat to UIImage
+ (UIImage *)imageFromCVMat:(cv::Mat&)cvMat;

    //UIImage to cv::Mat
- (cv::Mat)CVMat;


@end        

The method implementations here are unchanged from your code (although we don’t pass a UIImage in to convert, instead we refer to self)

Leave a Comment