agens-no / AGGeometryKit

Quadrilaterals on CALayer, CGGeometry-functions, UIView/CALayer properties and other invaluable tools.
Other
1.27k stars 130 forks source link

Possible to perspective-distort an image? #25

Closed chadkouse closed 9 years ago

chadkouse commented 9 years ago

I need to take an image and transform it into a new quad to give the image a perspective distortion. I want to take a rectangular image and skew. So far I haven't been able to determine how to do this. Ideally this transformation would not require the image to be added to a view since my purposes don't require me to display the image on the screen but do require me to process hundreds of these as fast as possible.

I appreciate any help or advice, thanks!

chadkouse commented 9 years ago

I need like a [UIImage drawInQuad:....] Method

hfossli commented 9 years ago

Well, you don't need to add them to screen in order to process them. I think the demo is showcasing this.

But you need to define the quad, alas find the corners of what you want to deskew. OpenCV might help you with finding corners.

@snown might me able to fill in here as well. And do please look at the other issues.

chadkouse commented 9 years ago

Thanks for the reply. The issue that seems closely related is https://github.com/hfossli/AGGeometryKit/issues/8

However it seems to be using deprecated methods/categories.

hfossli commented 9 years ago

Have you had a look at the code in AGKCATransform3DWithUIImageExample

- (void)cropImage:(UIImage *)image toQuad:(AGKQuad)quad
{
    static int count = 0;
    static dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("agk.cropImageToQuad", 0);
    });

    count++;
    [self.activityIndicator startAnimating];

    dispatch_async(queue, ^{

        UIImage *result = [image imageByCroppingToQuad:quad destinationSize:self.result.boundsSize];

        dispatch_async(dispatch_get_main_queue(), ^{
            self.result.image = result;

            count--;

            if(count == 0)
            {
                [self.activityIndicator stopAnimating];
            }
        });
    });
}
hfossli commented 9 years ago

The problem you described can be breaked down into 2 issues

  1. For each image find the proper quad to crop to
  2. Crop to the given quad

Let me know which of these you are struggling with.

chadkouse commented 9 years ago

For my use case a crop may work but I was looking to not crop the image but instead just distort it from a rectangle to a quad. I'll see if cropping is good enough though

hfossli commented 9 years ago

Maybe "crop" is not the proper wording - sorry if that was confusing. You want a deskewed UIImage, right? Well, the API to deskew images is contained within AGK. All you need is to pass the correct arguments.

Do you want to deskew the images and save them or just deskew them whenever the user is about to display them? (both is possible with AGK)

hfossli commented 9 years ago

Drawings, illustration and very concise text is needed in order for me to guide you to the right places to look. I am sorry, but this has been most confusing.

chadkouse commented 9 years ago

You're right, sorry I should have sent this sooner.

I will try your suggestions, thanks!

example

The reason I want to do this is to ultimately draw it into another larger image like this: example

hfossli commented 9 years ago

That is very easy with AGK. I'm on the go now, but i'll give you some code in 3-16 hours. =

chadkouse commented 9 years ago

I would greatly appreciate it as I am hours into this search :+1:

hfossli commented 9 years ago
    UIImage *original = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://weknowmemes.com/wp-content/uploads/2013/11/doge-meme-13.jpg"]]];

    CGSize size = original.size;
    CGRect imageBounds = CGRectMake(0, 0, size.width, size.height);

    // Arbitrary example
    AGKQuad quadrilateral = AGKQuadMake(CGPointMake(size.width * 0.0, size.height * 0.2),
                                        CGPointMake(size.width * 1.0, size.height * 0.0),
                                        CGPointMake(size.width * 0.9, size.height * 1.0),
                                        CGPointMake(size.width * 0.2, size.height * 0.8));

    CATransform3D transform = CATransform3DWithAGKQuadFromBounds(quadrilateral, imageBounds);

    UIImage *result = [original imageWithTransform:transform anchorPoint:CGPointZero];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    [UIImageJPEGRepresentation(original, 0.9) writeToFile:[documentsDirectory stringByAppendingPathComponent:@"source.jpg"] atomically:YES];
    [UIImageJPEGRepresentation(result, 0.9) writeToFile:[documentsDirectory stringByAppendingPathComponent:@"output.jpg"] atomically:YES];

    NSLog(@"Documents: %@", documentsDirectory);

The original source

The result output

chadkouse commented 9 years ago

You're the king of geometry -- Thanks I will see how this works in my application!

hfossli commented 9 years ago

All of this can be done multithreaded. You'll notice the edges is a little bit rough. This can be improved by improving the resampling algorithm. If this is not meeting your criteria I suggest looking into OpenCV as they are doing all this and more.

hfossli commented 9 years ago

So you can either try to find a good quad matching your needs or you could do a perspective distort with plain CATransform3D. I'm sure stackoverflow will be of help with the latter option. If you decide to open a stackoverflow post I'd be happy to add some bounty of 150 to ensure good answers.

chadkouse commented 9 years ago

Worked great and pretty good performance even single threaded. Thanks a lot!

hfossli commented 9 years ago

Glad to hear that!