Day 104 to 117 – Make my first iPhone app in 16 weeks (UICollectionView)

This is part of “Make my first iPhone app in 16 weeks” series.

  • Controller – UICollectionViewController (native view controller for UICollectionView)
  • View – UICollectionView
    • UICollectionView
    • UICollectionViewCell
    • UICollectionReusableView (Header/Footer/DecorationViews)
  • Content – UICollectionViewDataSource & UICollectionViewDelegate
  • Layout – objects that provide presentation logics for view objects
    • UICollectionViewLayout – This is a blank slate layout object. You have to register view objects, add UICollectionViewLayoutAttributes objects that describe the presentation logic of each view object
    • UICollectionViewLayoutAttributes – This provides the presentation logic for each view object.
    • UICollectionViewUpdateItem – The layout object receives instances of the UICollectionViewUpdateItem class whenever data items are inserted, deleted, or moved within the collection view. You never need to create instances of this class yourself.
  • Flow Layout (concrete subclass of Layout object)
    • Flow Layout – comes with two optional supplementary reusable views (header/footer)
    • UICollectionViewDelegateFlowLayout – Methods to work the flow layout

Configuration of views

  1. Registration views (If you registered the views in Storyboard, DO NOT register them programmatically)
    1. Register programmatically
    2. Storyboard
  2. Deque

Layout of views

  1. If you configured a cell without giving it a size, it will disappear. I configured a header view in Storyboard but didn’t give it a size through headerReferenceSize or collectionView:layout:referenceSizeForHeaderInSection: method, so dequeing the supplementary view kept throwing an error.

Day 97 to 103 – Make my first iPhone app in 16 weeks (variable storage types)

This is part of “Make my first iPhone app in 16 weeks” series.

Variable storage types

  1. __block – any variable that’s marked with this storage type will retain its value that was modified within a block beyond the enclosing scope of the block.
  2. __weak – weak pointer
  3. __strong – strong pointer
  4. const – variable that cannot be reassigned or modified
  5. extern – way to declare a global variable
  6. static – variable reachable only within its own implementation (.m file of the .h file where the static variable is declared in)

Day 96 – Make my first iPhone app in 16 weeks (Delegate)

This is part of “Make my first iPhone app in 16 weeks” series.

So I want to have my UIView to draw circles around four corners of the paper in the image. My view controller knows where the circles need to be drawn but my view does not have a clue! So iOS MVC explicitly prohibits View making a contact with Controller. What do I do?

The answer is “Delegate” I set the controller to be the delegate of the view.

In my view.h

@protocol CornerDetectionViewDelegate 
- (void)someMethod;
@end

@interface CornerDetectionView : UIView {
    id delegate;
}
@property (nonatomic, strong) id delegate;
@end

Then in view.m

@synthesize delegate;

Then in my controller.m

@interface EditImageViewController () 
@property (strong, nonatomic) IBOutlet CornerDetectionView *cornerDetectionView;
@end

@implementation EditImageViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Sets the controller as a delegate for CornerDetectionView
    self.cornerDetectionView.delegate = self;
}
- (void)someMethod
{
    // Implement this method
}
@end

Day 94 to 95 – Make my first iPhone app in 16 weeks (UIView)

This is part of “Make my first iPhone app in 16 weeks” series

There are two ways to draw something on screen:

  1. OpenGL (C API)
  2. Quartz (drawing kit), UIKit (display kit), or Core Animation (translation, transformation, rotation animations)

Regardless of which method you use, the drawing must happen within UIView or its subclasses.

The View Drawing Cycle

  1. drawRect: method is called on the view to display the contents. As the name of method implies, iOS passes a rectangle with the visible contents to the view. Override this method for custom views but NEVER call this method yourself.
    1. drawRect is called when the view appears initially
    2. Also it’s called automatically when the following events happen
      1. Moving or removing another view that was partially obscuring your view
      2. Making a previously hidden view visible again by setting its hidden property to NO
      3. Scrolling a view off of the screen and then back onto the screen
      4. Explicitly calling the setNeedsDisplay or setNeedsDisplayInRect: method of your view
  2. The view marks itself as updated and waits for new actions to arrive and trigger another update cycle
  3. If you want to manually change/update the contents of the view, call the setNeedsDisplay or setNeedsDisplayInRect:

Coordinate Systems

  1. User coordinate system – used when a user issues drawing commands
  2. View coordinate system – fixed coordinates relative to the view
  3. Hardware coordinate system – fixed coordinates on the physical device

Points vs Pixels

The purpose for using points system is to render relatively the same size output independent of the device. For example, one pixel line in retina display may correspond to one point which may map to 2 physical pixels (depends on the contentScaleFactor of the view). If you need a custom scale factor, adjust it in drawRect.

Each view has drawing context (CGContextRef) that describes stroke, color, fill and etc. To get the current drawing context, call UIGraphicsGetCurrentContext function.

Animating UIView

There are some built-in animations you can do with UIView without interacting directly with Core Graphics framework. Read documentation here.

Where as UIImage takes into account scale factor and appropriately displays the contents automatically, Quartz images do not account for scale factor. User should check the scale factor and adjust the image size accordingly.

Draw in Offscreen Bitmap

  1. Create a CGRect
  2. Set drawing context with UIGraphicsBeginImageContextWithOptions function
  3. Draw the image in that rect

Day 97 – Make my first iPhone app in 16 weeks (Strong vs Weak)

This is part of “Make my first iPhone app in 16 weeks” series.

I haven’t been blogging about the progress of my work for a couple weeks. It’s due part to my Christmas + New Years + My B-day + Wedding Anniversary + My hubby’s B-day break (Dec is a killer month for me personally) and the other part to getting too absorbed in the making of the app and forgetting to blog about it :) Ok. Enough with my excuses.

I actually have made quite a progress. The app now takes a photo, calculates the right matrix to un-skew the art work to its rectangular shape in the approximately correct proportion. I’m currently in the process of refactoring my code to make it more manageable.

Today, as I was thinking about the memory management issue (I keep getting memory warning from the device), I realized I didn’t fully understand the difference between different property attributes under ARC system.

  1. Strong – Any object that points “Strongly” to another object will increment the reference pointer by 1
  2. Retain (Same as Strong under ARC system. Use Strong for ARC to work)
  3. Weak – Any object that points “Weakly” to another object will simply point to it but will NOT increment the reference counter by 1
  4. Unsafe_unretained – Use with primitive data (BOOL, float, int …etc)
  5. Assign (Same as unsafe_unretained under ARC system. Use Unsafe_unretained for ARC to work)
  6. Copy – Retains a “Copy” of the object which does not reflect the changes applied by other external objects that have pointers to it.

I will probably make the most use out of strong and weak but I’m still not 100% sure about WHEN and WHERE and HOW to use either one. Then I found this amazing tutorial.

One of the limitations of ARC is that it only works with Obj-C objects. If you use Core Foundation objects, you are responsible for releasing those objects yourself. If you use Core Foundations objects interchangeably with objective-C objects, the following are a simple set of rules that will help convert from one to the other.

  1. __bridge (cast without transfer of ownership. Use when you want to use a type temporarily as if it is the other)
  2. __bridge_transfer (cast and gives ownership to ARC) – Use whenever CF…create/copy/retain… functions is used
    1. (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes ( ... )
    2. Alternative – wrap the code in CFBridgingRelease()
  3. __bridge_retained (cast and relieves ownership from ARC)
    1. CFStringRef s2 = (__bridge_retained CFStringRef)s1;
    2. Alternative – wrap the code in CFBridgingRetain()

Day 78 to 79 – Make my first iPhone app in 16 weeks (Perspective Transform)

This is part of “Make my first iPhone app in 16 weeks” series.

Tristimulus value (RGB color) breakdown

  1. Intensity (brightness) – scalar value
  2. Chromaticity (color) – 2-vector (Red and Green. Blue is thrown away by convention as Blue can be deduced by knowing Red and Green) – R / (R + G + B) + G / (R + G + B) + B / (R + G + B) = 1

Homography matrix – used in warping the image

White paper on how to  estimate the ratio of the paper used -here (Someone else’s implementation)

Different transform explained here

 

 

 

Day 74 to 77 – Make my first iPhone app in 16 weeks (Detecting Corners)

This is part of “Make my first iPhone app in 16 weeks” series.

Functions to find corners:

  1. cornerHarris – most effective edge detector
  2. goodFeaturesToTrack – only finds the most prominent corners using cornerMinEigenVal or cornerHarris
  3. cornerSubPix – refines corners into sub-pixel level
  4. cornerMinEigenVal & cornerEigenValsAndVecs – to make custom corner detector

Day 72 – Make my first iPhone app in 16 weeks (OpenCV Feature Detection)

This is part of “Make my first iPhone app in 16 weeks” series.

FEATURE DETECTION

Harris Corner

1. Use cornerHarris() function to detect corners

cornerHarris(grayscale source image, OutputArray dst, int blockSize, int ksize, double k, int borderType=BORDER_DEFAULT )
  1. src – Input single-channel 8-bit or floating-point image.
  2. dst – Image to store the Harris detector responses. It has the type CV_32FC1 and the same size as src .
  3. blockSize – Neighborhood size (see the details on cornerEigenValsAndVecs() ).
  4. ksize – Aperture parameter for the Sobel() operator.
  5. k – Harris detector free parameter. See the formula below.
  6. borderType – Pixel extrapolation method. See borderInterpolate()

2. Use normalize() and convertScaleAbs() functions to normalize it

normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
convertScaleAbs( dst_norm, dst_norm_scaled );

Shi-Tomasi corner detector

Use goodFeaturesToTrack() function to detect corners

/// Apply corner detection
  goodFeaturesToTrack( grayscale source image,
               destination corner image,
               maxCorners,
               qualityLevel,
               minDistance,
               Mat(),
               blockSize,
               useHarrisDetector,
               k );

Create your own corner detection

Use cornerEigenValsAndVecs() function to get Eigen Value and Eigen Vector for Harris Corner detection

cornerEigenValsAndVecs( src_gray, myHarris_dst, blockSize, apertureSize, BORDER_DEFAULT );

Use cornerMinEigenVal() function to get minimum Eigen Value for Shi-Tomasi Corner detection

 cornerMinEigenVal( src_gray, myShiTomasi_dst, blockSize, apertureSize, BORDER_DEFAULT );

Detecting corners location in subpixeles

Use cornerSubPix() function to get more precise corner location (more exact than integer pixel)

cornerSubPix( src_gray, corners, winSize, zeroZone, criteria );

Feature Detection

Use FeatureDetector class and its detect() method to find key features

int minHessian = 400;
SurfFeatureDetector detector( minHessian );
std::vector keypoints_1, keypoints_2;
detector.detect( img_1, keypoints_1 );
detector.detect( img_2, keypoints_2 );

Mat img_keypoints_1; Mat img_keypoints_2;
drawKeypoints( img_1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT );
drawKeypoints( img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT );

Feature Description

1. Use SurfDescriptorExtractor class and its method compute() to find the feature vector correspondent to the key points (assuming you have key points detected by SurfFeatureDetector class)

SurfDescriptorExtractor extractor;
Mat descriptors_1, descriptors_2;
extractor.compute( img_1, keypoints_1, descriptors_1 );
extractor.compute( img_2, keypoints_2, descriptors_2 );

2. Use BFMatcher class and its method match() to match the key descriptors

BFMatcher matcher(NORM_L2);
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );

3. Use drawMatches() to draw matches

Mat img_matches;
drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches );

Feature Matching with FLANN

1. Use SurfFeatureDetector class to detect keypoints

int minHessian = 400;
SurfFeatureDetector detector( minHessian );
std::vector keypoints_1, keypoints_2;
detector.detect( img_1, keypoints_1 );
detector.detect( img_2, keypoints_2 );

2. Compute descriptors

SurfDescriptorExtractor extractor;
Mat descriptors_1, descriptors_2;
extractor.compute( img_1, keypoints_1, descriptors_1 );
extractor.compute( img_2, keypoints_2, descriptors_2 );

3. Use FlannBasedMatcher class to match features

FlannBasedMatcher matcher;
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );

Homography

Use findHomography() function to find the transform between matched keypoints

Mat H = findHomography( obj, scene, CV_RANSAC );

Use perspectiveTransform() function to map the points

perspectiveTransform( obj_corners, scene_corners, H);

Day 71 – Make my first iPhone app in 16 weeks (OpenCV Moments)

This is part of “Make my first iPhone app in 16 weeks” series.

Moments

An image moment is a certain particular weighted average (moment) of the image pixels’ intensities, or a function of such moments, usually chosen to have some attractive property or interpretation. (from wikipedia.org)

1. Use moments() function to get moments

vector mu(contours.size() );
  for( int i = 0; i < contours.size(); i++ )
     { mu[i] = moments( contours[i], false ); }

2. Use moments() function to get moments

vector mc( contours.size() );
  for( int i = 0; i < contours.size(); i++ )
     { mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }

contourArea() function calculates the area of a shape
arcLength() function calculates the perimeter of a shape

Point Polygon Test

The function determines whether the point is inside a contour, outside, or lies on an edge (or coincides with a vertex). It returns positive (inside), negative (outside), or zero (on an edge) value, correspondingly. When measureDist=false , the return value is +1, -1, and 0, respectively. Otherwise, the return value is a signed distance between the point and the nearest contour edge.

1. Use pointPolygonTest() function to get distance to the contour

for( int j = 0; j < src.rows; j++ )
     { for( int i = 0; i < src.cols; i++ )
          { raw_dist.at(j,i) = pointPolygonTest( contours[0], Point2f(i,j), true ); }
     }