CraftAR Image recognition - Translating matchBoundingBox to points in screen - ios

I am using on device image recognition from Catchoom CraftAR and working with the example available on Github https://github.com/Catchoom/craftar-example-ios-on-device-image-recognition.
The image recognition works, I would like to use the matchBoundingBox to draw some squares on all the 4 corners. Somehow the calculations I am doing are not working, I have based them on this article:
http://support.catchoom.com/customer/portal/articles/1886553-obtain-the-bounding-boxes-of-the-results-of-image-recognition
The square views are added to the scanning overlay and this is how I am calculating the points where to add the 4 views:
CraftARSearchResult *bestResult = [results objectAtIndex:0];
BoundingBox *box = bestResult.matchBoundingBox;
float w = self._preview.frame.size.width;
float h = self._preview.frame.size.height;
CGPoint tr = CGPointMake(w * box.topRightX , h * box.topRightY);
CGPoint tl = CGPointMake(w * box.topLeftX, h * box.topLeftY);
CGPoint br = CGPointMake(w * box.bottomRightX, h * box.bottomRightY);
CGPoint bl = CGPointMake(w * box.bottomLeftX, h * box.bottomLeftY);
The x position looks like it is pretty close, but the y position is completely off and looks like mirrored.
I am testing on iOS 10 iPhone 6s
Am I missing something?

The issue was that I was using the preview frame to make the translation to the points in screen. But the points that come through with bounding box are not relative to the preview view, they are relative to the VideoFrame (as the support people of catchoom.com pointed out). The VideoFrame size is set by the capturePreset which only accepts two values AVCaptureSessionPreset1280x720 and AVCaptureSessionPreset640x480. The default one is AVCaptureSessionPreset1280x720
So in my case I had to make the calculations with size 1280x720 and then make the conversion from those coordinates to the coordinates in my preview view size.
So it ended up looking like this:
let box = bestResult.matchBoundingBox
let wVideoFrame:CGFloat = 1080.0;
let hVideoFrame:CGFloat = 720.0;
let wRelativePreview = wVideoFrame/CGFloat(preview.frame.size.height)
let hRelativePreview = wVideoFrame/CGFloat(preview.frame.size.width)
var tl = CGPoint(x: wVideoFrame * CGFloat(box.topLeftX),y: hVideoFrame * CGFloat(box.topLeftY));
var tr = CGPoint(x: wVideoFrame * CGFloat(box.topRightX) ,y: hVideoFrame * CGFloat(box.topRightY));
var br = CGPoint(x: wVideoFrame * CGFloat(box.bottomRightX),y: hVideoFrame * CGFloat(box.bottomRightY));
var bl = CGPoint(x: wVideoFrame * CGFloat(box.bottomLeftX),y: hVideoFrame * CGFloat(box.bottomLeftY));
tl = CGPoint(x: tl.x/wRelativePreview, y: tl.y/hRelativePreview)
tr = CGPoint(x: tr.x/wRelativePreview, y: tr.y/hRelativePreview)
br = CGPoint(x: br.x/wRelativePreview, y: br.y/hRelativePreview)
bl = CGPoint(x: bl.x/wRelativePreview, y: bl.y/hRelativePreview)
// 4 square visualize top-left, top.right, bottom-left and bottom-right points
var fr = vTL.frame;
fr.origin = tl;
vTL.frame = fr;
fr.origin = tr;
vTR.frame = fr;
fr.origin = br;
vBR.frame = fr;
fr.origin = bl;
vBL.frame = fr;
Now the points looked quite ok on screen, but they looked some how rotated. So I rotated the view 90 degrees:
// overlay is the container of the 3 squares to visualize the points in screen
overlay.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI/2.0))
Note this is not the official response from support from catchoom, this might not be 100% correct, but it worked for me quite well.

Related

ARKit Convert 3d object position to UIView coordinates

I have created measure demo which allow to put multiple points and show distance between them. which works fine
I want to show preview that what so far has been drawn in real world to the UIView using UIBezierPath . Just like http://armeasure.com/
I have tried many things to achieve this but I couldn't find any right way to do it.
if self.linkList.count == 1 {
bezierPath.removeAllPoints()
bezierPath.move(to: CGPoint(x: 10,y: 10))
} else {
guard self.linkList.count > 1 ,let object2 = self.linkList.lastNode, let object1 = self.linkList.lastNode?.previous else {return}
let value = self.getMeasurementXandYBetween(vector1: object1.node.mainNode.position, and: object2.node.mainNode.position)
print(value)
let x = Double((object1.node.mainNode.position.x + value ) * 377.9527559055 )
let y = Double((object1.node.mainNode.position.y + value) * 377.9527559055)
let pointCoordinates = CGPoint(x: x , y: y)
print("x : Y ",x,y)
bezierPath.addLine(to: pointCoordinates)
}
shapeLayer.removeFromSuperlayer()
shapeLayer.path = bezierPath.cgPath
shapeLayer.lineWidth = 0.5
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.position = CGPoint(x: 0, y: 0)
self.viewToDraw.layer.addSublayer(shapeLayer)
func getMeasurementXandYBetween(vector1:SCNVector3, and vector2:SCNVector3) -> Float {
return sqrtf((vector1.x - vector2.x) * (vector1.x - vector2.x) + (vector1.y - vector2.y) * (vector1.y - vector2.y))
}
The logic I used (which is not working is) Location of previous node + distance I got from getMeasurementXandYBetween multiply by 377.
Please suggest a hint or any other solution
You can get the coordinates of any point in screen-space by using the projectPoint() on your SCNSceneRenderer.
This will give you a vector with 3 elements, build your CGPoint using the first two and build your shape from those points.

Make bullets fire outwards in a circle

I have a player that when the user taps I want to be able to spawn 8 bullets around the player (with a 45˚ separation between them) and proceed to move them outwards to the edge of the screen.
The circle from where the bullets originate from is correct, but the bullets in the bottom left of the screen seem to be moving faster than the ones in the top right. Also the bullets are facing sideways, not pointing outwards.
func fireSpecialWeapon() {
stride(from: 0, to: 2 * CGFloat.pi, by: 2 * CGFloat.pi / 10 ).forEach { angle in
let bullet = SKSpriteNode(imageNamed: "bulletCircle")
bullet.setScale(3)
bullet.zRotation = angle
bullet.position = player.position
bullet.zPosition = 2
//move outwards to the edge of the screen
let distance: CGFloat = 2000
let endPoint = CGPoint(x: distance * cos(angle), y: distance * sin(angle))
let move = SKAction.move(to: endPoint, duration: 2)
self.addChild(bullet)
bullet.run(move)
}
}
You should use trig to figure out the end point based on the angle. distance * sin is the y component and distance * cos is the x component. The code looks somethign like this:
stride(from: 0, to: 2 * CGFloat.pi, by: 2 * CGFloat.pi / 8).forEach { angle in
let bullet = SKSpriteNode(imageNamed: "bulletCircle")
bullet.setScale(3)
bullet.zRotation = angle
bullet.position = player.position
bullet.zPosition = 2
//move outwards to the edge of the screen
let distance: CGFloat = 500
let endPoint = CGPoint(x: distance * cos(angle), y: distance * sin(angle))
let move = SKAction.move(to: endPoint, duration: 2)
self.addChild(bullet)
bullet.run(move)
}
The first thing I noticed is that your rotation calculation is not correct.
First the rotation of each bullet should be pi / 4 from its neighbours.
So you should not use pi / I but (pi / 4) * I.
That should fix up the rotation but I’m not sure if that’s everything that’s not working.

How to calculate the correct position for a matrix of circles to fit inside given view with proportional gaps?

I am pretty new to iOS development and I am trying to display a 10x10 grid inside a UIView respecting its bounds and I would like that the circles would be calculated based on the available width/height of the device.
What I tried so far without luck:
func setUpPoints() {
let matrixSize = 10
let diameter = (min(painelView.frame.size.width, painelView.frame.size.height) / CGFloat(matrixSize + 1)).rounded(.down)
let radius = diameter / 2
for i in 0...matrixSize {
for j in 0...matrixSize {
let x = CGFloat(i) * diameter + radius
let y = CGFloat(j) * diameter + radius
let frame = CGRect(x: x, y: y, width: diameter, height: diameter)
let circle = Circle(frame: frame)
circle.tag = j * matrixSize + i + 1
painelView.addSubview(circle)
}
}
}
My goal is to distribute the circles inside the gray rectangle proportionally so it will look like the Android pattern lock screen:
Can someone please give me some pointers?
Thanks.
If I understand what you are trying to do, then the following line:
let radius = (painelView.frame.size.width + painelView.frame.size.height) / CGFloat(matrixSize * 2)
should be:
let radius = (min(painelView.frame.size.width, painelView.frame.size.height) / CGFloat(matrixSize + 1)).rounded(.down)
The above change will allow the "square" of circles fit within whichever is smaller - the view's width or height, allowing for a gap around the "square" equal to half the diameter of each circle.
You also need to change both loops to start with 0.
for i in 0..<matrixSize {
for j in 0..<matrixSize {
BTW - your radius variable is really the diameter. And gap is really the radius.
The following code provides a border around the square of circles and it includes some space between the circles. Adjust as needed.
func setUpPoints() {
let matrixSize = 10
let borderRatio = CGFloat(0.5) // half a circle diameter - change as desired
let gapRatio = CGFloat(0.25) // quarter circle diameter - change as desired
let squareSize = min(painelView.frame.size.width, painelView.frame.size.height)
let diameter = (squareSize / (CGFloat(matrixSize) + 2 * borderRatio + CGFloat(matrixSize - 1) * gapRatio)).rounded(.down)
let centerToCenter = (diameter + diameter * gapRatio).rounded(.down)
let borderSize = (diameter * borderRatio).rounded()
for i in 0..<matrixSize {
for j in 0..<matrixSize {
let x = CGFloat(i) * centerToCenter + borderSize
let y = CGFloat(j) * centerToCenter + borderSize
let frame = CGRect(x: x, y: y, width: diameter, height: diameter)
let circle = Circle(frame: frame)
circle.tag = j * matrixSize + i + 1
painelView.addSubview(circle)
}
}
}

how to set dots using uiimage or uibutton

Here i have some json data like bone = 24. based on this json value i have to change some small dots to big dots as i show in my below image.
Like my above image . (please consider image 1). it have 10 big dots. And based on that bone.label value i have to change some big dots to small dots.
My doubt is:
How to set that dot image using (uiimage or uibutton). my idea is to set 10 uiimage and then using if statement condition i will change big dots to small dots. but if i do in this way i am not able to set constraints. is they any other idea to dots in above my cirecle??
Do it in code, not in Interface builder, and without constraints. I doubt that working with circular relations is working well with autolayout constraints.
If you position the dot images with their center instead of the frame, their location is also not changing with their size.
Use the same size for all of your UIImageViews and then just change the image to a small or large dot. Set the content mode of the UIImageViews to center, so that the images won't be scaled to fit the size of the UIImageView.
UPDATE
You could solve the positioning of the dots by drawing them in a UIView instead of using constraints. Here's a function that returns an array with CGPoints evenly distributed around a circle, which you could use when drawing the dots:
func generatePoints(totalPoints: Int, center: CGPoint, radius: Double) -> [CGPoint] {
let arc: Double = 360
let startAngle: Double = 180
let mpi: Double = M_PI/180
var startRadians: Double = startAngle * mpi
let incrementAngle: Double = arc/Double(totalPoints)
let incrementRadians = incrementAngle * mpi
var points = [CGPoint]()
var currentPoint = totalPoints
while currentPoint > 0 {
currentPoint--
let xp = CGFloat(ceil(Double(center.x) + sin(startRadians) * radius))
let yp = CGFloat(ceil(Double(center.y) + cos(startRadians) * radius))
points.append(CGPointMake(xp, yp))
startRadians -= incrementRadians
}
return points;
}
Objective-C version (somewhat ugly, because it's from an ooooooold project of mine):
+ (NSArray*)generatePoints:(NSInteger)totalPoints center:(CGPoint)center radius:(NSInteger)radius {
float circleradius = (float)radius;
const float arc = 360.f;
float startAngle = 180.f;
float mpi = M_PI/180.f;
float startRadians = startAngle * mpi;
float incrementAngle = arc/(float)totalPoints;
float incrementRadians = incrementAngle * mpi;
NSMutableArray *pts = [[NSMutableArray alloc] initWithCapacity:totalPoints];
while (totalPoints--) {
float xp = ceilf(center.x + sinf(startRadians) * circleradius);
float yp = ceilf(center.y + cosf(startRadians) * circleradius);
[pts addObject:[NSValue valueWithCGPoint:CGPointMake(xp, yp)]];
startRadians -= incrementRadians;
}
return pts;
}

Create arc between two CGPoints and get CGPoints along it

I want to display images on an arc between two points. I have a starting CGPoint and an ending CGPoint. I've seen answers for something like this using SceneKit and other answers that produce entire arches and graphs. I'm just working within a regular view.
My end goal is to end up with something like this (where letters represent images):
And I need to know where to set the center for B & C.
let firstLabel = UILabel(frame: CGRectMake(12, 200, 50, 50))
let lastLabel = UILabel(frame: CGRectMake(250, 360, 50, 50))
Here you probably need parametric equation of ellipse rather than circle. It looks like this:
x = a*cos(t) y = b*sin(t), where 0<=t<=2π
a and b are ellipse's radiuses. If we take that A is (x1, y1) and D is (x2, y2), n - number of points you wish between A and D (2 in your example - B and C) then point calculation should look something like this:
let a = x2 - x1
let b = y2 - y1
let angleStep = M_PI_2 / Double(n + 1)
var angle = angleStep
var points: [CGPoint] = []
while angle < M_PI_2 {
let x = a * CGFloat(cos(angle))
let y = y2 - b * CGFloat(sin(angle))
points.append(CGPointMake(x, y))
angle += angleStep
}

Resources