From 05ffa0d2ee1d2d0e304ef67233630692c2dc1b9f Mon Sep 17 00:00:00 2001 From: Andrew Schaaf Date: Sun, 5 Aug 2012 20:43:30 -0400 Subject: [PATCH] first public commit --- .gitignore | 2 + Cakefile | 3 + LICENSE.txt | 22 +++++++ README.md | 9 +++ redact-by-example.xcodeproj/project.pbxproj | 9 +++ redact-by-example/main.m | 64 +++++++++++++++++++- tests/package.json | 10 +++ tests/redacted.png | Bin 0 -> 107 bytes tests/result-expected.png | Bin 0 -> 114 bytes tests/subject.png | Bin 0 -> 114 bytes tests/tests.coffee | 58 ++++++++++++++++++ 11 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 Cakefile create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 tests/package.json create mode 100644 tests/redacted.png create mode 100644 tests/result-expected.png create mode 100644 tests/subject.png create mode 100644 tests/tests.coffee diff --git a/.gitignore b/.gitignore index 47042c2..4fa8ac6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .DS_Store xcuserdata +node_modules +tests/build diff --git a/Cakefile b/Cakefile new file mode 100644 index 0000000..835ca80 --- /dev/null +++ b/Cakefile @@ -0,0 +1,3 @@ + +task 'test', () -> + require('./tests/tests').main() diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1cdd24a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2012+ redact-by-example contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5f06f0 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +Suppose that a pixel has the color (R, G, B) if and only if it's been redacted. + +Suppose you want the same redactions that were applied to EXAMPLE to be applied to SRC, with the resulting image saved at DEST. + + redact-by-example EXAMPLE SRC DEST R G B + +Note: R, G, B `\in` {0, ..., 255} + +Note: DEST is always written as a PNG diff --git a/redact-by-example.xcodeproj/project.pbxproj b/redact-by-example.xcodeproj/project.pbxproj index edfc210..2e111b2 100644 --- a/redact-by-example.xcodeproj/project.pbxproj +++ b/redact-by-example.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0054FE2615CECA58000D8FE3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0054FE2515CECA58000D8FE3 /* Foundation.framework */; }; 0054FE2915CECA58000D8FE3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0054FE2815CECA58000D8FE3 /* main.m */; }; 0054FE2D15CECA58000D8FE3 /* redact_by_example.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0054FE2C15CECA58000D8FE3 /* redact_by_example.1 */; }; + 0054FE3915CED11B000D8FE3 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0054FE3815CED11B000D8FE3 /* AppKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -31,6 +32,7 @@ 0054FE2815CECA58000D8FE3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 0054FE2B15CECA58000D8FE3 /* redact-by-example-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "redact-by-example-Prefix.pch"; sourceTree = ""; }; 0054FE2C15CECA58000D8FE3 /* redact_by_example.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = redact_by_example.1; sourceTree = ""; }; + 0054FE3815CED11B000D8FE3 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/AppKit.framework; sourceTree = DEVELOPER_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -38,6 +40,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0054FE3915CED11B000D8FE3 /* AppKit.framework in Frameworks */, 0054FE2615CECA58000D8FE3 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -65,6 +68,7 @@ 0054FE2415CECA58000D8FE3 /* Frameworks */ = { isa = PBXGroup; children = ( + 0054FE3815CED11B000D8FE3 /* AppKit.framework */, 0054FE2515CECA58000D8FE3 /* Foundation.framework */, ); name = Frameworks; @@ -197,7 +201,9 @@ buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "redact-by-example/redact-by-example-Prefix.pch"; + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx10.7; }; name = Debug; }; @@ -206,7 +212,9 @@ buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "redact-by-example/redact-by-example-Prefix.pch"; + MACOSX_DEPLOYMENT_TARGET = 10.7; PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx10.7; }; name = Release; }; @@ -229,6 +237,7 @@ 0054FE3215CECA58000D8FE3 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/redact-by-example/main.m b/redact-by-example/main.m index 029b87e..4e16d7c 100644 --- a/redact-by-example/main.m +++ b/redact-by-example/main.m @@ -1,8 +1,68 @@ #import +#import + + +typedef struct _RGB { + unsigned char r, g, b; +} RGB; + + +void fatal_error(NSString *msg) { + NSLog(@"%@", msg); + exit(1); +} + +NSBitmapImageRep* loadImageRep(const char *path) { + NSString *pathString = [NSString stringWithCString:path encoding:NSUTF8StringEncoding]; + NSImage *img = [[NSImage alloc] initWithContentsOfFile:pathString]; + NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]; + if ([rep samplesPerPixel] != 3) { + fatal_error(@"only RGB supported so far"); + } + if ([rep bitsPerPixel] != 24) { + fatal_error(@"only 24-bit pixels supported so far"); + } + return rep; +} + int main(int argc, const char * argv[]) { - @autoreleasepool { - NSLog(@"Hello, World!"); + if (argc != (1 + 3 + 3)) { + fatal_error(@"invalid args"); + } + NSBitmapImageRep *exampleRep = loadImageRep(argv[1]); + NSBitmapImageRep *targetRep = loadImageRep(argv[2]); + NSString *destPath = [NSString stringWithCString:argv[3] encoding:NSUTF8StringEncoding]; + RGB redactionPixel; + redactionPixel.r = atoi(argv[4]); + redactionPixel.g = atoi(argv[5]); + redactionPixel.b = atoi(argv[6]); + + long w = (long)[exampleRep pixelsWide]; + long h = (long)[exampleRep pixelsHigh]; + long i; + if ((w != [exampleRep pixelsWide]) || (h != [exampleRep pixelsHigh])) { + fatal_error(@"Dimensions don't match."); + } + + RGB *examplePixels = (RGB *)[exampleRep bitmapData]; + RGB *targetPixels = (RGB *)[targetRep bitmapData]; + RGB pixel; + for (long y = 0; y < h; y++) { + for (long x = 0; x < w; x++) { + i = (y * w) + x; + pixel = examplePixels[i]; + if ( + (pixel.r == redactionPixel.r) && + (pixel.g == redactionPixel.g) && + (pixel.b == redactionPixel.b)) { + targetPixels[i] = redactionPixel; + } + } } + + NSData *png = [targetRep representationUsingType:NSPNGFileType properties:nil]; + [png writeToFile:destPath atomically:YES]; + return 0; } diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 0000000..63dada0 --- /dev/null +++ b/tests/package.json @@ -0,0 +1,10 @@ +{ + "name": "", + "version": "0.0.0", + "dependencies": { + "uiautomation-runner": "0.0.4", + "mkdirp": "~0.3.3", + "png-guts": ">=0.0.3", + "async": "~0.1.22" + } +} diff --git a/tests/redacted.png b/tests/redacted.png new file mode 100644 index 0000000000000000000000000000000000000000..c20517258f0e518232344867c07b3a82d5f49359 GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIab;lm4+PbBX6gV%6g*uV zLpZJ{Pq06r@bJKg!;K3MH7@+e;LOC_!J??p%*asJ$KmYw{%9dkErX}4pUXO@geCwT CVj(I3 literal 0 HcmV?d00001 diff --git a/tests/result-expected.png b/tests/result-expected.png new file mode 100644 index 0000000000000000000000000000000000000000..e8423f30653431900bd767292102f3c8ba43b14f GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2hGm!icqpk;}I0Jk_Tp1YtGyG!s|Np;m{#iMo zpo*u9V+eP zP{q^5F@)oKGDq~*2F08#ro&na=a>=|?2Szre9|)+KWYhBGcc%z@WmYd>X8Rj&*16m K=d#Wzp$PznoE%O7 literal 0 HcmV?d00001 diff --git a/tests/tests.coffee b/tests/tests.coffee new file mode 100644 index 0000000..d55d037 --- /dev/null +++ b/tests/tests.coffee @@ -0,0 +1,58 @@ +fs = require 'fs' +assert = require 'assert' +{exec} = require 'child_process' +{spawn_with_output} = require 'uiautomation-runner' +PNG_GUTS = require('png-guts').BIN_PATH +async = require 'async' +mkdirp = require 'mkdirp' + + +SETTINGS = { + xcode_project: "#{__dirname}/../redact-by-example.xcodeproj" + xcode_scheme: 'redact-by-example' + xcode_sdk: 'macosx10.7' + xcode_configuration: 'Release' + build_dir: "#{__dirname}/build" +} + + +main = () -> + async.series [ + ((c) -> mkdirp SETTINGS.build_dir, c) + xcodebuild + run_tests + ], (e) -> + throw e if e + console.log 'OK' + + +xcodebuild = (c) -> + spawn_with_output "xcodebuild", [ + '-project', SETTINGS.xcode_project, + '-scheme', SETTINGS.xcode_scheme, + '-sdk', SETTINGS.xcode_sdk, + '-configuration', SETTINGS.xcode_configuration, + 'build', + ('CONFIGURATION_BUILD_DIR=' + SETTINGS.build_dir) + ], {noisy:true}, c + + +run_tests = (c) -> + bin = "#{SETTINGS.build_dir}/redact-by-example" + example = "#{__dirname}/redacted.png" + src = "#{__dirname}/subject.png" + dest = "#{__dirname}/build/result.png" + dest_stripped = "#{__dirname}/build/result-stripped.png" + exec "'#{bin}' #{example} #{src} #{dest} 0 0 255", (e, out, err) -> + return c e if e + exec "cat '#{dest}' | '#{PNG_GUTS}' --strip-ancillary > '#{dest_stripped}'", (e, out, err) -> + return c e if e + exec "compare -metric RMSE '#{dest_stripped}' '#{__dirname}/result-expected.png' /dev/null", (e, out, err) -> + return c e if e + assert.equal err, "0 (0)\n" + c null + + +module.exports = {main} +if not module.parent + main()