I have some simple code that performs canny edge detection and overlays the edges on the original image.
The code works, but I'd like the edges to be drawn in black, currently they're drawn in white.
static void sketchImage(Mat srcColor, Mat& dst, bool sketchMode, int debugType)
{
Mat srcGray;
Mat edgesBgr;
cvtColor(srcColor, srcGray, CV_BGRA2GRAY);
cvtColor(srcColor, srcColor, CV_BGRA2BGR);
GaussianBlur(srcGray, srcGray, cvSize(5, 5),1.2,1.2);
GaussianBlur(srcColor, srcColor, cvSize(5,5), 1.4, 1.4);
CvSize size = srcColor.size();
Mat edges = Mat(size, CV_8U);
Canny(srcGray, edges, 150, 150);
cvtColor(edges, edgesBgr, cv::COLOR_GRAY2BGR);
dst = srcColor + edgesBgr;
}
I'm sure this is pretty simple but I'm fairly new to openCV and I'd appreciate any help.
Full code as requested:
#import "ViewController.h"
#import "opencv2/highgui.hpp"
#import "opencv2/core.hpp"
#import "opencv2/opencv.hpp"
#import "opencv2/imgproc.hpp"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIImageView *display;
#property (strong, nonatomic) UIImage* image;
#property (strong, nonatomic) UIImage* backup;
#property NSInteger clickflag;
#end
#implementation ViewController
using namespace cv;
- (IBAction)convert_click:(id)sender {
NSLog(#"Clicked");
if (_clickflag == 0)
{
cv::Mat cvImage, cvBWImage;
UIImageToMat(_image, cvImage);
//cv::cvtColor(cvImage, cvBWImage, CV_BGR2GRAY);
//cvBWImage = cvImage;
cartoonifyImage(cvImage, cvBWImage, false, 0);
_image = MatToUIImage(cvBWImage);
[_display setImage:_image];
_clickflag = 1;
}
else if(_clickflag == 1)
{
_image = _backup;
[_display setImage:_image];
_clickflag = 0;
}
}
static UIImage* MatToUIImage(const cv::Mat& m)
{
//CV_Assert(m.depth() == CV_8U);
NSData *data = [NSData dataWithBytes:m.data length:m.step*m.rows];
CGColorSpaceRef colorSpace = m.channels() == 1 ?
CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
// Creating CGImage from cv::Mat
CGImageRef imageRef = CGImageCreate(m.cols, m.rows, m.elemSize1()*8, m.elemSize()*8,
m.step[0], colorSpace, kCGImageAlphaNoneSkipLast|kCGBitmapByteOrderDefault,
provider, NULL, false, kCGRenderingIntentDefault);
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return finalImage;
}
static void UIImageToMat(const UIImage* image, cv::Mat& m)
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols = image.size.width;
CGFloat rows = image.size.height;
m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8,
m.step[0], colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault);
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
}
static void sketchImage(Mat srcColor, Mat& dst, bool sketchMode, int debugType)
{
Mat srcGray;
Mat edgesBgr;
cvtColor(srcColor, srcGray, CV_BGRA2GRAY);
cvtColor(srcColor, srcColor, CV_BGRA2BGR);
GaussianBlur(srcGray, srcGray, cvSize(5, 5),1.2,1.2);
GaussianBlur(srcColor, srcColor, cvSize(5,5), 1.4, 1.4);
CvSize size = srcColor.size();
Mat edges = Mat(size, CV_8U);
Canny(srcGray, edges, 150, 150);
cvtColor(edges, edgesBgr, cv::COLOR_GRAY2BGR);
//edgesBgr = edgesBgr.inv();
NSLog(#"%d, %d\n", srcColor.size().height, srcColor.size().width);
NSLog(#"%d, %d\n", edgesBgr.size().height, edgesBgr.size().width);
dst = edgesBgr + srcColor;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_image = [UIImage imageNamed:#"Robben.jpg"];
_backup = [UIImage imageNamed:#"Robben.jpg"];
_clickflag = 0;
[_display setImage:_image];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
static void sketchImage(Mat srcColor, Mat& dst, bool sketchMode, int debugType)
{
Mat srcGray;
cvtColor(srcColor, srcGray, CV_BGRA2GRAY);
cvtColor(srcColor, srcColor, CV_BGRA2BGR);
GaussianBlur(srcGray, srcGray, cvSize(5, 5),1.2,1.2);
GaussianBlur(srcColor, srcColor, cvSize(5,5), 1.4, 1.4);
CvSize size = srcColor.size();
Mat edges = Mat(size, CV_8U);
Canny(srcGray, edges, 150, 150);
dst = srcColor.clone();
dst.setTo(0,edges);
}
You could apply bitwise_not(dst,dst) so that white becomes black and black becomes white !
void bitwise_not(InputArray src, OutputArray dst, InputArray
mask=noArray())
Related
I have a video file from which I'm capturing a frames. I want to crop a triangle from captured frame and display it, but my program shows just a source frame.
Here is my code:
cv::Mat Detector::cropRegionOfInterest(cv::Mat& frame)
{
cv::Point corners[1][3];
corners[0][0] = cv::Point(0, frameHeight);
corners[0][1] = cv::Point(frameWidth, frameHeight);
corners[0][2] = cv::Point(frameWidth / 2, frameHeight / 2);
const cv::Point* cornerList[1] = { corners[0] };
int numPoints = 3;
int numPolygons = 1;
cv::Mat mask(frame.size(), CV_8UC1, cv::Scalar(0, 0, 0));
cv::fillPoly(mask, cornerList, &numPoints, numPolygons, cv::Scalar(255, 255, 255), 8);
cv::Mat result(frame.size(), CV_8UC3);
cv::bitwise_and(frame, mask, result);
return result;
}
Instead of displaying source frame I want it to display cropped triangle.
Since you're using CV_8UC3 as the type of result, I'm assuming (see the Edit at the end of the answer if that's not the case) that the input image frame also has 3 channels. In that case, I'm a bit surprised that you can even see the non-cropped image, as running your code simply throws an exception on my machine at the call to bitwise_and:
OpenCV(3.4.1) Error: Sizes of input arguments do not match
From the documentation, it seems to me that you can't mix different input and mask types. A quick and dirty solution is to split the input image into a vector of three channels, call bitwise_and for each of them, and then merge them back. The code below works for me:
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
cv::Mat cropRegionOfInterest(cv::Mat& frame)
{
const int frameWidth=frame.cols-1;
const int frameHeight=frame.rows-1;
cv::Point corners[1][3];
corners[0][0] = cv::Point(0, frameHeight);
corners[0][1] = cv::Point(frameWidth, frameHeight);
corners[0][2] = cv::Point(frameWidth / 2, frameHeight / 2);
const cv::Point* cornerList[1] = { corners[0] };
int numPoints = 3;
int numPolygons = 1;
cv::Mat mask(frame.rows,frame.cols, CV_8UC1, cv::Scalar(0, 0, 0));
cv::fillPoly(mask, cornerList, &numPoints, numPolygons, cv::Scalar(255, 255, 255), 8);
std::vector<cv::Mat> src_channels;
std::vector<cv::Mat> result_channels;
cv::split(frame,src_channels);
for(int idx=0;idx<3;++idx)
{
result_channels.emplace_back(frame.rows,frame.cols,CV_8UC1);
cv::bitwise_and(src_channels[idx], mask,result_channels[idx]);
}
cv::Mat result;
cv::merge(result_channels,result);
return result;
}
int main(int argc, char** argv )
{
if ( argc != 2 )
{
printf("usage: DisplayImage.out <Image_Path>\n");
return -1;
}
Mat image;
image = imread( argv[1], 1 );
if ( !image.data )
{
printf("No image data \n");
return -1;
}
cv::Mat cropped=cropRegionOfInterest(image);
namedWindow("cropped Image", WINDOW_AUTOSIZE );
imshow("cropped Image", cropped);
waitKey(0);
return 0;
}
Edit: From your comments it seems that frameĀ is actually grayscale. In that case, nevermind all the code above, and just change cv::Mat result(frame.size(), CV_8UC3); to
cv::Mat result(frame.rows,frame.cols,CV_8UC1);
in your original code.
Actually, I have read the official documentation here about class Map in opencv to try to use the module reg. And This is my test image:
This is my code:
#include<opencv.hpp>
#include "opencv2/reg/mapshift.hpp"
#include "opencv2/reg/mappergradshift.hpp"
#include "opencv2/reg/mapperpyramid.hpp"
using namespace cv;
using namespace std;
using namespace cv::reg;
Mat highlight1(const Mat src, const Mat t_mask) {
Mat srcImg = src.clone(), mask = t_mask.clone();
threshold(mask, mask, 0, 255, THRESH_BINARY_INV + THRESH_OTSU);
cvtColor(mask, mask, COLOR_GRAY2BGR);
cvtColor(srcImg, srcImg, COLOR_GRAY2BGR);
dilate(mask - Scalar(0, 0, 255), mask, Mat(), Point(-1, -1), 1);
return srcImg - mask;
}
int main() {
Mat img1 = imread("img.jpg", 0);
Mat img2;
// Warp original image
Vec<double, 2> shift(5., 5.);
MapShift mapTest(shift);
mapTest.warp(img1, img2);
// Register
Ptr<MapperGradShift> mapper = makePtr<MapperGradShift>();
MapperPyramid mappPyr(mapper);
Ptr<Map> mapPtr = mappPyr.calculate(img1, img2);
MapShift* mapShift = dynamic_cast<MapShift*>(mapPtr.get());
// Display registration result
Mat result;
mapShift->inverseWarp(img2, result);
Mat registration_before = highlight1(img1, img2);
Mat registration_after = highlight1(img1, result);
return 0;
}
But as we see, the registration_after is even worse than registration_before. What's I have missed?
This is registration_before:
This is registration_after:
I am trying to apply adaptive thresholding to an image of an A4 paper as shown below:
I use the code below to apply the image manipulation:
+ (UIImage *)processImageWithOpenCV:(UIImage*)inputImage {
cv::Mat cvImage = [inputImage CVMat];
cv::Mat res;
cv::cvtColor(cvImage, cvImage, CV_RGB2GRAY);
cvImage.convertTo(cvImage,CV_32FC1,1.0/255.0);
CalcBlockMeanVariance(cvImage,res);
res=1.0-res;
res=cvImage+res;
cv::threshold(res,res, 0.85, 1, cv::THRESH_BINARY);
cv::resize(res, res, cv::Size(res.cols/2,res.rows/2));
return [UIImage imageWithCVMat:cvImage];
}
void CalcBlockMeanVariance(cv::Mat Img,cv::Mat Res,float blockSide=13) // blockSide - the parameter (set greater for larger font on image)
{
cv::Mat I;
Img.convertTo(I,CV_32FC1);
Res=cv::Mat::zeros(Img.rows/blockSide,Img.cols/blockSide,CV_32FC1);
cv::Mat inpaintmask;
cv::Mat patch;
cv::Mat smallImg;
cv::Scalar m,s;
for(int i=0;i<Img.rows-blockSide;i+=blockSide)
{
for (int j=0;j<Img.cols-blockSide;j+=blockSide)
{
patch=I(cv::Rect(j,i,blockSide,blockSide));
cv::meanStdDev(patch,m,s);
if(s[0]>0.01) // Thresholding parameter (set smaller for lower contrast image)
{
Res.at<float>(i/blockSide,j/blockSide)=m[0];
}else
{
Res.at<float>(i/blockSide,j/blockSide)=0;
}
}
}
cv::resize(I,smallImg,Res.size());
cv::threshold(Res,inpaintmask,0.02,1.0,cv::THRESH_BINARY);
cv::Mat inpainted;
smallImg.convertTo(smallImg,CV_8UC1,255);
inpaintmask.convertTo(inpaintmask,CV_8UC1);
inpaint(smallImg, inpaintmask, inpainted, 5, cv::INPAINT_TELEA);
cv::resize(inpainted,Res,Img.size());
Res.convertTo(Res,CV_8UC3);
}
Although the inputted image is greyscaled, it outputs an yellowish image as shown below:
My hypothesis is that whilst conversion between the cv::Mat and UIImage, something happened leading to the color image, however I can not figure out how to fix this issue.
**please ignore the status bar as these images are screenshots of the iOS app.
Update:
I have tried using CV_8UC1 instead of CV_8UC3 for Res.convertTo() and added cvtColor(Res, Res, CV_GRAY2BGR); but am still getting very similar results.
Could it be the conversion between cv::mat and UIImage which is causing this problem??
I want my image to be like this shown below.
You can use OpenCV framework and implement below code
+(UIImage *)blackandWhite:(UIImage *)processedImage
{
cv::Mat original = [MMOpenCVHelper cvMatGrayFromAdjustedUIImage:processedImage];
cv::Mat new_image = cv::Mat::zeros( original.size(), original.type() );
original.convertTo(new_image, -1, 1.4, -50);
original.release();
UIImage *blackWhiteImage=[MMOpenCVHelper UIImageFromCVMat:new_image];
new_image.release();
return blackWhiteImage;
}
+ (cv::Mat)cvMatGrayFromAdjustedUIImage:(UIImage *)image
{
cv::Mat cvMat = [self cvMatFromAdjustedUIImage:image];
cv::Mat grayMat;
if ( cvMat.channels() == 1 ) {
grayMat = cvMat;
}
else {
grayMat = cv :: Mat( cvMat.rows,cvMat.cols, CV_8UC1 );
cv::cvtColor( cvMat, grayMat, cv::COLOR_BGR2GRAY );
}
return grayMat;
}
+ (cv::Mat)cvMatFromAdjustedUIImage:(UIImage *)image
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols = image.size.width;
CGFloat rows = image.size.height;
cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault);
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
return cvMat;
}
+ (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];
CGColorSpaceRef colorSpace;
if (cvMat.elemSize() == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
}
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
CGImageRef imageRef = CGImageCreate(cvMat.cols, // Width
cvMat.rows, // Height
8, // Bits per component
8 * cvMat.elemSize(), // Bits per pixel
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone | kCGBitmapByteOrderDefault, // Bitmap info flags
provider, // CGDataProviderRef
NULL, // Decode
false, // Should interpolate
kCGRenderingIntentDefault); // Intent
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return image;
}
its working for me check the output for your document
Try this:
+ (UIImage *)processImageWithOpenCV:(UIImage*)inputImage {
cv::Mat cvImage = [inputImage CVMat];
threshold(cvImage, cvImage, 128, 255, cv::THRESH_BINARY);
return [UIImage imageWithCVMat:cvImage];
}
Result image:
I'm using OpenCV to test image operations. But using the following method results in an error, which i can't explain to myself.
- (cv::Mat)cvMatGrayFromUIImage:(UIImage *)image
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols = image.size.width;
CGFloat rows = image.size.height;
cv::Mat cvMat = cv::Mat(rows, cols, CV_8UC1); // 8 bits per component, 1 channel
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace);
return cvMat;
}
When i run this method, i get the following output into the xcode console
Aug 17 11:14:24 OpenCVDemo[1250] <Error>: CGBitmapContextCreate: unsupported parameter combination: set CGBITMAP_CONTEXT_LOG_ERRORS environmental variable to see the details
Aug 17 11:14:24 OpenCVDemo[1250] <Error>: CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
For example here is an method, which is working fine.
- (cv::Mat)cvMat3ChannelFromUIImage:(UIImage *)image
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols = image.size.width;
CGFloat rows = image.size.height;
cv::Mat rgba(rows, cols, CV_8UC4, cvScalar(1,2,3,4)); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(rgba.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
rgba.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
cv::Mat bgr( rgba.rows, rgba.cols, CV_8UC3 );
cv::Mat alpha( rgba.rows, rgba.cols, CV_8UC1 );
cv::Mat out[] = { bgr, alpha };
// rgba[0] -> bgr[2], rgba[1] -> bgr[1],
// rgba[2] -> bgr[0], rgba[3] -> alpha[0]
int from_to[] = { 0,2, 1,1, 2,0, 3,3 };
mixChannels( &rgba, 1, out, 2, from_to, 4 );
return bgr;
}
I hope here is somebody who can explain, why the gray-method is not working.
- (cv::Mat)cvMat3ChannelFromUIImage:(UIImage *)image
{
UIImage *theImage = [UIImage imageWithData:UIImagePNGRepresentation(image)];
CGColorSpaceRef colorSpace = CGImageGetColorSpace(theImage.CGImage);
CGFloat cols = theImage.size.width;
CGFloat rows = theImage.size.height;
cv::Mat rgba(rows, cols, CV_8UC4, cvScalar(1,2,3,4)); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(rgba.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
rgba.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), theImage.CGImage);
CGContextRelease(contextRef);
cv::Mat bgr( rgba.rows, rgba.cols, CV_8UC3 );
cv::Mat alpha( rgba.rows, rgba.cols, CV_8UC1 );
cv::Mat out[] = { bgr, alpha };
// rgba[0] -> bgr[2], rgba[1] -> bgr[1],
// rgba[2] -> bgr[0], rgba[3] -> alpha[0]
int from_to[] = { 0,2, 1,1, 2,0, 3,3 };
mixChannels( &rgba, 1, out, 2, from_to, 4 );
return bgr;
}
//Mat Conversion
+ (cv::Mat)cvMatFromUIImage:(UIImage *)image
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols,rows;
if (image.imageOrientation == UIImageOrientationLeft
|| image.imageOrientation == UIImageOrientationRight) {
cols = image.size.height;
rows = image.size.width;
}
else{
cols = image.size.width;
rows = image.size.height;
}
cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault);
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
cv::Mat cvMatTest;
cv::transpose(cvMat, cvMatTest);
if (image.imageOrientation == UIImageOrientationLeft
|| image.imageOrientation == UIImageOrientationRight) {
}
else{
return cvMat;
}
cvMat.release();
cv::flip(cvMatTest, cvMatTest, 1);
return cvMatTest;
}
//Gray Conversion
+ (cv::Mat)cvMatGrayFromUIImage:(UIImage *)image
{
cv::Mat cvMat = [self cvMatFromUIImage:image];
cv::Mat grayMat;
if ( cvMat.channels() == 1 ) {
grayMat = cvMat;
}
else {
grayMat = cv :: Mat( cvMat.rows,cvMat.cols, CV_8UC1 );
cv::cvtColor( cvMat, grayMat, CV_BGR2GRAY );
}
return grayMat;
}
//Call the above method
cv::Mat grayImage = [self cvMatGrayFromUIImage:"your image"];
I am storing frames in real time from a CvVideoCamera using the - (void)processImage:(Mat&)image delegate method.
After storing the images I average them all into one image to replicate a long exposure shot using this code:
Mat merge(Vector<Mat> frames, double alpha)
{
Mat firstFrame = frames.front();
Mat exposed = firstFrame * alpha;
for (int i = 1; i < frames.size(); i++) {
Mat frame = frames[i];
exposed += frame * alpha;
}
return exposed;
}
After getting the averaged image back I convert it back to a UIImage, but the image I get back is in a strange color space, does anyone know how I can fix this?
Conversion code:
+ (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
CGColorSpaceRef colorSpace;
if ( cvMat.elemSize() == 1 ) {
colorSpace = CGColorSpaceCreateDeviceGray();
}
else {
colorSpace = CGColorSpaceCreateDeviceRGB();
}
CGDataProviderRef provider = CGDataProviderCreateWithCFData( (__bridge CFDataRef)data );
CGImageRef imageRef = CGImageCreate( cvMat.cols, cvMat.rows, 8, 8 * cvMat.elemSize(), cvMat.step[0], colorSpace, kCGImageAlphaNone|kCGBitmapByteOrderDefault, provider, NULL, false, kCGRenderingIntentDefault );
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease( imageRef );
CGDataProviderRelease( provider );
CGColorSpaceRelease( colorSpace );
return finalImage;
}
Here is an example(note: the plane in the middle is because I am recording off a computer monitor)