Book Image

Instant OpenCV for iOS

4 (1)
Book Image

Instant OpenCV for iOS

4 (1)

Overview of this book

Computer vision on mobile devices is becoming more and more popular. Personal gadgets are now powerful enough to process high-resolution images, stitch panoramas, and detect and track objects. OpenCV, with its decent performance and wide range of functionality, can be an extremely useful tool in the hands of iOS developers. Instant OpenCV for iOS is a practical guide that walks you through every important step for building a computer vision application for the iOS platform. It will help you to port your OpenCV code, profile and optimize it, and wrap it into a GUI application. Each recipe is accompanied by a sample project or an example that helps you focus on a particular aspect of the technology. Instant OpenCV for iOS starts by creating a simple iOS application and linking OpenCV before moving on to processing images and videos in real-time. It covers the major ways to retrieve images, process them, and view or export results. Special attention is also given to performance issues, as they greatly affect the user experience.Several computer vision projects will be considered throughout the book. These include a couple of photo filters that help you to print a postcard or add a retro effect to your images. Another one is a demonstration of the facial feature detection algorithm. In several time-critical cases, the processing speed is measured and optimized using ARM NEON and the Accelerate framework. OpenCV for iOS gives you all the information you need to build a high-performance computer vision application for iOS devices.
Table of Contents (7 chapters)

Applying a retro effect (Intermediate)


In this recipe, we'll learn how one can apply a custom photo effect to images from Gallery. We will implement a "retro" filter with OpenCV, so that the photographs look old, as shown in the following screenshot:

Getting ready

The source code for this recipe can be found in the Recipe07_ApplyingRetroEffect folder in the code bundle that accompanies this book. You can use the iOS Simulator to work on this recipe.

How to do it...

This recipe heavily relies on the previous one, as we're going to implement the same workflow: loading images from Gallery, processing them with OpenCV, and displaying them on the screen.

The following are the steps required to apply our filter to an image from Gallery:

  1. First of all, we need to implement our custom filter. We'll create the RetroFilter class in C++ for that purpose.

  2. Then we have to modify the ViewController class properly, by adding appropriate fields and its initialization in the viewDidLoad method.

  3. Finally, we'll implement the applyFilter method that wraps the call to the RetroFilter class.

Let's implement the described steps:

  1. The following is a declaration from the RetroFilter.hpp file for a class that is going to be used for photo stylization:

    class RetroFilter
    {
    public:
        struct Parameters
        {
            cv::Size frameSize;
            cv::Mat fuzzyBorder;
            cv::Mat scratches;
        };
        
        RetroFilter(const Parameters& params);
        virtual ~RetroFilter() {}; 
        void applyToPhoto(const cv::Mat& frame, cv::Mat& retroFrame);
        void applyToVideo(const cv::Mat& frame, cv::Mat& retroFrame);
        
    protected:
        Parameters params_;
        
        cv::RNG rng_;
        float multiplier_;
        
        cv::Mat borderColor_;
        cv::Mat scratchColor_;
        
        std::vector<cv::Mat> sepiaPlanes_;
        cv::Mat sepiaH_;
        cv::Mat sepiaS_;
    };
  2. We'll consider implementations for two main methods from the RetroFilter.cpp file. The following is a constructor for the class:

    RetroFilter::RetroFilter(const Parameters& params) : rng_(time(0))
    {
        params_ = params;
        
        multiplier_ = 1.0;
        
        borderColor_.create(params_.frameSize, CV_8UC1);
        scratchColor_.create(params_.frameSize, CV_8UC1);
        
        sepiaH_.create(params_.frameSize, CV_8UC1);
        sepiaH_.setTo(Scalar(19));
        sepiaS_.create(params_.frameSize, CV_8UC1);
        sepiaS_.setTo(Scalar(78));
        sepiaPlanes_.resize(3);
        sepiaPlanes_[0] = sepiaH_;
        sepiaPlanes_[1] = sepiaS_;
        
        resize(params_.fuzzyBorder, params_.fuzzyBorder,
               params_.frameSize);
        
        if (params_.scratches.rows < params_.frameSize.height ||
            params_.scratches.cols < params_.frameSize.width)
        {
            resize(params_.scratches, params_.scratches,
                   params_.frameSize);
        }
    } 
  3. And the following is the implementation of the main processing method:

    void RetroFilter::applyToPhoto(const Mat& frame, Mat& retroFrame)
    {
        Mat luminance;
        cvtColor(frame, luminance, CV_BGR2GRAY);
        
        // Add scratches
        Scalar meanColor = mean(luminance.row(luminance.rows / 2));
        scratchColor_.setTo(meanColor * 2.0);
        int x = rng_.uniform(0, params_.scratches.cols - luminance.cols);
        int y = rng_.uniform(0, params_.scratches.rows - luminance.rows);
        cv::Rect roi(cv::Point(x, y), luminance.size());
        scratchColor_.copyTo(luminance, params_.scratches(roi));
        
        // Add fuzzy border
        borderColor_.setTo(meanColor * 1.5);
        alphaBlendC1(borderColor_, luminance, params_.fuzzyBorder);
        
        // Apply sepia-effect
        sepiaPlanes_[2] = luminance + 20;
        Mat hsvFrame;
        merge(sepiaPlanes_, hsvFrame);
        cvtColor(hsvFrame, retroFrame, CV_HSV2RGB);
    }
  4. On the Objective-C side, we need to add the RetroFilter::Parameters member to the ViewController class and the applyFilter method with the following implementation:

    - (UIImage*)applyFilter:(UIImage*)inputImage;
    {
        cv::Mat frame;
        UIImageToMat(inputImage, frame);
        
        params.frameSize = frame.size();
        RetroFilter retroFilter(params);
        
        cv::Mat finalFrame;
        retroFilter.applyToPhoto(frame, finalFrame);
        
        return MatToUIImage(finalFrame);
    } 

The remaining Objective-C code is based on the previous recipe, so it is not shown here.

How it works...

The only new information in this recipe is the implementation of the RetroFilter class. It uses popular OpenCV functions, and we will explain only its most interesting part—the applyToPhoto method.

This method applies a sequence of processing steps that help us to achieve a "retro" effect. The key idea is to convert an image to a monochrome color space, do all the processing in it, and eventually convert it back to RGB with the sepia effect.

Both scratches and borders are rendered with a color that depends on a mean color of the image. To avoid costly analysis of the whole image, we only look into middle row of the image:

Scalar meanColor = mean(luminance.row(luminance.rows / 2));

You can also see that we are using the cv::RNG class (initialized with rng_(time(0))) to choose a region on the image with scratches randomly. This allows us to get different patterns of scratches for different images.

Finally, we assemble back the channels of our image. We add a value of 20 to the luminance plane, so the contrast is artificially decreased. After that, we use the OpenCV merge function to pack color planes into the single image, then convert it to the RGB color space with the help of the cvtColor function.

There's more...

You can try to use your own images with scratches and borders. As before, we recommend you to use GIMP software to edit images. But please note that both scratches.png and fuzzyBorder.png should be one-channel images, because they are used as a mask and alpha channel correspondingly.