Rotating an Image in Silverlight without cropping - graphics

I am currently working on a simple Silverlight app that will allow people to upload an image, crop, resize and rotate it and then load it via a webservice to a CMS.
Cropping and resizing is done, however rotation is causing some problems. The image gets cropped and is off centre after the rotation.
WriteableBitmap wb = new WriteableBitmap(destWidth, destHeight);
RotateTransform rt = new RotateTransform();
rt.Angle = 90;
rt.CenterX = width/2;
rt.CenterY = height/2;
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = width;
tempImage2.Height = height;
tempImage2.Source = rawImage;
wb.Render(tempImage2,rt);
wb.Invalidate();
rawImage = wb;
message.Text = "h:" + rawImage.PixelHeight.ToString();
message.Text += ":w:" + rawImage.PixelWidth.ToString();
//Finally set the Image back
MyImage.Source = wb;
MyImage.Width = destWidth;
MyImage.Height = destHeight;
The code above only needs to rotate by 90° at this time so I'm just setting destWidth and destHeight to the height and width of the original image.

It looks like your target image is the same size as your source image. If you want to rotate over 90 degrees, your width and height should be exchanged:
WriteableBitmap wb = new WriteableBitmap(destHeight, destWidth);
Also, if you rotate about the centre of the original image, part of it will end up outside the boundaries. You could either include some translation transforms, or simply rotate the image about a different point:
rt.CenterX = rt.CenterY = Math.Min(width / 2, height / 2);
Try it with a piece of rectangular paper to see why that makes sense.

Many thanks to those above.. they helped a lot. I include here a simple example which includes the additional transform necessary to move the rotated image back to the top left corner of the result.
int width = currentImage.PixelWidth;
int height = currentImage.PixelHeight;
int full = Math.Max(width, height);
Image tempImage2 = new Image();
tempImage2.Width = full;
tempImage2.Height = full;
tempImage2.Source = currentImage;
// New bitmap has swapped width/height
WriteableBitmap wb1 = new WriteableBitmap(height,width);
TransformGroup transformGroup = new TransformGroup();
// Rotate around centre
RotateTransform rotate = new RotateTransform();
rotate.Angle = 90;
rotate.CenterX = full/2;
rotate.CenterY = full/2;
transformGroup.Children.Add(rotate);
// and transform back to top left corner of new image
TranslateTransform translate = new TranslateTransform();
translate.X = -(full - height) / 2;
translate.Y = -(full - width) / 2;
transformGroup.Children.Add(translate);
wb1.Render(tempImage2, transformGroup);
wb1.Invalidate();

If the image isn't square you will get cropping.
I know this won't give you exactly the right result, you'll need to crop it afterwards, but it will create a bitmap big enough in each direction to take the rotated image.
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = Math.Max(width, height);
tempImage2.Height = Math.Max(width, height);
tempImage2.Source = rawImage;

You need to calculate the scaling based on the rotation of the corners relative to the centre.
If the image is a square only one corner is needed, but for a rectangle you need to check 2 corners in order to see if a vertical or horizontal edge is overlapped. This check is a linear comparison of how much the rectangle's height and width are exceeded.
Click here for the working testbed app created for this answer (image below):
double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
The pseudo-code is as follows (actual C# code at the end):
Convert rotation angle into Radians
Calculate the "radius" from the rectangle centre to a corner
Convert BR corner position to polar coordinates
Convert BL corner position to polar coordinates
Apply the rotation to both polar coordinates
Convert the new positions back to Cartesian coordinates (ABS value)
Find the largest of the 2 horizontal positions
Find the largest of the 2 vertical positions
Calculate the delta change for horizontal size
Calculate the delta change for vertical size
Return width/2 / x if horizontal change is greater
Return height/2 / y if vertical change is greater
The result is a multiplier that will scale the image down to fit the original rectangle regardless of rotation.
**Note: While it is possible to do much of the maths using matrix operations, there are not enough calculations to warrant that. I also thought it would make a better example from first-principles.*
C# Code:
/// <summary>
/// Calculate the scaling required to fit a rectangle into a rotation of that same rectangle
/// </summary>
/// <param name="rotation">Rotation in degrees</param>
/// <param name="pixelWidth">Width in pixels</param>
/// <param name="pixelHeight">Height in pixels</param>
/// <returns>A scaling value between 1 and 0</returns>
/// <remarks>Released to the public domain 2011 - David Johnston (HiTech Magic Ltd)</remarks>
private double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
{
// Convert angle to radians for the math lib
double rotationRadians = rotation * PiDiv180;
// Centre is half the width and height
double width = pixelWidth / 2.0;
double height = pixelHeight / 2.0;
double radius = Math.Sqrt(width * width + height * height);
// Convert BR corner into polar coordinates
double angle = Math.Atan(height / width);
// Now create the matching BL corner in polar coordinates
double angle2 = Math.Atan(height / -width);
// Apply the rotation to the points
angle += rotationRadians;
angle2 += rotationRadians;
// Convert back to rectangular coordinate
double x = Math.Abs(radius * Math.Cos(angle));
double y = Math.Abs(radius * Math.Sin(angle));
double x2 = Math.Abs(radius * Math.Cos(angle2));
double y2 = Math.Abs(radius * Math.Sin(angle2));
// Find the largest extents in X & Y
x = Math.Max(x, x2);
y = Math.Max(y, y2);
// Find the largest change (pixel, not ratio)
double deltaX = x - width;
double deltaY = y - height;
// Return the ratio that will bring the largest change into the region
return (deltaX > deltaY) ? width / x : height / y;
}
Example of use:
private WriteableBitmap GenerateConstrainedBitmap(BitmapImage sourceImage, int pixelWidth, int pixelHeight, double rotation)
{
double scale = CalculateConstraintScale(rotation, pixelWidth, pixelHeight);
// Create a transform to render the image rotated and scaled
var transform = new TransformGroup();
var rt = new RotateTransform()
{
Angle = rotation,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(rt);
var st = new ScaleTransform()
{
ScaleX = scale,
ScaleY = scale,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(st);
// Resize to specified target size
var tempImage = new Image()
{
Stretch = Stretch.Fill,
Width = pixelWidth,
Height = pixelHeight,
Source = sourceImage,
};
tempImage.UpdateLayout();
// Render to a writeable bitmap
var writeableBitmap = new WriteableBitmap(pixelWidth, pixelHeight);
writeableBitmap.Render(tempImage, transform);
writeableBitmap.Invalidate();
return writeableBitmap;
}
I released a Test-bed of the code on my website so you can try it for real - click to try it
P.S. Yes this is my answer from another question, duplicated exactly, but the question does require the same answer as that one to be complete.

Related

OpenCV get pixels on an ellipse

I'm trying to get the pixels of an ellipse from an image.
For example, I draw an ellipse on a random image (sample geeksforgeeks code):
import cv2
path = r'C:\Users\Rajnish\Desktop\geeksforgeeks\geeks.png'
image = cv2.imread(path)
window_name = 'Image'
center_coordinates = (120, 100)
axesLength = (100, 50)
angle = 0
startAngle = 0
endAngle = 360
color = (0, 0, 255)
thickness = 5
image = cv2.ellipse(image, center_coordinates, axesLength,
angle, startAngle, endAngle, color, thickness)
cv2.imshow(window_name, image)
It gives output like below:
Now, I want to get the pixel value of boundary line of ellipse. If it is possible I would like to get the pixel of ellipse using cv2.ellipse() back as an array of coordinates.
Can anyone help me with this please.
There is no direct OpenCV way probably to get these points of the ellipse but you can extract your points via indirect way like this:
mask = cv2.inRange(image, np.array(color), np.array(color))
contour = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2][0]
contour will store the outer points of your red ellipse.
Here, what I have done is created a mask image of the ellipse and found the externalmost contour's points that is the required thing.
If you want to obtain points (locations) on an ellipse, you can use ellipse2Poly() function.
If the argument type of ellipse2Poly() is inconvenient, calculating by yourself is most convenient way.
This sample code is C ++, but what calculated is clear.
//Degree -> Radian
inline double RadFromDeg( double Deg ){ return CV_PI*Deg/180.0; }
//Just calculate points mathematically.
// Arguments are same as cv::ellipse2Poly (alothough ellipse parameters is cv::RotateRect).
void My_ellipse2Poly(
const cv::RotatedRect &EllipseParam,
double StartAngle_deg,
double EndAngle_deg,
double DeltaAngle_deg,
std::vector< cv::Point2d > &DstPoints
)
{
double Cos,Sin;
{
double EllipseAngleRad = RadFromDeg(EllipseParam.angle);
Cos = cos( EllipseAngleRad );
Sin = sin( EllipseAngleRad );
}
//Here, you will be able to reserve the destination vector size, but in this sample, it was omitted.
DstPoints.clear();
const double HalfW = EllipseParam.size.width * 0.5;
const double HalfH = EllipseParam.size.height * 0.5;
for( double deg=StartAngle_deg; deg<EndAngle_deg; deg+=DeltaAngle_deg )
{
double rad = RadFromDeg( deg );
double u = cos(rad) * HalfW;
double v = sin(rad) * HalfH;
double x = u*Cos + v*Sin + EllipseParam.center.x;
double y = u*Sin - v*Cos + EllipseParam.center.y;
DstPoints.emplace_back( x,y );
}
}

How to stretch geometry so its bounding box fits precisely the screen in Three.js

I am looking for the way of stretching a geometry (with all vertices z = 0) into visible screen (HTML Canvas Element).
For now I have worked out how to fit the geometry to the screen, like this:
with following code that basically adjusts camera.z to fit geometry to the height of canvas.
geometry.computeBoundingBox();
const bbox = geometry.boundingBox;
const geometryCenter = bbox.getCenter(new THREE.Vector3());
const geometrySize = bbox.getSize(new THREE.Vector3())
const cameraZ = getZFromGeometrySize(camera.fov, geometrySize);
const scale = getScaleFromZ(height, camera.fov, cameraZ);
const zoomTransform = d3.zoomIdentity
.translate(width * 0.5, height * 0.5)
.scale(scale);
zoom.transform(canvasSelection, zoomTransform);
camera.position.set(geometryCenter.x, geometryCenter.y, cameraZ)
camera.updateProjectionMatrix();
with below definitions of functions:
function getZFromGeometrySize(fov, geometrySize) {
const maxSize = Math.max( geometrySize.x, geometrySize.y );
const halfFOVRadians = toRadians(fov * 0.5);
return maxSize / ( 2 * Math.tan( halfFOVRadians ) );
}
function getScaleFromZ (height, fov, z) {
const halfFOVRadians = toRadians(fov * 0.5);
return height / (2 * Math.tan(halfFOVRadians) * z);
}
This however is using camera position so geometry will fit the view. However, I am looking for the way to stretch the geometry so its bounding box precisely fits the screen, ideally with some predefined padding.
Since this is not related to camera settings I need to manipulate geometry vertices values to stretch it horizontally. How to achieve this? I want to retain values of vertices as they relate to underlying data.
I assume this would need to be a function of canvas dimensions (width, height), geometry coordinates, and camera settings returning new geometry coordinates? Any hint is appreciated.
A short answer to this question is: to set camera's aspect ratio to 1.0.
This will work if geometry bounds are in clip space already [-1, 1 ]. If not they have to be converted to clip space first.

set coordinates as center of imageview Android programmatically

I have coordinates to align imageviews on these, programmatically. And right now images are being aligned but the image starts from this coordinate or I should say that the coordinate becomes its top left corner of my imageview and I want to make it center of my imageview. how to make coordinate center of imageview? Right now I'm doing this:
ImageView iv = new ImageView(this);
float x_coordinate = 256;
float y_coordinate = 350;
iv.setX(x_coordinate);
iv.setY(y_coordinate);
iv.setImageResource(R.drawable.myimage);
iv.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
mylayout.addView(iv);
I didn't find the simple one line solution but i solved my problem doing the following procedure. Here Place is my model class of which getX_Cord() and getY_Cord() returns the x and y coordinate of image, And you have to save an image in your drawable folder of which size you want to set on your screen in my case this is R.drawable.placeImage.
// display parameters
Point size = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getSize(size);
width = size.x;
height = size.y;
final Place place = roomPlace.get(i);
Drawable d = getResources().getDrawable(R.drawable.placeImage);
int y = d.getIntrinsicHeight() / 2;
int x = d.getIntrinsicWidth() / 2;
placeImage.setX((place.getX_Cord() * width) - x);
placeImage.setY((place.getY_Cord() * height) - y);
May be someone gets with more appropriate solution but i solved my problem doing this.

Trying to draw Rotated text with CGAffineTransform and MakeRotation appears at wrong location

I'm trying to draw some rotated texts by using the CGAffineTransform.MakeRotation method at specifc location. I also make use of the TranslateCTM, but something must be wrong as rotated texts do not appear aligned and at the correct x, y position where they should appear, here is simple the code I'm using, anyone know where the problem is? :
public override void Draw (RectangleF rect)
{
DrawTextRotated("Hello1",10,100,30);
DrawTextRotated("Hello2",50,100,60);
SetNeedsDisplay();
}
static public float DegreesToRadians(float x)
{
return (float) (Math.PI * x / 180.0);
}
public void DrawTextRotated(string text,int x, int y, int rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
c.TextMatrix = CGAffineTransform.MakeRotation((float)DegreesToRadians((float)(-rotDegree)));
c.ConcatCTM(c.TextMatrix);
float xxx = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*y);
float yyy = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*x);
// Move the context back into the view
c.TranslateCTM(-xxx,yyy);
c.SetTextDrawingMode(CGTextDrawingMode.Fill);
c.SetShouldSmoothFonts(true);
MonoTouch.Foundation.NSString str = new MonoTouch.Foundation.NSString(text);
SizeF strSize = new SizeF();
strSize = str.StringSize(UIFont.SystemFontOfSize(12));
RectangleF tmpR = new RectangleF(x,y,strSize.Width,strSize.Height);
str.DrawString(tmpR,UIFont.SystemFontOfSize(12),UILineBreakMode.WordWrap,UITextAlignment.Right);
c.RestoreState();
}
Thanks !
Here's some code that will draw text rotated properly about the top-left corner of the text. For the moment, I'm disregarding your use of text alignment.
First, a utility method to draw a marker where we expect the text to show up:
public void DrawMarker(float x, float y)
{
float SZ = 20;
CGContext c = UIGraphics.GetCurrentContext();
c.BeginPath();
c.AddLines( new [] { new PointF(x-SZ,y), new PointF(x+SZ,y) });
c.AddLines( new [] { new PointF(x,y-SZ), new PointF(x,y+SZ) });
c.StrokePath();
}
And the code to draw the text (note I've replaced all int rotations with float, and you may want negate your rotation):
public void DrawTextRotated(string text, float x, float y, float rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
DrawMarker(x,y);
// Proper rotation about a point
var m = CGAffineTransform.MakeTranslation(-x,-y);
m.Multiply( CGAffineTransform.MakeRotation(DegreesToRadians(rotDegree)));
m.Multiply( CGAffineTransform.MakeTranslation(x,y));
c.ConcatCTM( m );
// Draws text UNDER the point
// "This point represents the top-left corner of the string’s bounding box."
//http://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html
NSString ns = new NSString(text);
UIFont font = UIFont.SystemFontOfSize(12);
SizeF sz = ns.StringSize(font);
RectangleF rect = new RectangleF(x,y,sz.Width,sz.Height);
ns.DrawString( rect, font);
c.RestoreState();
}
Rotation about a point requires translation of the point to the origin followed by rotation, followed by rotation back to the original point. CGContext.TextMatrix has no effect on NSString.DrawString so you can just use ConcatCTM.
The alignment and line break modes don't have any effect. Since you're using NSString.StringSize, the bounding rectangle fits the entirety of the text, snug up against the left and right edges. If you make the width of the bounding rectangle wider and use UITextAlignment.Right, you'll get proper right alignment, but the text will still rotate around the top left corner of the entire bounding rectangle. Which is not, I'm guessing, what you're expecting.
If you want the text to rotate around the top right corner, let me know and I'll adjust the code accordingly.
Here's the code I used in my test:
DrawTextRotated("Hello 0",100, 50, 0);
DrawTextRotated("Hello 30",100,100,30);
DrawTextRotated("Hello 60",100,150,60);
DrawTextRotated("Hello 90",100,200,90);
Cheers.

How to draw circle with specific color in XNA?

XNA doesn't have any methods which support circle drawing.
Normally when I had to draw circle, always with the same color, I just made image with that circle and then I could display it as a sprite.
But now the color of the circle is specified during runtime, any ideas how to deal with that?
You can simply make an image of a circle with a Transparent background and the coloured part of the circle as White. Then, when it comes to drawing the circles in the Draw() method, select the tint as what you want it to be:
Texture2D circle = CreateCircle(100);
// Change Color.Red to the colour you want
spriteBatch.Draw(circle, new Vector2(30, 30), Color.Red);
Just for fun, here is the CreateCircle method:
public Texture2D CreateCircle(int radius)
{
int outerRadius = radius*2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.TransparentWhite;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f/radius;
for (double angle = 0; angle < Math.PI*2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
texture.SetData(data);
return texture;
}

Resources