add spec to compare algebraic solution and OpenCV

This commit is contained in:
Paul Zabelin
2018-02-27 02:41:51 -08:00
parent 884fa6c490
commit a5cb37639e
3 changed files with 128 additions and 4 deletions

View File

@@ -33,6 +33,7 @@
4BDEE374200049CD007214F1 /* with-overlay.svg in Resources */ = {isa = PBXBuildFile; fileRef = 4B2D2E891C7C2E620045FD11 /* with-overlay.svg */; };
4BDEE37620005B6D007214F1 /* resetAnchorPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDEE37520005B6D007214F1 /* resetAnchorPoint.swift */; };
4BDEE37720005B6D007214F1 /* resetAnchorPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDEE37520005B6D007214F1 /* resetAnchorPoint.swift */; };
4BDF6470204691090022C5F8 /* OpenCV_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDF646F204691090022C5F8 /* OpenCV_Spec.swift */; };
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
607FACD81AFB9204008FA782 /* PanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* PanViewController.swift */; };
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
@@ -79,6 +80,7 @@
4BA7B14D1C7D710D00933779 /* ReferenceImages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ReferenceImages; sourceTree = "<group>"; };
4BDEE37220004185007214F1 /* PhotoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoViewController.swift; sourceTree = "<group>"; };
4BDEE37520005B6D007214F1 /* resetAnchorPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = resetAnchorPoint.swift; sourceTree = "<group>"; };
4BDF646F204691090022C5F8 /* OpenCV_Spec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenCV_Spec.swift; sourceTree = "<group>"; };
4EA2244D76A3BF4D89D92FD5 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
54B21BF309BC78C5CEC00411 /* Pods-Example-Framework Unit Specs-Application Specs.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-Framework Unit Specs-Application Specs.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example-Framework Unit Specs-Application Specs/Pods-Example-Framework Unit Specs-Application Specs.debug.xcconfig"; sourceTree = "<group>"; };
607FACD01AFB9204008FA782 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -198,6 +200,7 @@
607FACE81AFB9204008FA782 /* Tests */ = {
isa = PBXGroup;
children = (
4BDF646F204691090022C5F8 /* OpenCV_Spec.swift */,
4BA7B14D1C7D710D00933779 /* ReferenceImages */,
4B2D2E861C7C2AD10045FD11 /* SnapshotSpec.swift */,
4B1B1C0C1C7A76C8003F8165 /* PerformanceTest.swift */,
@@ -611,6 +614,7 @@
4B2D2E871C7C2AD10045FD11 /* SnapshotSpec.swift in Sources */,
4BA3D17D1C771B560009B690 /* SpecHelper.swift in Sources */,
4B74DB47203BDA9C0030F41B /* PerformanceTest.swift in Sources */,
4BDF6470204691090022C5F8 /* OpenCV_Spec.swift in Sources */,
4BDEE37720005B6D007214F1 /* resetAnchorPoint.swift in Sources */,
4B4009952042575A00B7CE37 /* QuadrilateralCalc.swift in Sources */,
);
@@ -835,7 +839,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Tests/Application Specs-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
};
@@ -855,7 +858,6 @@
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Tests/Application Specs-Bridging-Header.h";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
};
name = Release;

View File

@@ -0,0 +1,87 @@
import Quick
import Nimble
struct Quadrilateral {
var upperLeft, upperRight, lowerRight, lowerLeft:CGPoint
}
class OpenCVWrapper: NSObject {
func canBeCalledFromSwift() -> Bool {
return true
}
static func transform(_ from:Quadrilateral, to:Quadrilateral) -> CATransform3D {
return CATransform3DIdentity
}
}
class OpenCV_Spec: QuickSpec {
override func spec() {
describe("opencv") {
context("OpenCVWrapper") {
it("can be called from swift") {
let wrapper = OpenCVWrapper()
expect(wrapper).notTo(beNil())
expect(wrapper.canBeCalledFromSwift()) == true
}
context("transform") {
it("should be identity for same") {
let start = Quadrilateral(upperLeft: CGPoint.zero,
upperRight: CGPoint(x: 10, y: 0),
lowerRight: CGPoint(x: 0, y: 10),
lowerLeft: CGPoint(x: 10, y: 10))
let desination = start
let transform = OpenCVWrapper.transform(start, to: desination)
expect(CATransform3DIsIdentity(transform)) == true
}
}
context("compare with algebraic solution") {
let points = [
CGPoint(x: 108.315837, y: 80.1687782),
CGPoint(x: 377.282671, y: 41.4352201),
CGPoint(x: 193.321418, y: 330.023027),
CGPoint(x: 459.781253, y: 251.836131),
]
let frame = CGRect(origin: CGPoint.zero,
size: CGSize(width: 20, height: 10))
func openCVTransform() -> CATransform3D {
let start = Quadrilateral(upperLeft: CGPoint(x: frame.minX, y: frame.minY),
upperRight: CGPoint(x: frame.maxX, y: frame.minY),
lowerRight: CGPoint(x: frame.maxX, y: frame.maxY),
lowerLeft: CGPoint(x: frame.minX, y: frame.maxY))
let destination = Quadrilateral(upperLeft: points[0],
upperRight: points[1],
lowerRight: points[3],
lowerLeft: points[2])
return OpenCVWrapper.transform(start, to: destination)
}
func algebraTransform() -> CATransform3D {
let destination = QuadrilateralCalc()
destination.topLeft = points[0]
destination.topRight = points[1]
destination.bottomLeft = points[2]
destination.bottomRight = points[3]
let start = QuadrilateralCalc()
start.topLeft = CGPoint(x: frame.minX, y: frame.minY)
start.topRight = CGPoint(x: frame.maxX, y: frame.minY)
start.bottomLeft = CGPoint(x: frame.minX, y: frame.maxY)
start.bottomRight = CGPoint(x: frame.maxX, y: frame.maxY)
return start.rectToQuad(rect: start.box(), quad: destination)
}
it("should be same") {
let openCV = openCVTransform()
let algebra = algebraTransform()
expect(openCV) algebra
}
}
}
}
}
}

View File

@@ -34,6 +34,41 @@ func beCloseTo(_ expectedValue: Vector3Type, within delta: Double = 0.00001) ->
}
}
extension CATransform3D {
func flattened() -> [CGFloat] {
return [
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44,
]
}
}
public func beCloseTo(_ expectedValue: CATransform3D, within delta: CGFloat = CGFloat(DefaultDelta)) -> Predicate<CATransform3D> {
let errorMessage = "be close to <\(stringify(expectedValue))> (each within \(stringify(delta)))"
return Predicate.simple(errorMessage) { actualExpression in
if let actual = try actualExpression.evaluate() {
if CATransform3DEqualToTransform(actual, expectedValue) {
return .matches
} else {
let expected = expectedValue.flattened()
for (index, m) in actual.flattened().enumerated() {
if fabs(m - expected[index]) > delta {
return .doesNotMatch
}
}
return .matches
}
}
return .doesNotMatch
}
}
public func (lhs: Expectation<CATransform3D>, rhs: CATransform3D) {
lhs.to(beCloseTo(rhs))
}
public func (lhs: Expectation<Matrix3x3Type>, rhs: Matrix3x3Type) {
lhs.to(beCloseTo(rhs))
}
@@ -110,7 +145,7 @@ extension GKRandomSource {
return CGPoint(x: nextDouble(), y: nextDouble())
}
func nextQuadrilateral() -> Quadrilateral {
return Quadrilateral(Array(0...3).map {_ in nextPoint()})
func nextQuadrilateral() -> PerspectiveTransform.Quadrilateral {
return PerspectiveTransform.Quadrilateral(Array(0...3).map {_ in nextPoint()})
}
}