Cropping from the same UIImage with the same CGRect gives different results

By : Elena
Source: Stackoverflow.com
Question!

I have the following functional in the app:

  1. the user takes (or chooses) an image (hereinafter originalImage).
  2. the originalImage is sent to some external API, which returns the array of coordinates of dots that I need to add to originalImage.
  3. Since the dots are always located in one area (face), I want to crop the originalImage close to the face borders and display to the user only the result of crop.
  4. After the crop result is displayed I'm adding dots to it one by one.

Here is the code that does the job (except sending image, let's say it has already happened)

class ScanResultViewController{

   @IBOutlet weak var scanPreviewImageView: UIImageView!

   let originalImage = ORIGINAL_IMAGE //meaning we already have it

   let scanDots = [["x":123, "y":123], ["x":234, "y":234]]//total 68 coordinates

   var cropRect:CGRect!

   override func viewDidLoad() {
      super.viewDidLoad()

         self.setScanImage()
   }

   override func viewDidAppear(animated: Bool) {
      super.viewDidAppear(animated)

      self.animateScan(0)
   }


   func setScanImage(){

      self.cropRect = self.getCropRect(self.scanDots, sourceImage:self.originalImage)

      let croppedImage = self.originalImage.imageAtRect(self.cropRect)

      self.scanPreviewImageView.image = croppedImage
      self.scanPreviewImageView.contentMode = .ScaleAspectFill

   }


   func animateScan(index:Int){

       let i = index

       self.originalImage = self.addOnePointToImage(self.originalImage, pointImage: GREEN_DOT!, point: self.scanDots[i])

       let croppedImage = self.originalImage.imageAtRect(self.cropRect)

       self.scanPreviewImageView.image = croppedImage
       self.scanPreviewImageView.contentMode = .ScaleAspectFill

       if i < self.scanDots.count-1{

           let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
           dispatch_after(delay, dispatch_get_main_queue()) {

               self.animateScan(i+1)
           }
       }
   }


   func addOnePointToImage(sourceImage:UIImage, pointImage:UIImage, point: Dictionary<String,CGFloat>)->UIImage{

       let rect = CGRect(x: 0, y: 0, width: sourceImage.size.width, height: sourceImage.size.height)

       UIGraphicsBeginImageContextWithOptions(sourceImage.size, true, 0)
       let context = UIGraphicsGetCurrentContext()

       CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
       CGContextFillRect(context, rect)

       sourceImage.drawInRect(rect, blendMode: .Normal, alpha: 1)

       let pointWidth = sourceImage.size.width/66.7

       pointImage.drawInRect(CGRectMake(point["x"]!-pointWidth/2, point["y"]!-pointWidth/2, pointWidth, pointWidth), blendMode: .Normal, alpha: 1)

       let result = UIGraphicsGetImageFromCurrentImageContext()
       UIGraphicsEndImageContext()

       return result
   }


   func getCropRect(points: Array<Dictionary<String,CGFloat>>, sourceImage:UIImage)->CGRect{

       var topLeft:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)
       var topRight:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)
       var bottomLeft:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)
       var bottomRight:CGPoint = CGPoint(x: points[0]["x"]!, y: points[0]["y"]!)

       for p in points{

           if p["x"]<topLeft.x {topLeft.x = p["x"]!}
           if p["y"]<topLeft.y {topLeft.y = p["y"]!}

           if p["x"]>topRight.x {topRight.x = p["x"]!}
           if p["y"]<topRight.y {topRight.y = p["y"]!}

           if p["x"]<bottomLeft.x {bottomLeft.x = p["x"]!}
           if p["y"]>bottomLeft.y {bottomLeft.y = p["y"]!}

           if p["x"]>bottomRight.x {bottomRight.x = p["x"]!}
           if p["y"]>bottomRight.y {bottomRight.y = p["y"]!}

       }

       let rect = CGRect(x: topLeft.x, y: topLeft.y, width: (topRight.x-topLeft.x), height: (bottomLeft.y-topLeft.y))

       return rect
   }

}

extension UIImage{

   public func imageAtRect(rect: CGRect) -> UIImage {
       let imageRef: CGImageRef = CGImageCreateWithImageInRect(self.CGImage, rect)!
       let subImage: UIImage = UIImage(CGImage: imageRef)

       return subImage
   }

}

The problem is that in setScanImage the desired area is accurately cropped and displayed, but when animateScan method is called a different area of the same image is cropped (and displayed) though cropRect is the same and the size of originalImage is totally the same.

Any ideas, guys?

By the way if I display originalImage without cropping it everything works smoothly.

By : Elena


Answers

So finally after approximately 10 hours net time (and a lot of help of the stackoverflow community:-) I managed to fix the problem:

In the function addOnePointToImage you need to change the following:

In this line:

UIGraphicsBeginImageContextWithOptions(sourceImage.size, true, 0)

you need to change the last argument (which stands for scale) to 1:

UIGraphicsBeginImageContextWithOptions(sourceImage.size, true, 1)

That totally resolves the issue.

By : Elena


Your java script returning colour in rgb format (backgroundColor : rgb(204, 204, 204) ).

Below code will work :

var text = document.getElementById("text");

var square = document.getElementById("square");
$(document).ready(function() {
text.onclick = function() {
  if (this.style.color === "blue") {
    this.style.color = "black";
    console.log("it worked");
  } else {
    this.style.color = "blue";
  }
};

square.onclick = function() {
  if (this.style.backgroundColor == "rgb(204, 204, 204)") {
    this.style.backgroundColor = "#fff";
     console.log("div worked");
  } else {
    this.style.backgroundColor = "#ccc";
  }
};
  });
#text {
  color: blue;
}
#square {
  height: 50px;
  width: 50px;
  background-color: #ccc;
  transition: all .5s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="text">Test</p>

<div id="square"></div>

By : Abhijeet


The problem is JavaScript will set color as rgb() even if you give a hex value(in this case #ccc) to backgroundColor. There are two possible way to solve this

  1. Check for rgb() instead of hex

var text = document.getElementById("text");

var square = document.getElementById("square");

text.onclick = function()
{
    if(this.style.color === "blue")
    {
        this.style.color = "black";
        console.log("it worked");
    } else
    {
        this.style.color = "blue";
    }
};

square.onclick = function()
{
    if(this.style.backgroundColor === "rgb(204, 204, 204)")
    {
        this.style.backgroundColor = "#fff";
    } else
    {
        this.style.backgroundColor = "#ccc";
    }
};
#text
{
    color: blue;
}

#square
{
    height: 50px;
    width: 50px;
    background-color: #ccc;
    transition: all .5s ease;
}
<p id="text">Test</p>

<div id="square"></div>

  1. Convert the obtained rgb() color from this.style.backgroundColor to hex and check with if

    Check here for that method RGB to Hex and Hex to RGB



This video can help you solving your question :)
By: admin