mirror of
https://github.com/zhigang1992/PerspectiveTransform.git
synced 2026-04-29 12:45:14 +08:00
add spec to compare algebraic solution and OpenCV
This commit is contained in:
@@ -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;
|
||||
|
||||
87
Example/Tests/OpenCV_Spec.swift
Normal file
87
Example/Tests/OpenCV_Spec.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user