BradLarson / GPUImage

An open source iOS framework for GPU-based image and video processing
http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework
BSD 3-Clause "New" or "Revised" License
20.25k stars 4.62k forks source link

Image process is rotate 90° #895

Open emmanuelperez opened 11 years ago

emmanuelperez commented 11 years ago

Hi,

I'm using your framework to process image taken by camera. My application is in portrait mode only. After taken the photo and apply a filter on it, the image display on a GPUImageView is rotate by 90° I read some other post on this issue and can't find the solution. Thanks and regards

marzapower commented 11 years ago

This bug affects me too when recording videos.

BradLarson commented 11 years ago

How do you set up your camera for image processing? How do you feed your images to the GPUImageView? If you are feeding directly from the camera into the view, are you setting the camera's outputImageOrientation to UIInterfaceOrientationPortrait?

marzapower commented 11 years ago

I am feeding the images from the camera to GPUImageView directly without filters. This is the code:

_videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
_videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
[_videoCamera addTarget:_videoView];
pengpai commented 11 years ago

same issue

emmanuelperez commented 11 years ago

Hi Brad,

I'm not using GPUImage to take photo, i've got my own classes to do that. This is the code to take the photo, where "orientation" value is AVCaptureVideoOrientationPortrait, and i send the image to save and to proceed on the call to [delegate performSelector:@selector(savePhoto:) withObject:image]

Thanks for your reply and best regards

marzapower commented 11 years ago

Any news on this issue?

emmanuelperez commented 11 years ago

No, no more news from Brad. In fact, i use another function to fix orientation on Image taken from camera before process it. But the problem still be there with filter using lookup image as amatorka or missEtikate and don't know why with these filter even if the orientation is well configured, the result is rotate.

marzapower commented 11 years ago

I found that using a sepia filter with intensity = 0 there is no recording rotation issue. When using GPUImage without filters the whole tool doesn't work as intended. This should be fixed.

rongconmocrang commented 11 years ago

hi marzapower. Where can I set intensity= 0?

marzapower commented 11 years ago

Do it like this:

filter = [[GPUImageSepiaFilter alloc] init];
filter.intensity = 0;
rongconmocrang commented 11 years ago

Hi marzapower, Thanks for reply quickly. But your solution was not work with me, the image is still rotated.

skyuplam commented 11 years ago

It's the issue of the conversion of the meta orientation and and UIImageOrientation. I have fixed it by updating the the orientation value in the meta data dictionary of the photo.

Referece: http://stackoverflow.com/questions/6699330/how-to-save-photo-with-exifgps-and-orientation-on-iphone

eliperkins commented 11 years ago

So I started diving in to GPUImage, specifically GPUImageStillCamera. I started simple, just creating a camera that can overlay a UIImage and capture it.

To set up the camera, my code looks a bit like this:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.stillCamera = [[GPUImageStillCamera alloc] init];

    GPUImageAlphaBlendFilter *blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
    blendFilter.mix = 1.0f;

    // Create new filter to blend the camera + overlay
    self.overlayImage = [UIImage imageNamed:@"overlay"];
    self.overlayImagePicture = [[GPUImagePicture alloc] initWithImage:self.overlayImage smoothlyScaleOutput:YES];
    [self.overlayImagePicture processImage];

    // Add the camera to the filter
    [self.stillCamera addTarget:blendFilter];

    // Add the overlay to the filter
    [self.overlayImagePicture addTarget:blendFilter];

    // Add the filter to the view
    GPUImageView *filterView = (GPUImageView *)self.view;
    [blendFilter addTarget:filterView];

    // Retain the overlay
    self.overlay = blendFilter;

    // Set the output orientation to the current orientation (doesn't work yet...)
    UIInterfaceOrientation deviceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    self.stillCamera.outputImageOrientation = deviceOrientation;

    [self.stillCamera startCameraCapture]; 
}

Pretty standard stuff. I added a button as well, and linked it up to an action that looks like this:

- (void)takePhoto:(id)sender {
    [self.captureButton setEnabled:NO];

    [self.stillCamera capturePhotoAsImageProcessedUpToFilter:self.overlay withCompletionHandler:^(UIImage *processedImage, NSError *error) {
        [self.captureButton setEnabled:YES];
        PhotoReviewViewController *vc = [[PhotoReviewViewController alloc] initWithImage:processedImage];
        [self.navigationController pushViewController:vc animated:YES];
    }];
}

The PhotoReviewViewController just displays the photo in a new view controller with a UIImageView. Nothing crazy.

Additionally, I'm responding to UIDeviceOrientationDidChangeNotification as such:

- (void)deviceOrientationDidChange:(NSNotification *)note {
    UIInterfaceOrientation deviceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    self.stillCamera.outputImageOrientation = deviceOrientation;
}

The caveat here is my view controller (and app as a whole) is locked to landscape.

Expected: My PhotoReviewViewController shows the image in the correct orientation. Acutal: My PhotoReviewViewController shows the image rotated 90° left/right, depending on which way I orient the device.

In diving deeper, I found that my procedure of setting outputImageOrientation seemed okay, following the chain of calls through GPUImageVideoCamera, specifically to updateOrientationSendToTargets. The problem actually arised when hitting the capturePhotoAsImageProcessedUpToFilter: method.

capturePhotoAsImageProcessedUpToFilter calls a variety of methods, leading up to the juicy bit for me though, imageFromCurrentlyProcessedOutput. This method grabs UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];, runs deviceOrientation through a sensible switch, calculates the correct UIImageOrientation, and calls imageFromCurrentlyProcessedOutputWithOrientation: to move forward with grabbing the UIImage for me. All of this seems fine and dandy, except that my image was never in the right orientation! By just hard coding the switch to always return UIImageOrientationUp for any deviceOrientation, all is well.

I've tried by best to reason my way through this, but I can't. Any thoughts on removing imageWithCGImage:scale:orientation: and going with imageWithCGImage: instead?

BradLarson commented 11 years ago

Yeah, I was seeing this same behavior myself the other day, and I think it is localized to the default image rotation applied based on the accelerometer in that method. The way to solve this would probably be to maintain the camera metadata throughout the image capture and base things off of that.

StephyJoseSics commented 11 years ago

@emmanuelperez : Can u please share the orientation fixing function. I have been wasting too much time on this and still couldnt fix it. Thanks

wdanxna commented 11 years ago

I am also ran into this problem which photo captured by GPUImageStillCamera been rotated 90 degree, still struggling with it. is anyone worked out some final solution?

drunkirishcoder commented 11 years ago

Check out this Stack Overflow answer. So basically the iPhone camera's native mode is landscape. You will get orientation right if you take a portrait photo and retrieve the UIImage. UIImageView handles the rotation of the image for you when displayed, but GPUImageView does not do that.

mediavrog commented 11 years ago

What helped me after a little investigation was to load the picture either by Uri or File reference, so GPUImage can determine the rotation properly (at least on the Android port - I guess there is some similar mechanic for iPhone?)

// load either by File or other ContentResolver
// important for correct usage of detecting image rotation (File: ExifInterface; ContentResolver: MediaStore > ImageColumns)
        if ("file".equals(uri.getScheme())) {
            mGPUImageView.setImage(new File(uri.getPath()));
        } else {
            mGPUImageView.setImage(uri);
        }
MuhammedRafeek commented 10 years ago

I also have the same issue Is there any way to do it in iPhone??.

Ted2014 commented 10 years ago

First of all, I would like to thank Brad to let us use GPUImage, fantastic program.

I also have the same problem in my iPhone app that I am developing now. It seems difficult to improve it within GPUImage, but I found a way to rotate a saved image by 90° by using an edit function in iOS, which needs to ask user's operation.

The way is, go to photo library, select a saved image rotated by 90°, push "Edit" button to edit the image, you will find image rotation button at bottom left of the screen. This button makes an image rotated by 90° rotate back to the correct position.

This is not a smart way, but better than nothing to do. Please try it if acceptable.

zenvendof commented 10 years ago

(Solution at the end for the quickies)

To those who are still scratching head on this, if you are using UIImagePickerController when you:

  1. Take a photo via camera
  2. Obtain a photo from saved library

The UIImage obtained has the right orientation, if you do a debug after obtaining the UIImage, check out the imageOrientation properties:

Portrait

uiimage imageorientation for portrait image

Landscape

uiimage imageorientation for landscape photos

@drunkirishcoder is right, based on the information from his link:

http://stackoverflow.com/questions/13074161/uiimage-from-uiimagepickercontroller-orientation-issue

I was able to figure out why landscape photos look ok after going through filter(s). imageFromCurrentlyProcessedOutputWithOrientation is producing the right output, based on the current device orientation, it produces the image as per request, for video camera operations, this is fine because you are taking videos in the same orientation as the video images enter through the chain.

However, if the input is a still image (e.g. GPUImagePicture), the original orientation could be Landscape (UIImageOrientationUp), or Portrait (UIImageOrientationRight). GPUImagePicture probably load it correctly using the UIImageOrientation information, so your image looks "right". However, when it save the image back into a UIImage via any of the capture from frame function, it will "lost" the original orientation because you are likely holding the device in portrait orientation :-) (Which is UIImageOrientationUp). When you put it back out to a UIImageView, it will be....well you guessed 90° rotated!. Bottomline is the saved image orientation is NOT the same as your device orientation (default for camera is landscape).

Solution

Well, simple isn't it, just give it the right orientation again, we can do it in GPUImage but that would mean changing the code to crusade down the original image info, for now:

processedImage = [UIImage imageWithCGImage:[processedImage CGImage] scale:1.0 orientation:originalImage.imageOrientation];

This will give the processedImage the original orientation. I tested it out on my app it works. Would be embarrassing in production since in the iOS simulator somehow looks alright!

bluealert commented 10 years ago

use @skyuplam 's solution, I solved this problem.

[self.stillCamera capturePhotoAsJPEGProcessedUpToFilter:self.currentFilter withCompletionHandler:^(NSData processedJPEG, NSError error){

    // Save to assets library
    ALAssetsLibrary *library = [ALAssetsLibrary new];

    NSMutableDictionary* metadata = [NSMutableDictionary dictionaryWithDictionary:self.stillCamera.currentCaptureMetadata];
    metadata[(__bridge NSString*)kCGImagePropertyOrientation] = @(UIImageOrientationUp);

    [library writeImageDataToSavedPhotosAlbum:processedJPEG metadata:metadata completionBlock:^(NSURL *assetURL, NSError *error2) {
         if (error2) {
             NSLog(@"ERROR: the image failed to be written");
         } else {
             NSLog(@"PHOTO SAVED - assetURL: %@", assetURL);
         }

     }];
}];
MobileMon commented 9 years ago

None of these solutions support GPUImageView. If I push a portrait image into GPUImageView, it comes out landscape. How to fix?

self.gpuImagePicture = [[GPUImagePicture alloc] initWithImage:myImage];
[self.gpuImagePicture addTarget:self.gpuImageView];
[self.gpuImagePicture processImage];
reddragon commented 9 years ago

The solution by @zenvendof works great. Here is how I fixed it end-to-end:

// self.chosenImg is the image that you took, replace it with the appropriate image.
GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:self.chosenImg];
GPUImageSepiaFilter *stillImageFilter = [[GPUImageSepiaFilter alloc] init];

[stillImageSource addTarget:stillImageFilter];
[stillImageFilter useNextFrameForImageCapture];
[stillImageSource processImage];

UIImage* processedImg = [stillImageFilter imageFromCurrentFramebuffer];
processedImg = [UIImage imageWithCGImage:[processedImg CGImage] scale:1.0 orientation:self.chosenImg.imageOrientation];
reddragon commented 9 years ago

But ideally, GPUImage should preserve the original orientation. @BradLarson any thoughts?

natesymer commented 9 years ago

The issue with the 90 degree rotation is that images taken by iOS are rotated 90 degrees. The JPEGs are rotated back when they are rendered. This is all done through the EXIF orientation tag.

Mirant123 commented 8 years ago

Same issue i face when i capture video and filter . Video automatically rotate and display in 90 degree anti-clockwised. I also check below methods for that but nothing happen. if (orientation == UIInterfaceOrientationPortrait){ [filterRef setInputRotation:kGPUImageRotateRight atIndex:0]; } else if (orientation == UIInterfaceOrientationLandscapeRight) { [filterRef setInputRotation:kGPUImageRotate180 atIndex:0]; } else if (orientation == UIInterfaceOrientationPortraitUpsideDown) { [filterRef setInputRotation:kGPUImageRotateLeft atIndex:0]; }

HungTDO commented 7 years ago

@mediavrog :+1: Thanks. You saved my life =)) I'm finding this error for android app, surprise when i found that in a topic for IOS =)). Thanks again.

inorganik commented 7 years ago

Wanted to add another +1 for @zenvendof solution. However wanted to add one thing. My app allows any orientation except upside down. I found I always need to reset the orientation to up no matter which orientation the image was taken in:

stillImage = desaturationFilter.imageFromCurrentFramebuffer()
if let stillCG = stillImage?.cgImage {
    stillImage = UIImage(cgImage: stillCG, scale: 1.0, orientation: UIImageOrientation.up)
}