Drawing line on a UIImage NOT UIImageView - Xamarin iOS - ios

Hello I currently have this method which draws line on an UIImageView.
However I am trying to make it compatible with a UIImage and have not had any luck. This example here works beautifully for text but not great for lines.
DrawOnUIImageView.cs
private void Draw(Face face, UIImageView imageView)
{
CAShapeLayer boundingBoxLayer = new CAShapeLayer();
boundingBoxLayer.Frame = face.rect;
boundingBoxLayer.FillColor = null;
boundingBoxLayer.StrokeColor = UIColor.Red.CGColor;
imageView.Layer.AddSublayer(boundingBoxLayer);
CAShapeLayer secondBoxLayer = new CAShapeLayer();
secondBoxLayer.FillColor = null;
secondBoxLayer.StrokeColor = UIColor.Green.CGColor;
boundingBoxLayer.AddSublayer(secondBoxLayer);
var path = new CGPath();
List<LandmarkLine> lines = new List<LandmarkLine>();
foreach (var landmark in face.landmarks)
{
List<CGPoint> addTo = new List<CGPoint>();
foreach (var point in landmark.points)
{
addTo.Add(new CGPoint((point.X * face.rect.Width), (1 - point.Y) * face.rect.Height));
}
CGPath outline = new CGPath();
outline.AddLines(addTo.ToArray());
outline.CloseSubpath();
path.AddPath(outline);
}
secondBoxLayer.Path = path;
//imageView.Layer.AddSublayer(outline);
}
Any advice on this would be great. Thanks

You can draw a line on your image like this:
private UIImage drawLineOnImage(UIImage img)
{
//UIImage orgImage = <YOUR IMAGE>
UIGraphics.BeginImageContext(orgImage.Size);
// 1: Draw the original image as the background
orgImage.Draw(new RectangleF(0,0,(float)orgImage.Size.Width,(float)orgImage.Size.Height));
// 2: Draw the line on the image
CGContext context = UIGraphics.GetCurrentContext();
context.SetLineWidth(1.0f);
context.MoveTo(0, 80);
context.AddLineToPoint(orgImage.Size.Width, 80);
context.SetStrokeColor(UIColor.Blue.CGColor);
context.StrokePath();
// Create new image
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
// Tidy up
UIGraphics.EndImageContext();
return image;
}
This code will create a new image as the original image size, then draw a copy of the original image onto the new image and draw a line on the new image.

Related

Cropping UIImage Using VNRectangleObservation

I'm using the Vision framework to detect rectangular documents in a captured photo. Detecting and drawing a path around the document is working perfectly. I then want to crop the image to be only the detected document. I'm successfully cropping the image, but it seems the coordinates don't line up and the cropped image is only part of the detected document and the rest is just the desk behind the document. I'm using the following cropping code:
private UIImage CropImage(UIImage image, CGRect rect, float scale)
{
var drawRect = new CGRect(rect.X, rect.Y, rect.Size.Width, rect.Size.Height);
using (var cgImage = image.CGImage.WithImageInRect(drawRect))
{
var croppedImage = UIImage.FromImage(cgImage);
return croppedImage;
};
}
Using the following parameters:
image is the same UIImage that i successfully drew the rectangle path on.
rect is the VNRectangleObservation.BoundingBox. This is normalized so i'm scaling it using the image.size. it's the same scaling i do when drawing the rectangle path.
scale is 1f, but i'm currently ignoring this.
The cropped image generally seems to be the right size, but it is shifted up and to the left which cuts off the lower and right side of the document. Any help would be appreciated.
for anyone else that finds this, the issue seemed to be CGImage rotating when cropping the image which caused the VNRectangleObservation to not line up anymore. I used this article, Tracking and Altering Images, to get a working solution using CIFilter. Cropping code follows:
var ciFilter = CIFilter.FromName("CIPerspectiveCorrection");
if (ciFilter == null) continue;
var width = inputImage.Extent.Width;
var height = inputImage.Extent.Height;
var topLeft = new CGPoint(observation.TopLeft.X * width, observation.TopLeft.Y * height);
var topRight = new CGPoint(observation.TopRight.X * width, observation.TopRight.Y * height);
var bottomLeft = new CGPoint(observation.BottomLeft.X * width, observation.BottomLeft.Y * height);
var bottomRight = new CGPoint(observation.BottomRight.X * width, observation.BottomRight.Y * height);
ciFilter.SetValueForKey(new CIVector(topLeft), new NSString("inputTopLeft"));
ciFilter.SetValueForKey(new CIVector(topRight), new NSString("inputTopRight"));
ciFilter.SetValueForKey(new CIVector(bottomLeft), new NSString("inputBottomLeft"));
ciFilter.SetValueForKey(new CIVector(bottomRight), new NSString("inputBottomRight"));
var ciImage = inputImage.CreateByApplyingOrientation(CGImagePropertyOrientation.Up);
ciFilter.SetValueForKey(ciImage, CIFilterInputKey.Image);
var outputImage = ciFilter.OutputImage;
var uiImage = new UIImage(outputImage);
imageList.Add(uiImage);
imageList is a List<UImage> since i'm handling multiple detected rectangles.
observation is a single observation of type VNRectangleObservation.
The cropped image generally seems to be the right size, but it is shifted up and to the left which cuts off the lower and right side of the document.
From Apple documentation CGImageCreateWithImageInRect , there is a discussion about the cropped size .
CGImageCreateWithImageInRect performs the following tasks to create the subimage:
It calls the CGRectIntegral function to adjust the rect parameter to integral bounds.
It intersects the rect with a rectangle whose origin is (0,0) and size is equal to the size of the image specified by the image parameter.
It reads the pixels within the resulting rectangle, treating the first pixel within as the origin of the subimage.
If W and H are the width and height of image, respectively, then the point (0,0) corresponds to the first pixel of the image data. The point (W–1, 0) is the last pixel of the first row of the image data, while (0, H–1) is the first pixel of the last row of the image data and (W–1, H–1) is the last pixel of the last row of the image data.
Then you can check in your local project with an image (size is : 1920 * 1080) as follow:
UIImageView imageView = new UIImageView(new CGRect(0, 400, UIScreen.MainScreen.Bounds.Size.Width, 300));
UIImage image = new UIImage("th.jpg");
imageView.Image = CropImage(image, new CGRect(0, 0, 1920, 1080), 1);
View.AddSubview(imageView);
The CropImage Method :
private UIImage CropImage(UIImage image, CGRect rect, float scale)
{
var drawRect = new CGRect(rect.X, rect.Y, rect.Size.Width, rect.Size.Height);
using (var cgImage = image.CGImage.WithImageInRect(drawRect))
{
if(null != cgImage)
{
var croppedImage = UIImage.FromImage(cgImage);
return croppedImage;
}
else
{
return image;
}
};
}
This will show the Original Size of Image :
Now you can modify the cropped size as follow :
UIImageView imageView = new UIImageView(new CGRect(0, 400, UIScreen.MainScreen.Bounds.Size.Width, 300));
UIImage image = new UIImage("th.jpg");
imageView.Image = CropImage(image, new CGRect(0, 0, 1920, 100), 1);
View.AddSubview(imageView);
Here I set x = 0 , y = 0 , that means from (0,0) to start , and width is 1920 ,height is 100 . I just crop the height of the original Image . The effect as follow :
Then if you modify the x/y ,the cropped image will move to other area to crop .As follow:
UIImageView imageView = new UIImageView(new CGRect(0, 400, UIScreen.MainScreen.Bounds.Size.Width, 300));
UIImage image = new UIImage("th.jpg");
imageView.Image = CropImage(image, new CGRect(0, 100, 1920, 100), 1);
View.AddSubview(imageView);
Then you will see it's different with the second effect :
So when cropping an image , you should understand the drawRect of image.CGImage.WithImageInRect(drawRect) clearly .
Note from doc :
Be sure to specify the subrectangle's coordinates relative to the original image's full size, even if the UIImageView shows only a scaled version.

Why Circle detection detects many circles than its there?

I am using following functions to detect images. However, it detects thousands of circles instead of 16. How can I make sure it only detects what I see? Changing Radius or Relative intensity do not make any difference.
The images I used is this :
Bitmap ImageBitmap = (Bitmap)pictureBox1.Image;
var filter = new FiltersSequence(new IFilter[]
{
Grayscale.CommonAlgorithms.BT709,
new Threshold(0x40)
});
var binaryImage = filter.Apply(ImageBitmap);
// for (int i = 0; i < 10000; i++)
{
// System.Drawing.Image image = System.Drawing.Image.FromFile(imagePath);
// GrayBMP_File.CreateGrayBitmapFile(image, "c:/path/to/8bpp/image.bmp");
// Bitmap ImageBitmap = Convert.Gra ImageBitmap.Con
HoughCircleTransformation circleTransform = new HoughCircleTransformation(50);
// apply Hough circle transform
circleTransform.ProcessImage(binaryImage);
Bitmap houghCirlceImage = circleTransform.ToBitmap();
// get circles using relative intensity
HoughCircle[] circles = circleTransform.GetCirclesByRelativeIntensity(0.9);
int numCircles = circleTransform.CirclesCount;
label1.Text = numCircles.ToString();
pictureBox1.Image = houghCirlceImage;
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(ImageBitmap);
foreach (HoughCircle circle in circles)
{
g.DrawEllipse(Pens.Green, circle.X, circle.Y, 10,10);
}
pictureBox1.Image = ImageBitmap;
// ImageBitmap.Dispose();
// binaryImage.Dispose();
}
Try this python solution from here:
import cv2
import numpy as np
img = cv2.imread('test.jpg',0)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
d=1
for i in circles[0,:]:
# draw the outer circle
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
crop_img=img[i[0]-i[2]-2:i[0]+i[2]+2,i[1]-i[2]-2:i[1]+i[2]+2]
cv2.imshow('cropped circle',crop_img)
cv2.imwrite('test_%d.png'%d,crop_img)
cv2.waitKey(0)
d+=1
cv2.imshow('detected circles',cimg)
print(len(circles[0,:]))
cv2.waitKey(0)
cv2.destroyAllWindows()
OUTPUT:
16

Fade UICollectionViewCells with overlap goes wrong

I have an UICollectionView with some UICollectionViewCells. Cells are supposed to overlap each other, but also to fade a bit based on their position. See below the result:
How can I avoid those corners to be visible? (top between 3 and 4, or 4 and 5, or all the right side between 5 and 6). They should overlap, but that should not affect the image.
In order to create a fade effect I would use an overlay like this:
Save the original image in a variable to be able to reset the process for different alpha values
Draw a shape that has same color as background (color alpha should be proportional with the item position) on top of your current image
Replace the result image with your current one
I will give you an example to illustrate better:
private UIImage baseImage;
private UIImage ChangeImageColor(UIImage image, nfloat alpha, UIColor color)
{
var alphaColor = color.ColorWithAlpha(alpha);
if(baseImage == null)
{
baseImage = image;
}
else
{
image = baseImage;
}
UIGraphics.BeginImageContextWithOptions(image.Size, false, UISCreen.MainScreen.Scale);
var context = UIGraphics.GetCurrentContext();
alphaColor.SetFill();
context.TranslateCTM(0, image.Size.Height);
context.ScaleCTM(new nfloat(1.0), new nfloat(-1.0));
context.SetBlendMode(CGBlendMode.Lighten);
var rect = new CGRect(0, 0, image.Size.Width, image.Size.Height);
context.DrawImage(rect, image.CGImage);
context.SetBlendMode(CGBlendMode.SourceAtop);
context.AddRect(rect);
context.DrawPath(CGPathDrawingMode.Fill);
image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return image;
}

Xamarin iOS screenshot gets truncated when the content is wider than the frame

I am trying to save a screenshot of a chart using xamarin. Everything is fine, except that there are times that the chart is wider than the frame, and the user needs to scroll - saving a screenshot of this gives me a truncated chart. Been scratching my head on this for almost 2 days now, below is the code. Hope anyone can point me to something.
UIImage image;
UIGraphics.BeginImageContext (UIScreen.MainScreen.ApplicationFrame.Size);
View.DrawViewHierarchy (View.Frame, true);
image = UIGraphics.GetImageFromCurrentImageContext ();
UIGraphics.EndImageContext ();
image.SaveToPhotosAlbum((img, err) => {
...
});
try use a function like this:
public UIImage SnapshotImageWithCrop (UIView view, float locHorizontal, float locVertical, float widthSize, float heightSize)
{
UIGraphics.BeginImageContext (view.Frame.Size);
view.DrawViewHierarchy (view.Frame, true);
UIImage image = UIGraphics.GetImageFromCurrentImageContext ();
UIGraphics.EndImageContext ();
UIImage croppedImage;
using (CGImage cr = image.CGImage.WithImageInRect (new RectangleF (locHorizontal, locVertical, widthSize, heightSize))) {
croppedImage = UIImage.FromImage (cr);
}
return croppedImage;
}

how to add some text to a static image programatically, Using MonoTouch?

I have a static image of size 1024*768 with some logo on one side,
i want to have some text added to that image eg: Page 1, (on another side)
i got some code from
public override void ViewDidLoad ()
{
try {
base.ViewDidLoad ();
UIImage ii = new UIImage (Path.Combine (NSBundle.MainBundle.BundleUrl.ToString ().Replace ("%20", " ").Replace ("file://", ""), "images2.png"));
RectangleF wholeImageRect = new RectangleF (0, 0, ii.CGImage.Width, ii.CGImage.Height);
imageView = new UIImageView (wholeImageRect);
this.View.AddSubview (imageView);
imageView.Image = DrawVerticalText ("Trail Text", 100, 100);
Console.Write ("Switch to Simulator now to see ");
Console.WriteLine ("some stupid graphics tricks");
} catch (Exception ex) {
}
}
public static UIImage DrawVerticalText (string text, int width, int height)
{
try {
float centerX = width / 2;
float centerY = height / 2;
//Create the graphics context
byte[] mybyteArray;
CGImage tt = null;
UIImage ii = new UIImage (Path.Combine (NSBundle.MainBundle.BundleUrl.ToString ().Replace ("%20", " ").Replace ("file://", ""), "images2.png"));
using (NSData imagedata = ii.AsPNG ()) {
mybyteArray = new byte[imagedata.Length];
System.Runtime.InteropServices.Marshal.Copy (imagedata.Bytes, mybyteArray, 0, Convert.ToInt32 (imagedata.Length));
using (CGBitmapContext ctx = new CGBitmapContext (mybyteArray, width, height, 8, 4 * width, CGColorSpace.CreateDeviceRGB (), CGImageAlphaInfo.PremultipliedFirst)) {
//Set the font
ctx.SelectFont ("Arial", 16f, CGTextEncoding.MacRoman);
//Measure the text's width - This involves drawing an invisible string to calculate the X position difference
float start, end, textWidth;
//Get the texts current position
start = ctx.TextPosition.X;
//Set the drawing mode to invisible
ctx.SetTextDrawingMode (CGTextDrawingMode.Invisible);
//Draw the text at the current position
ctx.ShowText (text);
//Get the end position
end = ctx.TextPosition.X;
//Subtract start from end to get the text's width
textWidth = end - start;
//Set the fill color to blue
ctx.SetRGBFillColor (0f, 0f, 1f, 1f);
//Set the drawing mode back to something that will actually draw Fill for example
ctx.SetTextDrawingMode (CGTextDrawingMode.Fill);
//Set the text rotation to 90 degrees - Vertical from bottom to top.
ctx.TextMatrix = CGAffineTransform.MakeRotation ((float)(360 * 0.01745329f));
//Draw the text at the center of the image.
ctx.ShowTextAtPoint (2, 2, text);
tt = ctx.ToImage ();
}
}
//Return the image
return UIImage.FromImage (tt);
} catch (Exception ex) {
return new UIImage (Path.Combine (NSBundle.MainBundle.BundleUrl.ToString ().Replace ("%20", " ").Replace ("file://", ""), "images2.png"));
}
}
the output i am getting as following
As you can see it gets completely stretched in terms of width, i need this to be solved Any suggestions ???
At the same time the original image has nothing in the upper part, where as after processing it shows multi coloured layer, how to fix that ??
Why do you not draw your text directly to the image? Perhaps you can try this:
private static UIImage PutTextOnImage(UIImage image, string text, float x, float y)
{
UIGraphics.BeginImageContext(new CGSize(image.Size.Width, image.Size.Height));
using (CGContext context = UIGraphics.GetCurrentContext())
{
// Copy original image
var rect = new CGRect(0, 0, image.Size.Width, image.Size.Height);
context.SetFillColor(UIColor.Black.CGColor);
image.Draw(rect);
// Use ScaleCTM to correct upside-down imaging
context.ScaleCTM(1f, -1f);
// Set the fill color for the text
context.SetTextDrawingMode(CGTextDrawingMode.Fill);
context.SetFillColor(UIColor.FromRGB(255, 0, 0).CGColor);
// Draw the text with textSize
var textSize = 20f;
context.SelectFont("Arial", textSize, CGTextEncoding.MacRoman);
context.ShowTextAtPoint(x, y, text);
}
// Get the resulting image from context
var resultImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return resultImage;
}
The above method draws your text at coords x, y with given color and textsize. If you want it vertically you need to rotate the text with rotateCTM. keep in mind rotateCTM uses radius.
Add this to your using Context block (before DrawTextAtPoint):
var angle = 90;
var radius = 90 * (nfloat)Math.PI / 180;
context.RotateCTM(radius);

Resources