I've been using this method to export pixels using the MagickWand API:
MagickExportImagePixels
But, this doesn't appear to obey the orientation exif data in the image. Is there anyway to extract the pixels in the correct orientation, so I can write them out in a different format (Not using MagickWriteImage)? Basically, I want the behaviour of the auto-orient option for convert.
Thanks!
I am one of the developers for ImageMagick and it seems we forgot to add this method to Wand. The next release of ImageMagick (6.8.9-9) will include the following method that can be used to automatically orient an image:
MagickAutoOrientImage(magick_wand);
When 6.8.9+ release of ImageMagick with MagickAutoOrientImage() is not available I use the following code to auto-orient images:
void auto_orient_image(MagickWand* image) {
PixelWand* pwand=0;
switch(MagickGetImageOrientation(image))
{
case UndefinedOrientation:
case TopLeftOrientation:
default:
break;
case TopRightOrientation:
MagickFlopImage(image);
break;
case BottomRightOrientation:
pwand=NewPixelWand();
MagickRotateImage(image, pwand, 180.0);
case BottomLeftOrientation:
MagickFlipImage(image);
break;
case LeftTopOrientation:
MagickTransposeImage(image);
break;
case RightTopOrientation:
pwand=NewPixelWand();
MagickRotateImage(image, pwand, 90.0);
break;
case RightBottomOrientation:
MagickTransverseImage(image);
break;
case LeftBottomOrientation:
pwand=NewPixelWand();
MagickRotateImage(image, pwand, 270.0);
break;
}
if (pwand) DestroyPixelWand(pwand);
MagickSetImageOrientation(image, TopLeftOrientation);
}
Might need to add error handling.
It looks like the only way to do this is pseudo-manually by getting the image orientation and rotating the image accordingly. It's not difficult, but I was hoping a more concise solution was built into the API. An incomplete example follows:
OrientationType t = MagickGetImageOrientation(magick_wand);
if (t == RightTopOrientation) {
MagickRotateImage(mw, pw, 90);
}
...
Related
I'm using the Xam.Plugin.Media in my Forms app to take pictures.
I take the Image stream (GetStream) and convert to a byte[] and store in my DB.
I then use that as the image source.
On Android and UWP, its working fine.
On iOS, if the picture is taken in portrait mode, the image once, selected is always rotated 90 deg.
I will later, upload this to a server and that image could be used on a different device.
For this, I also tried the GetStreamWithImageRotatedForExternalStorage but in this case, I cant see the image at all.
There are bytes in the stream (I DisplayAlert the length) but the image does not display.
Any idea what I might be doing wrong?
My code:-
private async Task TakePicture(WineDetails details)
{
await CrossMedia.Current.Initialize();
if (CrossMedia.Current.IsCameraAvailable && CrossMedia.Current.IsTakePhotoSupported)
{
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
AllowCropping = true,
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
SaveToAlbum = false,
RotateImage = true
});
if (file == null)
return;
using (var ms = new MemoryStream())
{
var stream = file.GetStreamWithImageRotatedForExternalStorage();
stream.CopyTo(ms);
details.LabelImage = ms.ToArray();
details.NotifyChange("ImageSource");
}
}
}
The image is updated in the page via the NotifyChange and looks like this:-
ImageSource.FromStream(() => new MemoryStream(this.LabelImage))
This works fine in all cases on Android and UWP, works on iOS using GetStream (except the image is incorrectly rotated) but does not work using GetStreamWithImageRotatedForExternalStorage on iOS.
Anyone else using this plugin?
Any idea why GetStream returns a rotated image?
Any idea why GetStreamWithImageRotatedForExternalStorage is not working?
Thanks
Update:-
Changed SaveToAlbum = true and when I open the gallery, the image is rotated 90 deg.
Have RotateImage = true which could cause the issue? I'll try setting it to false.
I still can't set the image source to the byte array of the image using GetStreamWithImageRotatedForExternalStorage.
using (var ms = new MemoryStream())
{
file.GetStreamWithImageRotatedForExternalStorage().CopyTo(ms);
details.LabelImage = ms.ToArray();
}
using the byte array for an image
return ImageSource.FromStream(() => new MemoryStream(this.LabelImage));
This does not work for me, GetStream works ok.
Update:-
Ok so, RotateImage = false + GetStreamWithImageRotatedForExternalStorage allows me to display the image but its still incorrectly rotated in my app and the gallery.
I'm using this plugin, which is similar (if not the same thing - I know James Montemagno has recently packaged/bundled his work with Xamarin).
If you check the issues board there, you'll see there are quite a few people that have similar troubles (image rotation on iOS). Almost every 'solution' mentions using GetStreamWithImageRotatedForExternalStorage.
My issue was similar - I was unable to take a photo on iOS in portrait mode, without other (non-ios Devices) rotating the image. I have tried for weeks to solve this issue, but support on the plugin seems to be quite limited.
Ultimately I had to solve this with a huge workaround - using a custom renderer extending from FFImageLoading to display our images and MetadataExtractor. We were then able to extract the EXIF data from the stream and apply a rotation transformation to the FFImageLoding image control.
The rotation information was stored in a sort of weird way, as a string. This is the method I'm using to extract the rotation information, and return the amount it needs to be rotated as an int. Note that for me, iOS was able to display the image correctly still, so it's only returned a rotation change for Android devices.
public static int GetImageRotationCorrection(byte[] image)
{
try
{
var directories = ImageMetadataReader.ReadMetadata(new MemoryStream(image));
if (Device.Android == "Android")
{
foreach (var directory in directories)
{
foreach (var tag in directory.Tags)
{
if (tag.Name == "Orientation")
{
if (tag.Description == "Top, left side(Horizontal / normal)")
return 0;
else if (tag.Description == "Left side, bottom (Rotate 270 CW)")
return 270;
else if (tag.Description == "Right side, top (Rotate 90 CW")
return 90;
}
}
}
}
return 0;
}
catch (Exception ex)
{
return 0;
}
}
Note that there is also a custom renderer for the image for FFImage Loading.
public class RotatedImage : CachedImage
{
public static BindableProperty MyRotationProperty = BindableProperty.Create(nameof(MyRotation), typeof(int), typeof(RotatedImage), 0, propertyChanged: UpdateRotation);
public int MyRotation
{
get { return (int)GetValue(MyRotationProperty); }
set { SetValue(MyRotationProperty, value); }
}
private static void UpdateRotation(BindableObject bindable, object oldRotation, object newRotation)
{
var _oldRotation = (int)oldRotation;
var _newRotation = (int)newRotation;
if (!_oldRotation.Equals(_newRotation))
{
var view = (RotatedImage)bindable;
var transformations = new System.Collections.Generic.List<ITransformation>() {
new RotateTransformation(_newRotation)
};
view.Transformations = transformations;
}
}
}
So, in my XAML - I had declared a RotatedImage instead of the standard Image. With the custom renderer, I'm able to do this and have the image display rotated the correct amount.
image.MyRotation = GetImageRotationCorrection(imageAsBytes)
It's a totally unnecessary workaround - but these are the lengths that I had to go to to get around this issue.
I'll definitely be following along with this question, there might be someone in the community who could help us both!
The SaveMetaData flag is causing the rotation issue.
Setting it to false (default is true) now displays the photo correctly.
One side effect of that, the image no longer appears in the gallery if SaveToAlbum=true.
Still can't use an image take when using GetStreamWithImageRotatedForExternalStorage, even using FFImageLoading.
I found that while using Xam.Plugin.Media v5.0.1 (https://github.com/jamesmontemagno/MediaPlugin), the combination of three different inputs produced different results on Android vs. iOS:
StoreCameraMediaOptions.SaveMetaData
StoreCameraMediaOptions.RotateImage
Using MediaFile.GetStream() vs. MediaFile.GetStreamWithImageRotatedForExternalStorage()
On Android, SaveMetaData = false, RotateImage = true, and using MediaFile.GetStreamWithImageRotatedForExternalStorage() worked for me whether I was saving the result stream externally or processing the result stream locally for display.
On iOS, the combination of RotateImage = true and StreamRotated = true would result in a NullReferenceException coming out of the plugin library. Using MediaFile.GetStreamWithImageRotatedForExternalStorage() appeared to have no impact on behaivor.
--
Before going further, it's important to understand that image orientation in the JPEG format (which Xam.Plugin.Media seems to use) isn't as straightforward as you might think. Rather than rotating the raw image bytes 90 or 180 or 270 degrees, JPEG orientation can be set through embedded EXIF metadata. Orientation issues will happen with JPEGs either if EXIF data is stripped or if downstream consumers don't handle the EXIF data properly.
The approach I landed on was to normalize JPEG image orientation at the point the image is captured without relying on EXIF metadata. This way, downstream consumers shouldn't need to be relied on to properly inspect and handle EXIF orientation metadata.
The basic solution is:
Scan a JPEG for EXIF orientation metadata
Transform the JPEG to rotate/flip as needed
Set the result JPEG's orientation metadata to default
--
Code example compatible with Xamarin, using ExifLib.Standard (1.7.0) and SixLabors.ImageSharp (1.0.4) NuGet packages. Based on (Problem reading JPEG Metadata (Orientation))
using System;
using System.IO;
using ExifLib;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Processing;
namespace Your.Namespace
{
public static class ImageOrientationUtility
{
public static Stream NormalizeOrientation(Func<Stream> inputStreamFunc)
{
using Stream exifStream = inputStreamFunc();
using var exifReader = new ExifReader(exifStream);
bool orientationTagExists = exifReader.GetTagValue(ExifTags.Orientation, out ushort orientationTagValue);
if (!orientationTagExists)
// You may wish to do something less aggressive than throw an exception in this case.
throw new InvalidOperationException("Input stream does not contain an orientation EXIF tag.");
using Stream processStream = inputStreamFunc();
using Image image = Image.Load(processStream);
switch (orientationTagValue)
{
case 1:
// No rotation required.
break;
case 2:
image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Horizontal));
break;
case 3:
image.Mutate(x => x.RotateFlip(RotateMode.Rotate180, FlipMode.None));
break;
case 4:
image.Mutate(x => x.RotateFlip(RotateMode.Rotate180, FlipMode.Horizontal));
break;
case 5:
image.Mutate(x => x.RotateFlip(RotateMode.Rotate90, FlipMode.Horizontal));
break;
case 6:
image.Mutate(x => x.RotateFlip(RotateMode.Rotate90, FlipMode.None));
break;
case 7:
image.Mutate(x => x.RotateFlip(RotateMode.Rotate270, FlipMode.Horizontal));
break;
case 8:
image.Mutate(x => x.RotateFlip(RotateMode.Rotate270, FlipMode.None));
break;
}
image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)1);
var outStream = new MemoryStream();
image.Save(outStream, new JpegEncoder{Quality = 100});
outStream.Position = 0;
return outStream;
}
}
}
And to use in conjunction with Xam.Plugin.Media:
MediaFile photo = await CrossMedia.Current.TakePhotoAsync(options);
await using Stream stream = ImageOrientationUtility.NormalizeOrientation(photo.GetStream);
I'm trying to grab two pictures from an basler GigE camera video feed with pylon and opencv. The two pictures are converted to grayscale and compared using absdiff. The pixels that have changed should be the object i'm looking for.
Here is the while-loop of the code.
while(camera.IsGrabbing()) {
camera.RetrieveResult(5000,ptrGrabResult, TimeoutHandling_ThrowException);
if(ptrGrabResult->GrabSucceeded()){
formatConverter.Convert(pylonImage, ptrGrabResult);
openCVImage1 = cv::Mat(ptrGrabResult->GetHeight(), ptrGrabResult->GetWidth(), CV_8UC3, (uint8_t *) pylonImage.GetBuffer());
}
else{
cout<<"ERROR OpenCVImage1:" <<ptrGrabResult->GetErrorCode()<<ptrGrabResult->GetErrorDescription()<< endl;
}
camera.RetrieveResult(5000,ptrGrabResult, TimeoutHandling_ThrowException);
if(ptrGrabResult->GrabSucceeded()){
formatConverter.Convert(pylonImage, ptrGrabResult);
openCVImage2 = cv::Mat(ptrGrabResult->GetHeight(), ptrGrabResult->GetWidth(), CV_8UC3, (uint8_t *) pylonImage.GetBuffer());
}
else{
cout<<"ERROR OpenCVImage2:" <<ptrGrabResult->GetErrorCode()<<ptrGrabResult->GetErrorDescription()<< endl;
}
cvtColor(openCVImage1,grayImage1,CV_BGR2GRAY);
cvtColor(openCVImage2,grayImage2,CV_BGR2GRAY);
absdiff(grayImage1,grayImage2,differenceImage);
imshow("DiffImage", differenceImage);
threshold(differenceImage,thresholdImage,sensitivity, 255, THRESH_BINARY);
switch(waitKey(10)){
case 27: //ESC
return 0;
case 116: //t
trackingEnabled = !trackingEnabled;
if(trackingEnabled=false) cout<<"tracking disabled"<<endl;
else cout<<"tracking enabled"<<endl;
break;
case 112: //d
debugMode=!debugMode;
if(debugMode==false){cout<<"Code paused, press 'p' again to resume"<<endl;
while (pause==true) {
switch(waitKey(0)){
case 112: //p
pause = false;
cout<<"Code Resumed"<<endl;
break;
}
}
}
}
//do stuff
}
I have three problems doing that.
First: The differenceImage is always black. Altough I'm moving in fornt of the camera. Probably I'm not grabbing the pictures correctly. Does anyone know what i'm doing wrong? Already tried to add a waitKey(10) before the second RetrieveResult.
Second: The switch(waitKey(10)) is not working. No Matter what i press on the keyboard, there is no output on the screen. Already tried it using if and else if, but that didn't solve the problem either.
Third: If i stop debugging and start debugging again, i get an exception caught by my catch block. Probably this is because the camera didn't stop Grabbing after i stopped debugging. If i plug the camera out and back in the script runs. I tried using stopGrabbing() and close() but it didn't work.
I'm using windows 7 with visual studio 2010!!
Thanks a lot in Advance!
I have set up my launch images / splashscreen in the Resources/splash folder with the normal naming scheme (Default~ipad.png, Default-Portrait#2x~ipad.png, etc). I have not set any of the UILaunchImage* plist entries.
When I launch my Cordova app on an iPad2, it immediately loads one of my launch images. A few seconds later (when the org.apache.cordova.splashscreen plugin starts getting run, from what I can see in the log), the launch image will change, typically to something that's a different resolution, so the entire image shifts. After that point, the image stays as it is until I call navigator.splashscreen.hide(). This has also happened on an iPhone4.
Again, the splash screen displays immediately on app open, shifts a few seconds later, the page finishes loading (according to the console) about 5 seconds in, and then navigator.splashscreen.hide() gets calls in my ready() event.
It seems to me that maybe Xcode is choosing one of my images to be the launch image by default, and then the cordova splashscreen plugin is choosing another one when it gets loaded with the other plugins. I did initially gets a black flash and a console error of "Default.png" could not be found, so I added a "Default~ipad.png" to the splash images. I had thought it would have used either the Portrait or Landscape images for ipad, but the plugin appears to only do that if CDV_IsIPad() and isOrientationLocked.
I even tried reverting all images in Resources/splash to the default Cordova launch images. When I do that, the Cordova launch image displays immediately on app load, a few seconds later, when the Cordova splashscreen plugin loads, the launch image changes to one of MY launch images, then fades on navigator.splashscreen.hide(). When this happens, I can not find any references anywhere to my launch images that I removed from the project; not in Resources/splash or in the plist. Very odd.
Any ideas why the splash screen is getting changed by the cordova splashscreen plugin, or what I should be doing to fix this?
I ended up looking at the pull requests for the Cordova Splashscreen plugin, just to see if anyone had addressed this issue. Looks like they did!
in src/ios/CDVSplashScreen.m:
- } else if (CDV_IsIPad() && isOrientationLocked) {
- switch (orientation) {
- case UIInterfaceOrientationLandscapeLeft:
- case UIInterfaceOrientationLandscapeRight:
- imageName = [imageName stringByAppendingString:#"-Landscape"];
- break;
-
- case UIInterfaceOrientationPortrait:
- case UIInterfaceOrientationPortraitUpsideDown:
- default:
- imageName = [imageName stringByAppendingString:#"-Portrait"];
- break;
should be
+ } else if (CDV_IsIPad()) {
+ if (isOrientationLocked) {
+ imageName = [imageName stringByAppendingString:(supportsLandscape ? #"-Landscape" : #"-Portrait")];
+ } else {
+ switch (orientation) {
+ case UIInterfaceOrientationLandscapeLeft:
+ case UIInterfaceOrientationLandscapeRight:
+ imageName = [imageName stringByAppendingString:#"-Landscape"];
+ break;
+
+ case UIInterfaceOrientationPortrait:
+ case UIInterfaceOrientationPortraitUpsideDown:
+ default:
+ imageName = [imageName stringByAppendingString:#"-Portrait"];
+ break;
+ }
I'll start off by admitting that I am a beginner to ActionScript and I am in the process of coding my own basic arcade game (Similar to that of the old arcade game "Joust"). Whilst I have been able to code the sprite's movement I am looking to make the sprite flip to face the other way when I press the right arrow. I figured either I could try and rotate the object around its axis (Which I've tried multiple times and has proved difficult) or I could try and "Replace" the current sprite with another sprite (Which is just the sprite facing the opposite way). I've searched everywhere for a method of replacing a sprite with another sprite but to no avail. How would it be possible to give this sprite a flip effect when a certain keyCode is used?
Try this simple code below. Here 'object' is the movieclip/sprite that you want to flip
stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
function OnKeyDown(event:KeyboardEvent):void
{
var uiKeyCode:uint = event.keyCode;
switch (uiKeyCode)
{
case Keyboard.LEFT :
object.scaleX = -1; //flip
break;
case Keyboard.RIGHT :
object.scaleX = 1; //unflip
break;
}
}
NOTE: If you want the movieclip to flip without any shift in its position then the movieclip must be horizontally center registered.
Tell me if this works for you.
Are you using as2/as3? you could flip the axis Y 180 degrees
if your using as2 you will need to either mirror the bitmap via actionScript or
add a second bitmap that is mirrored to the display list.
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressedDown);
function keyPressedDown(event:KeyboardEvent):void
{
var key:uint = event.keyCode;
switch (key)
{
case Keyboard.LEFT :
myMovieClip.rotaionY = 180; // MC will be mirrored
break;
case Keyboard.RIGHT :
myMovieClip.rotaionY = 0;
}
How to set Camera FPS?
May be
cvSetCaptureProperty(cameraCapture, CV_CAP_PROP_FPS, 30);
?
But it's return
HIGHGUI ERROR: V4L2: Unable to get property (5) - Invalid argument
Because there is no implementation in highgui/cap_v4l.cpp
static int icvSetPropertyCAM_V4L( CvCaptureCAM_V4L* capture,
int property_id, double value ){
static int width = 0, height = 0;
int retval;
/* initialization */
retval = 0;
/* two subsequent calls setting WIDTH and HEIGHT will change
the video size */
/* the first one will return an error, though. */
switch (property_id) {
case CV_CAP_PROP_FRAME_WIDTH:
width = cvRound(value);
if(width !=0 && height != 0) {
retval = icvSetVideoSize( capture, width, height);
width = height = 0;
}
break;
case CV_CAP_PROP_FRAME_HEIGHT:
height = cvRound(value);
if(width !=0 && height != 0) {
retval = icvSetVideoSize( capture, width, height);
width = height = 0;
}
break;
case CV_CAP_PROP_BRIGHTNESS:
case CV_CAP_PROP_CONTRAST:
case CV_CAP_PROP_SATURATION:
case CV_CAP_PROP_HUE:
case CV_CAP_PROP_GAIN:
case CV_CAP_PROP_EXPOSURE:
retval = icvSetControl(capture, property_id, value);
break;
default:
fprintf(stderr,
"HIGHGUI ERROR: V4L: setting property #%d is not supported\n",
property_id);
}
/* return the the status */
return retval;
}
How to solve it?
using the python wrappers for opencv, it worked for me to refer to the variable as:
cap = cv2.VideoCapture(1)
cap.set(cv2.cv.CV_CAP_PROP_FPS, 60)
I am using python 2.7.3 and opencv 2.4.8
The camera is the PS3 Eye
I don't know if that's still valid, but some time ago, something like one year and a half, I encountered that exactly problem. I contacted with a developer of OpenCV and he told me that the access and ability to change some of the properties of a capture weren't implemented yet and some other just worked for certain kinds of camera. I finally took a look to libdc1394 (working in linux) and made some functions that converted the data retrieved by libdc1394 to IplImages from OpenCV. It wasn't a such a tough task.
CV_CAP_PROP_FPS is a NOT a fake. See cap_libv4l.cpp(1) in OpenCV github repo. The key is to make sure, you use libv4l over v4l while configuring OpenCV. For that, before running cmake, install libv4l-dev
sudo apt-get install libv4l-dev
Now while configuring OpenCV with cmake, enable option, WITH_LIBV4L. If all goes good, in configuration status, you will see some thing similar to below
V4L/V4L2: Using libv4l1 (ver ) / libv4l2 (ver )
And then while building your OpenCV code, you will have to link with libv4l1/libv4l2/libv4lconvert.
Arbitary FPS values at the resolutions you choose, needn't be supported by your webcam. You may check supported resolutions/fps with a graphical tools like cheese or commands like lsusb (2)
check opencv2.4 handbook out, the video capture thing is much better than before,
->set(CV_CAP_PROP_FPS,30);works for me most of the times.
but a little bit low efficiency.
just in case you might not like the new opencv2.4 and still want to control your camera. check the videoinput lib here. it works good and using directshow features.
http://www.aishack.in/2010/03/capturing-images-with-directx/
http://www.muonics.net/school/spring05/videoInput/