When drawing from currentPoint.x and currentPoint.y to lastPoint.x and lastPoint.y
in diagonally right upside or right downwards it's giving me gaps (spaces) in the drawn line as shown in below image.
-(void)brushType{
UIGraphicsBeginImageContext(drawImage.frame.size);
CGContextRef Mycontext = UIGraphicsGetCurrentContext();
int x, cx, deltax, xstep,y, cy, deltay, ystep,error, st, dupe;
int x0, y0, x1, y1;
x0 = currentPoint.x;
y0 = currentPoint.y;
x1 = lastPoint.x;
y1 = lastPoint.y;
// find largest delta for pixel steps
st =(abs(y1 - y0) > abs(x1 - x0));
// if deltay > deltax then swap x,y
if (st) {
(x0 ^= y0);
(y0 ^= x0);
(x0 ^= y0); //swap(x0, y0);
(x1 ^= y1);
(y1 ^= x1);
(x1 ^= y1); // swap(x1, y1);
}
deltax = abs(x1 - x0);
deltay = abs(y1 - y0);
error = (deltax / 4);
y = y0;
if (x0 > x1) {
xstep = -1;
}
else {
xstep = 1;
}
if (y0 > y1) {
ystep = -1;
}
else {
ystep = 1;
}
for ((x = x0); (x != (x1 + xstep)); (x += xstep))
{
(cx = x);
(cy = y); // copy of x, copy of y
// if x,y swapped above, swap them back now
if (st) {
(cx ^= cy);
(cy ^= cx);
(cx ^= cy);
}
(dupe = 0); // initialize no dupe
if(!dupe) {
CGContextSetRGBStrokeColor(Mycontext, 0.0, 0.0, 0.0, 1.00f);
CGContextMoveToPoint(Mycontext, cx+brushSize*4,cy-brushSize/2);
CGContextAddLineToPoint(Mycontext, cx-brushSize/2, cy+brushSize*4);
}
(error -= deltay); // converge toward end of line
if (error < 0) { // not done yet
(y += ystep);
(error += deltax);}
}
CGContextStrokePath(Mycontext);
[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
brushSize = 4;
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:drawImage];
currentPoint.y -= 20;
[self brushType];
}
If anyone has an idea please helpe me to fix this issue!
I see a bunch of problems with your function, most of which are poor style. You are using XOR-swap, which makes the code hard to read. You declare all of your variables at the top of the method, making it harder to understand the lifetime of each variable. You put unnecessary parentheses all over the place, making the code harder to read. You call CGContextSetRGBStrokeColor repeatedly in your loop, but you only need to set the stroke color once. So first let's rewrite your method to fix those problems:
static inline void swap(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
-(void)brushType {
int x0 = currentPoint.x;
int y0 = currentPoint.y;
int x1 = lastPoint.x;
int y1 = lastPoint.y;
int deltax = abs(x1 - x0);
int deltay = abs(y1 - y0);
int needSwap = deltay > deltax;
if (needSwap) {
swap(&x0, &y0);
swap(&x1, &y1);
swap(&deltax, &deltay);
}
int error = deltax / 4;
int y = y0;
int xstep = x0 > x1 ? -1 : 1;
int ystep = y0 > y1 ? -1 : 1;
CGSize size = self.drawImage.bounds.size;
UIGraphicsBeginImageContext(size); {
CGContextRef gc = UIGraphicsGetCurrentContext();
for (int x = x0; x != x1 + xstep; x += xstep)
{
int cx = x;
int cy = y;
if (needSwap)
swap(&cx, &cy);
CGContextMoveToPoint(gc, cx + brushSize*4, cy - brushSize/2);
CGContextAddLineToPoint(gc, cx - brushSize/2, cy + brushSize*4);
error -= deltay; // converge toward end of line
if (error < 0) { // not done yet
y += ystep;
error += deltax;
}
}
[UIColor.blackColor setStroke];
CGContextStrokePath(gc);
[self.drawImage.image drawInRect:CGRectMake(0, 0, size.width, size.height)];
self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
} UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
So now it's easier to understand that you're stepping along the straight line from lastPoint to currentPoint. If that line is primarily horizontal, you increment x by 1 (or -1) at each step, and increment y as necessary to stay close to the true straight line. If the line is primarily vertical, you swap x and y.
Here's the problem. Suppose the straight line is at a 45 degree angle. You're going to increment x by 1 and y by 1 at every step. Pythagoras tells us that you're moving a distance of sqrt(2) ≈ 1.4142 points per step. Since your “brush” is stroked with the default stroke width of 1 point, there's a gap between adjacent stamps of the brush.
The correct way to fix this is to stop using ints and error correction terms to compute the points along the line. Core Graphics and UIKit use CGFloats anyway, so by using ints you're not only getting less accurate results, you're triggering extra conversions from CGFloat to int and back.
What you need to do is store x and y as CGFloat, and increment them both at each step so that the distance between brush stamps is 1.
-(void)brushType {
CGFloat dx = currentPoint.x - lastPoint.x;
CGFloat dy = currentPoint.y - lastPoint.y;
CGFloat length = hypotf(dx, dy);
dx /= length;
dy /= length;
CGSize size = self.drawImage.bounds.size;
// Bonus! This works correctly on Retina devices!
UIGraphicsBeginImageContextWithOptions(size, NO, self.drawImage.window.screen.scale); {
CGContextRef gc = UIGraphicsGetCurrentContext();
for (CGFloat i = 0; i < length; ++i) {
CGFloat x = lastPoint.x + i * dx;
CGFloat y = lastPoint.y + i * dy;
CGContextMoveToPoint(gc, x + brushSize * 4, y - brushSize / 2);
CGContextAddLineToPoint(gc, x - brushSize / 2, y + brushSize * 4);
}
[UIColor.blackColor setStroke];
CGContextSetLineWidth(gc, 1.01);
CGContextStrokePath(gc);
[self.drawImage.image drawAtPoint:CGPointZero];
self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
} UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
If you use this version of the method, you'll find that it looks much better, but still not quite perfect:
The problem, I believe, is that diagonal strokes are drawn using anti-aliasing, and anti-aliasing is an approximation. If you drawn two width-1 lines 1 point apart, the partial colorings that each line contributes to the common pixels don't add up perfectly.
An easy, cheesy way to fix it is to make your stroke width a little wider. Just add this before CGContextStrokePath:
CGContextSetLineWidth(gc, 1.1);
Result:
Related
Yesterday there was a rectangle stuck in the top corner of my program, and for some reason no matter what I did I could not get it out of there. Today I re-ran the program, and it disappeared, but I believe it's memory is still stuck because it's affecting my code.
I'm trying to detect when rectangle A (motioned by accelerometer) contacts rectangle B (randomly placed; motionless). Nothing happens when A contacts B, however when I move A into the top corner where the ghost rectangle was all of yesterday it runs my if statement continuously until I move it away.
Here is the code just incase it's needed
#synthesize Button;
#synthesize Rand;
//Awaking the accelerometer
-(void) awakeaccelerometer
{
[[UIAccelerometer sharedAccelerometer]setUpdateInterval:1.0/50.0];//<---Sensitivity
[[UIAccelerometer sharedAccelerometer]setDelegate:self];
NSLog(#"Accelerometer is awake");
float randX = arc4random() % 320;
float randY = arc4random() % 548;
CGPoint randNewPlace = CGPointMake(randX, randY);
Rand.center = randNewPlace;
}
//Controlling the accelerometer
-(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
#define MovingObjectRadius 4
//Acceleration for player
valueX = acceleration.x * 10; //speeds
valueY = acceleration.y * -10; //speeds
//Create new integers
int newX = (int)(Button.center.x + valueX);
int newY = (int)(Button.center.y + valueY);
//Position Validate...This stops the movements from going past the screen; 320 pixels
//For X
if (newX > (320 - MovingObjectRadius))
{
newX = (320 - MovingObjectRadius);
}
if (newX < (0 + MovingObjectRadius))
{
newX = (0 + MovingObjectRadius);
}
//For Y
if (newY > (548 - MovingObjectRadius))
{
newY = (548 - MovingObjectRadius);
}
if (newY < (0 + MovingObjectRadius))
{
newY = (0 + MovingObjectRadius);
}
//Make the new point
CGPoint buttonNewCenter = CGPointMake(newX,newY);
Button.center = buttonNewCenter;
CGRect blockRect = CGRectMake(newX, newY, 20, 20);
CGRect targetRect = CGRectMake(randX, randY, 20, 20);
if (CGRectIntersectsRect(blockRect, targetRect))
{
float tnewX = arc4random() % 320;
float tnewY = arc4random() % 548;
CGPoint randNewPl = CGPointMake(tnewX, tnewY);
Rand.center = randNewPl;
}
}
Please see the image. How can I get the two line intersection point(that is green rounded point)? I want to crop the inner part of the image. The closing path is any where in the line.
context = UIGraphicsGetCurrentContext();
CGContextBeginPath(context);
CGContextSetLineWidth(context, 1.0 * self.scale);
CGContextSetLineCap(context, kCGLineCapRound);
[[UIColor redColor] setStroke];
CGPoint firstPoint = CGPointFromString([self.touchPoints objectAtIndex:0]);
CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
for (NSString *pointString in self.touchPoints) {
CGPoint point = CGPointFromString(pointString);
CGContextAddLineToPoint(context, point.x, point.y);
}
CGContextStrokePath(context);
This code used for the draw the lines. Line drawing is working fine, cropping also working fine...But the intersection point is my major problem. Please help me.
Idea, check for intersections beginning with firstline<>lastline, firstline<>secondlastline ... firstline<>thirdline => secondline<>lastline etc. This should give you the outer most intersection.
The following Code is not tested, but should help you with your problem.
typedef struct {
CGPoint startPoint;
CGPoint endPoint;
} Line;
#define CGPointNULL CGPointMake(NAN, NAN)
#define Line(_i_) {CGPointFromString(touchPoints[_i_-1]), CGPointFromString(touchPoints[_i_])};
CGPoint LineIntersects(Line *first, Line *second) {
int x1 = first->startPoint.x; int y1 = first->startPoint.y;
int x2 = first->endPoint.x; int y2 = first->endPoint.y;
int x3 = second->startPoint.x; int y3 = second->startPoint.y;
int x4 = second->endPoint.x; int y4 = second->endPoint.y;
int d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4);
if (d == 0) return CGPointNULL;
int xi = ((x3-x4)*(x1*y2-y1*x2)-(x1-x2)*(x3*y4-y3*x4))/d;
int yi = ((y3-y4)*(x1*y2-y1*x2)-(y1-y2)*(x3*y4-y3*x4))/d;
return CGPointMake(xi,yi);
}
static inline BOOL CGPointIsValid(CGPoint p) {
return (p.x != NAN && p.y != NAN);
}
- (CGPoint)mostOuterIntersection:(NSArray *)touchPoints {
CGPoint intersection = CGPointNULL;
int touchCount = [touchPoints count];
for(int i = 1; i<touchCount; i++) {
Line first = Line(i);
for(int j = touchCount-1; j>i+1; j--) {
Line last = Line(j);
intersection = LineIntersects(&first, &last);
if(CGPointIsValid(intersection)) {
break;
}
}
}
return intersection;
}
I'm attempting to detect a piece of paper in a photo on the iPhone using OpenCV. I'm using the code from this question: OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection
Here's the code:
- (void)findEdges {
image = [[UIImage imageNamed:#"photo.JPG"] retain];
Mat matImage = [image CVMat];
find_squares(matImage, points);
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
[imageView setFrame:CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height)];
[self addSubview:imageView];
[imageView setAlpha:0.3f];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 0.8);
CGFloat scaleX = self.frame.size.width / image.size.width;
CGFloat scaleY = self.frame.size.height / image.size.height;
// Draw the detected squares.
for( vector<vector<cv::Point> >::const_iterator it = points.begin(); it != points.end(); it++ ) {
vector<cv::Point> square = *it;
cv::Point p1 = square[0];
cv::Point p2 = square[1];
cv::Point p3 = square[2];
cv::Point p4 = square[3];
CGContextBeginPath(context);
CGContextMoveToPoint(context, p1.x * scaleX, p1.y * scaleY); //start point
CGContextAddLineToPoint(context, p2.x * scaleX, p2.y * scaleY);
CGContextAddLineToPoint(context, p3.x * scaleX, p3.y * scaleY);
CGContextAddLineToPoint(context, p4.x * scaleX, p4.y * scaleY); // end path
CGContextClosePath(context);
CGContextSetLineWidth(context, 4.0);
CGContextStrokePath(context);
}
}
double angle( cv::Point pt1, cv::Point pt2, cv::Point pt0 ) {
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
void find_squares(Mat& image, vector<vector<cv::Point> >& squares) {
// blur will enhance edge detection
Mat blurred(image);
medianBlur(image, blurred, 9);
Mat gray0(blurred.size(), CV_8U), gray;
vector<vector<cv::Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 3; c++)
{
int ch[] = {c, 0};
mixChannels(&blurred, 1, &gray0, 1, ch, 1);
// try several threshold levels
const int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
// Use Canny instead of zero threshold level!
// Canny helps to catch squares with gradient shading
if (l == 0)
{
Canny(gray0, gray, 10, 20, 3); //
// Dilate helps to remove potential holes between edge segments
dilate(gray, gray, Mat(), cv::Point(-1,-1));
}
else
{
gray = gray0 >= (l+1) * 255 / threshold_level;
}
// Find contours and store them in a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Test contours
vector<cv::Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
Here's the input image:
Here's the result:
What am I doing wrong?
brushSize=1.0f;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)]; //originally self.frame.size.width, self.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapButt); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushSize);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 1.0, 0.0, 1.0);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x+brushSize*4,lastPoint.y-brushSize);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPoint.x-brushSize, lastPoint.y+brushSize*4);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I am using this Code for drawing......In touchesmoved.
i do getting gaps when iam drawing fastly... what to do?
You should use UITouch's previousLocationInView: method to get the previous known point you finger was and draw a line between that point and the current location.
Another solution is to keep the currentPoint in a static variable between two calls.
CGContextBeginPath(Mycontext);
CGContextMoveToPoint(Mycontext, lastPoint.x, lastPoint.y);
int x, cx, deltax, xstep,y, cy, deltay, ystep,error, st, dupe;
int x0, y0, x1, y1;
x0 = currentPoint.x;
y0 = currentPoint.y;
x1 = lastPoint.x;
y1 = lastPoint.y;
// find largest delta for pixel steps
st = (abs(y1 - y0) > abs(x1 - x0));
// if deltay > deltax then swap x,y
if (st) {
(x0 ^= y0); (y0 ^= x0); (x0 ^= y0); // swap(x0, y0);
(x1 ^= y1); (y1 ^= x1); (x1 ^= y1); // swap(x1, y1);
}
deltax = abs(x1 - x0);
deltay = abs(y1 - y0);
error = (deltax / 2);
y = y0;
if (x0 > x1) { xstep = -1; }
else { xstep = 1; }
if (y0 > y1) { ystep = -1; }
else { ystep = 1; }
for ((x = x0); (x != (x1 + xstep)); (x += xstep))
{
(cx = x); (cy = y); // copy of x, copy of y
// if x,y swapped above, swap them back now
if (st) { (cx ^= cy); (cy ^= cx); (cx ^= cy); }
(dupe = 0); // initialize no dupe
if(!dupe) {
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), cx+brushSize*4,cy-brushSize);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), cx-brushSize, cy+brushSize*4);
}
(error -= deltay); // converge toward end of line
if (error < 0) { // not done yet
(y += ystep);
(error += deltax);
}
}
CGContextStrokePath(Mycontext);
Image_Cookie.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
I have used the code below for drawing a line between two points.
#define PI 3.14159
CGContextBeginPath(ctx);
int x;
for(int y=rect.origin.y; y < rect.size.height; y++)
{
x = ((rect.size.width/4) * sin(((y*4) % 360) * PI/180)) + rect.size.width/2;
if(y == 0)
{
CGContextMoveToPoint(ctx, x, y);
}
else
{
CGContextAddLineToPoint(ctx, x, y);
}
}
CGContextStrokePath(ctx);
But I want to implement this with animation. How would it be possible? Can you help me?