├── .circleci └── config.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── diff.rs ├── image_creator.rs ├── lib.rs ├── main.rs ├── mkdir.rs └── rename.rs └── test └── images ├── after.png ├── before.png ├── diff.png ├── marked_after.png └── marked_before.png /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | working_directory: ~/lcs-image-diff-rs 3 | docker: 4 | - image: rustlang/rust:nightly 5 | environment: 6 | DISPLAY: ":99" 7 | 8 | version: 2 9 | jobs: 10 | build: 11 | <<: *defaults 12 | steps: 13 | - checkout 14 | - run: 15 | name: Test 16 | command: | 17 | rustup run nightly cargo test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | 4 | 5 | ## [0.1.7](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.6...0.1.7) (2019-01-06) 6 | 7 | ### Refactor 8 | 9 | Split crate to library and binary #4 10 | 11 | 12 | ## [0.1.6](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.5...0.1.6) (2018-03-15) 13 | 14 | ### Bug Fixes 15 | 16 | * Fixed a bug, panic occurred when difference width image input 17 | 18 | 19 | ## [0.1.5](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.4...0.1.5) (2018-02-25) 20 | 21 | ### Perf 22 | 23 | * Improve performance 24 | 25 | 26 | ## [0.1.4](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.3...0.1.4) (2018-02-23) 27 | 28 | ### Bug Fixes 29 | 30 | * Fix bug, when specify only filename permission required 31 | 32 | 33 | 34 | ## [0.1.3](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.2...0.1.3) (2018-02-23) 35 | 36 | ### Bug Fixes 37 | 38 | * Fix bug, panic occurred when dir is already exists 39 | 40 | 41 | ## [0.1.1](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.0...0.1.1) (2018-02-23) 42 | 43 | ### Bug Fixes 44 | 45 | * Fix marked range size was wrong. 46 | 47 | 48 | ## [0.1.2](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.1...0.1.2) (2018-02-23) 49 | 50 | ### Features 51 | 52 | * Modify default marked color 53 | 54 | 55 | ## [0.1.1](https://github.com/bokuweb/lcs-image-diff-rs/compare/0.1.0...0.1.1) (2018-02-23) 56 | 57 | ### Bug Fixes 58 | 59 | * Fix marked range size was wrong. 60 | 61 | ### Features 62 | 63 | * Modify default marked color 64 | 65 | 66 | 67 | ## 0.1.0 (2018-02-13) 68 | 69 | First release :tada: 70 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "adler32" 3 | version = "1.0.2" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "ansi_term" 8 | version = "0.10.2" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | 11 | [[package]] 12 | name = "arrayvec" 13 | version = "0.4.7" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "atty" 21 | version = "0.2.6" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 26 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 27 | ] 28 | 29 | [[package]] 30 | name = "base64" 31 | version = "0.9.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | dependencies = [ 34 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 36 | ] 37 | 38 | [[package]] 39 | name = "bitflags" 40 | version = "1.0.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | 43 | [[package]] 44 | name = "byteorder" 45 | version = "1.2.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | 48 | [[package]] 49 | name = "cfg-if" 50 | version = "0.1.2" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | 53 | [[package]] 54 | name = "clap" 55 | version = "2.30.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | dependencies = [ 58 | "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 65 | ] 66 | 67 | [[package]] 68 | name = "color_quant" 69 | version = "1.0.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | 72 | [[package]] 73 | name = "crossbeam-deque" 74 | version = "0.2.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | dependencies = [ 77 | "crossbeam-epoch 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 79 | ] 80 | 81 | [[package]] 82 | name = "crossbeam-epoch" 83 | version = "0.3.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | dependencies = [ 86 | "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 87 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 88 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 89 | "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 90 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 91 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 92 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 93 | ] 94 | 95 | [[package]] 96 | name = "crossbeam-utils" 97 | version = "0.2.2" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | dependencies = [ 100 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 101 | ] 102 | 103 | [[package]] 104 | name = "deflate" 105 | version = "0.7.17" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | dependencies = [ 108 | "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 110 | ] 111 | 112 | [[package]] 113 | name = "either" 114 | version = "1.4.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | 117 | [[package]] 118 | name = "fuchsia-zircon" 119 | version = "0.3.3" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | dependencies = [ 122 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 124 | ] 125 | 126 | [[package]] 127 | name = "fuchsia-zircon-sys" 128 | version = "0.3.3" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | 131 | [[package]] 132 | name = "futures" 133 | version = "0.1.18" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | 136 | [[package]] 137 | name = "futures-cpupool" 138 | version = "0.1.8" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | dependencies = [ 141 | "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", 142 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 143 | ] 144 | 145 | [[package]] 146 | name = "gif" 147 | version = "0.10.1" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | dependencies = [ 150 | "color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 152 | ] 153 | 154 | [[package]] 155 | name = "image" 156 | version = "0.20.1" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | dependencies = [ 159 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 160 | "gif 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 161 | "jpeg-decoder 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 164 | "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 165 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 167 | "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "tiff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 169 | ] 170 | 171 | [[package]] 172 | name = "inflate" 173 | version = "0.4.4" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | dependencies = [ 176 | "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 177 | ] 178 | 179 | [[package]] 180 | name = "jpeg-decoder" 181 | version = "0.1.14" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | dependencies = [ 184 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 185 | "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 186 | ] 187 | 188 | [[package]] 189 | name = "lazy_static" 190 | version = "0.2.11" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | 193 | [[package]] 194 | name = "lazy_static" 195 | version = "1.0.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | 198 | [[package]] 199 | name = "lcs-diff" 200 | version = "0.1.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | 203 | [[package]] 204 | name = "lcs-image-diff" 205 | version = "0.1.6" 206 | dependencies = [ 207 | "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 208 | "clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)", 209 | "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", 210 | "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 211 | "image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", 212 | "lcs-diff 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 213 | ] 214 | 215 | [[package]] 216 | name = "libc" 217 | version = "0.2.36" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | 220 | [[package]] 221 | name = "lzw" 222 | version = "0.10.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | 225 | [[package]] 226 | name = "memoffset" 227 | version = "0.2.1" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | 230 | [[package]] 231 | name = "nodrop" 232 | version = "0.1.12" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | 235 | [[package]] 236 | name = "num-derive" 237 | version = "0.2.3" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | dependencies = [ 240 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 241 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 242 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 243 | "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", 244 | ] 245 | 246 | [[package]] 247 | name = "num-integer" 248 | version = "0.1.39" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | dependencies = [ 251 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 252 | ] 253 | 254 | [[package]] 255 | name = "num-iter" 256 | version = "0.1.35" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | dependencies = [ 259 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 260 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 261 | ] 262 | 263 | [[package]] 264 | name = "num-rational" 265 | version = "0.2.1" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | dependencies = [ 268 | "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 269 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 270 | ] 271 | 272 | [[package]] 273 | name = "num-traits" 274 | version = "0.2.6" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | 277 | [[package]] 278 | name = "num_cpus" 279 | version = "1.8.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | dependencies = [ 282 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 283 | ] 284 | 285 | [[package]] 286 | name = "png" 287 | version = "0.12.0" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | dependencies = [ 290 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 291 | "deflate 0.7.17 (registry+https://github.com/rust-lang/crates.io-index)", 292 | "inflate 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 294 | ] 295 | 296 | [[package]] 297 | name = "proc-macro2" 298 | version = "0.4.24" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | dependencies = [ 301 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 302 | ] 303 | 304 | [[package]] 305 | name = "quote" 306 | version = "0.6.10" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | dependencies = [ 309 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 310 | ] 311 | 312 | [[package]] 313 | name = "rand" 314 | version = "0.4.2" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | dependencies = [ 317 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 318 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 319 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 320 | ] 321 | 322 | [[package]] 323 | name = "rayon" 324 | version = "1.0.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | dependencies = [ 327 | "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 328 | "rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 329 | ] 330 | 331 | [[package]] 332 | name = "rayon-core" 333 | version = "1.4.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | dependencies = [ 336 | "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 337 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 338 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 339 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 340 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 341 | ] 342 | 343 | [[package]] 344 | name = "redox_syscall" 345 | version = "0.1.37" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | 348 | [[package]] 349 | name = "redox_termios" 350 | version = "0.1.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | dependencies = [ 353 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 354 | ] 355 | 356 | [[package]] 357 | name = "safemem" 358 | version = "0.2.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | 361 | [[package]] 362 | name = "scoped_threadpool" 363 | version = "0.1.9" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | 366 | [[package]] 367 | name = "scopeguard" 368 | version = "0.3.3" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | 371 | [[package]] 372 | name = "strsim" 373 | version = "0.7.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | 376 | [[package]] 377 | name = "syn" 378 | version = "0.15.22" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | dependencies = [ 381 | "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 384 | ] 385 | 386 | [[package]] 387 | name = "termion" 388 | version = "1.5.1" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | dependencies = [ 391 | "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 392 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 393 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 394 | ] 395 | 396 | [[package]] 397 | name = "textwrap" 398 | version = "0.9.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | dependencies = [ 401 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 402 | ] 403 | 404 | [[package]] 405 | name = "tiff" 406 | version = "0.2.1" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | dependencies = [ 409 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 410 | "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 411 | "num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 412 | "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 413 | ] 414 | 415 | [[package]] 416 | name = "unicode-width" 417 | version = "0.1.4" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | 420 | [[package]] 421 | name = "unicode-xid" 422 | version = "0.1.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | 425 | [[package]] 426 | name = "vec_map" 427 | version = "0.8.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | 430 | [[package]] 431 | name = "winapi" 432 | version = "0.3.4" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | dependencies = [ 435 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 436 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 437 | ] 438 | 439 | [[package]] 440 | name = "winapi-i686-pc-windows-gnu" 441 | version = "0.4.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | 444 | [[package]] 445 | name = "winapi-x86_64-pc-windows-gnu" 446 | version = "0.4.0" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | 449 | [metadata] 450 | "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" 451 | "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" 452 | "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" 453 | "checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" 454 | "checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4" 455 | "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" 456 | "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" 457 | "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" 458 | "checksum clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c07b9257a00f3fc93b7f3c417fc15607ec7a56823bc2c37ec744e266387de5b" 459 | "checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d" 460 | "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" 461 | "checksum crossbeam-epoch 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "59796cc6cbbdc6bb319161349db0c3250ec73ec7fcb763a51065ec4e2e158552" 462 | "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" 463 | "checksum deflate 0.7.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4dddda59aaab719767ab11d3efd9a714e95b610c4445d4435765021e9d52dfb1" 464 | "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" 465 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 466 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 467 | "checksum futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0bab5b5e94f5c31fc764ba5dd9ad16568aae5d4825538c01d6bca680c9bf94a7" 468 | "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" 469 | "checksum gif 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4bca55ac1f213920ce3527ccd62386f1f15fa3f1714aeee1cf93f2c416903f" 470 | "checksum image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44665b4395d1844c96e7dc8ed5754782a1cdfd9ef458a80bbe45702681450504" 471 | "checksum inflate 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "84c683bde2d8413b8f1be3e459c30e4817672b6e7a31d9212b0323154e76eba7" 472 | "checksum jpeg-decoder 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0dfe27a6c0dabd772d0f9b9f8701c4ca12c4d1eebcadf2be1f6f70396f6a1434" 473 | "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" 474 | "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" 475 | "checksum lcs-diff 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c172ea7099cef89eb5a1a6e1f55d79a753823a0201d362f6ec5508028efcf4ed" 476 | "checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" 477 | "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" 478 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 479 | "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" 480 | "checksum num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8af1847c907c2f04d7bfd572fb25bbb4385c637fe5be163cf2f8c5d778fe1e7d" 481 | "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" 482 | "checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" 483 | "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" 484 | "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" 485 | "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" 486 | "checksum png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b" 487 | "checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" 488 | "checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" 489 | "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" 490 | "checksum rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "485541959c8ecc49865526fe6c4de9653dd6e60d829d6edf0be228167b60372d" 491 | "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" 492 | "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" 493 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 494 | "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" 495 | "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 496 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 497 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 498 | "checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7" 499 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 500 | "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" 501 | "checksum tiff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a2cc6c4fd13cb1cfd20abdb196e794ceccb29371855b7e7f575945f920a5b3c2" 502 | "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" 503 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 504 | "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" 505 | "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" 506 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 507 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 508 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lcs-image-diff" 3 | version = "0.1.6" 4 | repository = "https://github.com/bokuweb/lcs-image-diff-rs" 5 | license = "MIT" 6 | readme = "README.md" 7 | authors = ["bokuweb "] 8 | description = "Image diff tool with LCS algorithm" 9 | keywords = [ 10 | "diff", 11 | "image", 12 | "lcs", 13 | ] 14 | 15 | [lib] 16 | name = "lcs_image_diff" 17 | path = "src/lib.rs" 18 | 19 | [[bin]] 20 | name = "lcs-image-diff" 21 | path = "src/main.rs" 22 | required-features = ["binary"] 23 | 24 | [features] 25 | default = ["binary"] 26 | binary = ["futures", "futures-cpupool", "all_image_formats"] 27 | all_image_formats = [ 28 | "image/gif_codec", 29 | "image/jpeg", 30 | "image/ico", 31 | "image/png_codec", 32 | "image/pnm", 33 | "image/tga", 34 | "image/tiff", 35 | "image/webp", 36 | "image/bmp", 37 | "image/hdr", 38 | "image/dxt", 39 | "image/jpeg_rayon" 40 | ] 41 | 42 | [dependencies] 43 | base64 = "0.9.0" 44 | clap = "2.30.0" 45 | futures = { version = "0.1.18", optional = true } 46 | futures-cpupool = { version = "0.1.8", optional = true } 47 | image = { version = "0.20", default-features = false } 48 | lcs-diff = "0.1.1" 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 @bokuweb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lcs-image-diff 2 | Image diff library and tool with LCS algorithm. rust port of [murooka/go-diff-image](https://github.com/murooka/go-diff-image) 3 | 4 | [![](http://meritbadge.herokuapp.com/lcs-image-diff)](https://crates.io/crates/lcs-image-diff) 5 | [![CircleCI](https://circleci.com/gh/bokuweb/lcs-image-diff-rs/tree/master.svg?style=svg)](https://circleci.com/gh/bokuweb/lcs-image-diff-rs/tree/master) 6 | 7 | ## Requirements 8 | - latest Rust (recommend [rustup](https://www.rustup.rs/)) 9 | 10 | ## Library 11 | 12 | ### Usage 13 | 14 | ```toml 15 | # Cargo.toml 16 | [dependencies] 17 | image = "0.20" 18 | lcs-image-diff = { version = "0.1", default-features = false } 19 | ``` 20 | 21 | ```rust 22 | use lcs_image_diff::compare; 23 | 24 | let mut before = image::open("before.png")?; 25 | let mut after = image::open("after.png")?; 26 | 27 | let diff = compare(&mut before, &mut after, 100.0 / 256.0)?; 28 | 29 | before.save("marked_before.png")?; 30 | after.save("marked_after.png")?; 31 | diff.save("diff.png")?; 32 | ``` 33 | 34 | ## Binary 35 | 36 | ### Installation 37 | 38 | ``` bash 39 | cargo install lcs-image-diff 40 | ``` 41 | 42 | ### Usage 43 | 44 | ``` 45 | lcs-image-diff path/to/before.png path/to/after.png path/to/diff.png 46 | ``` 47 | 48 | ## Example 49 | 50 | | before.png | after.png | diff.png | 51 | | --------------- |---------------| -------------------- | 52 | | ![](https://github.com/bokuweb/lcs-image-diff-rs/blob/master/test/images/before.png?raw=true) | ![](https://github.com/bokuweb/lcs-image-diff-rs/blob/master/test/images/after.png?raw=true) |![](https://github.com/bokuweb/lcs-image-diff-rs/blob/master/test/images/diff.png?raw=true)| 53 | 54 | `lcs-image-diff` outputs marked before and after images too. 55 | 56 | | marked_before.png | marked_after.png | 57 | | --------------- |---------------| 58 | | ![](https://github.com/bokuweb/lcs-image-diff-rs/blob/master/test/images/marked_before.png?raw=true) | ![](https://github.com/bokuweb/lcs-image-diff-rs/blob/master/test/images/marked_after.png?raw=true) | 59 | 60 | ## LICENSE 61 | 62 | The MIT License (MIT) 63 | 64 | Copyright (c) 2018 @bokuweb 65 | 66 | Permission is hereby granted, free of charge, to any person obtaining a copy 67 | of this software and associated documentation files (the "Software"), to deal 68 | in the Software without restriction, including without limitation the rights 69 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 70 | copies of the Software, and to permit persons to whom the Software is 71 | furnished to do so, subject to the following conditions: 72 | 73 | The above copyright notice and this permission notice shall be included in all 74 | copies or substantial portions of the Software. 75 | 76 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 77 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 78 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 79 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 80 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 81 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 82 | SOFTWARE. 83 | -------------------------------------------------------------------------------- /src/diff.rs: -------------------------------------------------------------------------------- 1 | use super::base64::encode; 2 | use super::lcs_diff; 3 | 4 | #[derive(Debug)] 5 | pub struct CompareImage { 6 | dimensions: (u32, u32), 7 | pixels: Vec, 8 | } 9 | 10 | impl CompareImage { 11 | pub fn new(dimensions: (u32, u32), pixels: Vec) -> Self { 12 | CompareImage { dimensions, pixels } 13 | } 14 | 15 | pub fn create_encoded_rows(&self) -> Vec { 16 | self.pixels 17 | .chunks(self.dimensions.0 as usize * 4) 18 | .map(|chunk| encode(chunk)) 19 | .collect() 20 | } 21 | } 22 | 23 | pub fn diff(imga: CompareImage, imgb: CompareImage) -> Vec> { 24 | let imga = imga.create_encoded_rows(); 25 | let imgb = imgb.create_encoded_rows(); 26 | lcs_diff::diff(&imga, &imgb) 27 | } -------------------------------------------------------------------------------- /src/image_creator.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use super::image::*; 3 | use super::lcs_diff::*; 4 | use super::base64::decode; 5 | 6 | pub static BLACK: (u8, u8, u8) = (0, 0, 0); 7 | pub static RED: (u8, u8, u8) = (255, 119, 119); 8 | pub static GREEN: (u8, u8, u8) = (99, 195, 99); 9 | 10 | fn compute_range(r: &Vec) -> Vec<(usize, usize)> { 11 | let mut i = 0; 12 | let mut j = 0; 13 | let mut acc: usize; 14 | let mut y1: usize; 15 | let mut ranges: Vec<(usize, usize)> = Vec::new(); 16 | while i < r.len() { 17 | y1 = r[i]; 18 | acc = y1; 19 | i += 1; 20 | loop { 21 | if i >= r.len() { 22 | break; 23 | } 24 | let index = r[i]; 25 | if acc + 1 != index { 26 | break; 27 | } 28 | acc = index; 29 | i += 1; 30 | j += 1; 31 | } 32 | let y2 = y1 + j; 33 | j = 0; 34 | ranges.push((y1, y2)); 35 | } 36 | ranges 37 | } 38 | 39 | fn blend_diff_area(img: &mut G, ranges: Vec<(usize, usize)>, rgb: (u8, u8, u8), rate: f32) 40 | where 41 | G: GenericImage>, 42 | { 43 | for (y1, y2) in ranges { 44 | for y in y1..(y2 + 1) { 45 | for x in 0..img.dimensions().0 { 46 | let p = img.get_pixel(x, y as u32); 47 | let blended = blend(p, rgb, rate); 48 | img.put_pixel(x, y as u32, blended); 49 | } 50 | } 51 | } 52 | } 53 | 54 | fn blend(base: Rgba, rgb: (u8, u8, u8), rate: f32) -> Rgba { 55 | return Rgba { 56 | data: [ 57 | (base.data[0] as f32 * (1.0 - rate) + rgb.0 as f32 * (rate)) as u8, 58 | (base.data[1] as f32 * (1.0 - rate) + rgb.1 as f32 * (rate)) as u8, 59 | (base.data[2] as f32 * (1.0 - rate) + rgb.2 as f32 * (rate)) as u8, 60 | base.data[3], 61 | ], 62 | }; 63 | } 64 | 65 | fn put_diff_pixels( 66 | y: usize, 67 | img: &mut ImageBuffer, Vec>, 68 | row_width: u32, 69 | data: &String, 70 | rgb: (u8, u8, u8), 71 | rate: f32, 72 | ) -> Result<(), base64::DecodeError> { 73 | let row = decode(data)?; 74 | for x in 0..img.dimensions().0 { 75 | let index = x as usize * 4; 76 | let pixel: Rgba = if row_width > x { 77 | Rgba { 78 | data: [row[index], row[index + 1], row[index + 2], row[index + 3]], 79 | } 80 | } else { 81 | Rgba { data: [0, 0, 0, 0] } 82 | }; 83 | img.put_pixel(x as u32, y as u32, blend(pixel, rgb, rate)); 84 | } 85 | Ok(()) 86 | } 87 | 88 | pub fn mark_org_image( 89 | base: &mut DynamicImage, 90 | color: (u8, u8, u8), 91 | rate: f32, 92 | indexes: &Vec, 93 | ) { 94 | let range = compute_range(indexes); 95 | blend_diff_area(base, range, color, rate); 96 | } 97 | 98 | pub fn get_diff_image( 99 | before_width: u32, 100 | after_width: u32, 101 | result: &Vec>, 102 | rate: f32, 103 | ) -> Result { 104 | let height = result.len() as u32; 105 | let width = cmp::max(before_width, after_width); 106 | let mut img: ImageBuffer, Vec> = ImageBuffer::new(width, height); 107 | for (y, d) in result.iter().enumerate() { 108 | match d { 109 | &DiffResult::Added(ref a) => { 110 | put_diff_pixels(y, &mut img, after_width, &a.data, GREEN, rate)? 111 | } 112 | &DiffResult::Removed(ref r) => { 113 | put_diff_pixels(y, &mut img, before_width, &r.data, RED, rate)? 114 | } 115 | &DiffResult::Common(ref c) => { 116 | put_diff_pixels(y, &mut img, width, &c.data, BLACK, 0.0)? 117 | } 118 | } 119 | } 120 | Ok(ImageRgba8(img)) 121 | } 122 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate base64; 2 | extern crate image; 3 | extern crate lcs_diff; 4 | 5 | mod diff; 6 | mod image_creator; 7 | 8 | use diff::*; 9 | use image_creator::*; 10 | use image::*; 11 | pub use base64::DecodeError; 12 | 13 | /// Accepts two mutable references to `image::DynamicImage` and rate. 14 | /// Returns diff `image::DynamicImage` and marks removed and added 15 | /// parts on input images. 16 | /// 17 | /// # Examples 18 | /// 19 | /// ```no_run 20 | /// extern crate image; 21 | /// # use std::error::Error; 22 | /// # fn main() -> Result<(), Box> { 23 | /// use lcs_image_diff::compare; 24 | /// 25 | /// let mut before = image::open("before.png")?; 26 | /// let mut after = image::open("after.png")?; 27 | /// 28 | /// let diff = compare(&mut before, &mut after, 100.0 / 256.0)?; 29 | /// 30 | /// before.save("marked_before.png")?; 31 | /// after.save("marked_after.png")?; 32 | /// diff.save("diff.png")?; 33 | /// # Ok(()) 34 | /// # } 35 | /// ``` 36 | pub fn compare( 37 | before: &mut DynamicImage, 38 | after: &mut DynamicImage, 39 | rate: f32 40 | ) -> Result { 41 | let compare_before = CompareImage::new(before.dimensions(), before.raw_pixels()); 42 | let compare_after = CompareImage::new(after.dimensions(), after.raw_pixels()); 43 | let result = diff(compare_before, compare_after); 44 | 45 | let mut added: Vec = Vec::new(); 46 | let mut removed: Vec = Vec::new(); 47 | for d in result.iter() { 48 | match d { 49 | &lcs_diff::DiffResult::Added(ref a) => added.push(a.new_index.unwrap()), 50 | &lcs_diff::DiffResult::Removed(ref r) => removed.push(r.old_index.unwrap()), 51 | _ => (), 52 | } 53 | } 54 | 55 | mark_org_image(before, RED, rate, &removed); 56 | mark_org_image(after, GREEN, rate, &added); 57 | 58 | get_diff_image(before.dimensions().0, after.dimensions().0, &result, rate) 59 | } 60 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate futures_cpupool; 3 | extern crate image; 4 | extern crate lcs_image_diff; 5 | 6 | #[macro_use] 7 | extern crate clap; 8 | 9 | mod mkdir; 10 | mod rename; 11 | 12 | use std::path::Path; 13 | use image::*; 14 | use rename::*; 15 | use mkdir::*; 16 | use clap::{App, Arg}; 17 | use futures_cpupool::CpuPool; 18 | use futures::{future, Future}; 19 | use lcs_image_diff::compare; 20 | 21 | static RATE: f32 = 100.0 / 256.0; 22 | 23 | pub fn save_image(image: &DynamicImage, filename: &str) { 24 | let path = Path::new(filename).parent().unwrap(); 25 | let _result = mkdirp(path); 26 | image.save(filename).unwrap(); 27 | } 28 | 29 | fn main() { 30 | let app = App::new(crate_name!()) 31 | .version(crate_version!()) 32 | .author(crate_authors!()) 33 | .about(crate_description!()) 34 | .arg( 35 | Arg::with_name("before_image") 36 | .help("path to before image") 37 | .required(true), 38 | ) 39 | .arg( 40 | Arg::with_name("after_image") 41 | .help("path to after image") 42 | .required(true), 43 | ) 44 | .arg( 45 | Arg::with_name("diff_image") 46 | .help("path to diff image") 47 | .required(true), 48 | ); 49 | let matches = app.get_matches(); 50 | let before_image = matches.value_of("before_image").unwrap(); 51 | let after_image = matches.value_of("after_image").unwrap(); 52 | let diff_image = matches.value_of("diff_image").unwrap().to_owned(); 53 | let mut before = image::open(before_image).unwrap(); 54 | let mut after = image::open(after_image).unwrap(); 55 | 56 | let marked_before = add_prefix_to_file_name(&before_image, &"marked_"); 57 | let marked_after = add_prefix_to_file_name(&after_image, &"marked_"); 58 | 59 | let result = compare(&mut before, &mut after, RATE).unwrap(); 60 | 61 | { 62 | let thread_pool = CpuPool::new_num_cpus(); 63 | let before_thread = thread_pool.spawn_fn(move || -> Result<(), ()> { 64 | save_image(&before, &marked_before); 65 | Ok(()) 66 | }); 67 | let after_thread = thread_pool.spawn_fn(move || -> Result<(), ()> { 68 | save_image(&after, &marked_after); 69 | Ok(()) 70 | }); 71 | let result_thread = thread_pool.spawn_fn(move || -> Result<(), ()> { 72 | save_image(&result, &diff_image); 73 | Ok(()) 74 | }); 75 | future::join_all(vec![before_thread, after_thread, result_thread]) 76 | .wait() 77 | .unwrap(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/mkdir.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::Path; 3 | use std::fs::create_dir_all; 4 | 5 | pub fn mkdirp>(p: P) -> io::Result<()> { 6 | if let Err(e) = create_dir_all(p) { 7 | if e.kind() != io::ErrorKind::AlreadyExists { 8 | return Ok(()); 9 | } 10 | return Err(e); 11 | } 12 | Ok(()) 13 | } -------------------------------------------------------------------------------- /src/rename.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | pub fn add_prefix_to_file_name(file_name: &str, prefix: &str) -> String { 4 | let path = Path::new(file_name); 5 | let file_name = path.file_name().unwrap(); 6 | let dir = path.parent().unwrap(); 7 | if dir.to_str().unwrap() == "" { 8 | return format!("{}{}", prefix, file_name.to_str().unwrap()); 9 | } 10 | format!("{}/{}{}", 11 | dir.to_str().unwrap(), 12 | prefix, 13 | file_name.to_str().unwrap()) 14 | } 15 | 16 | #[test] 17 | fn shoud_add_prefix_with_dir() { 18 | let filename = add_prefix_to_file_name(&"foo/bar/filename.png", &"sample_"); 19 | assert_eq!(filename, "foo/bar/sample_filename.png"); 20 | } 21 | 22 | #[test] 23 | fn shoud_add_prefix_without_dir() { 24 | let filename = add_prefix_to_file_name(&"filename.png", &"sample_"); 25 | assert_eq!(filename, "sample_filename.png"); 26 | } -------------------------------------------------------------------------------- /test/images/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/lcs-image-diff-rs/d29feda97e6365e4f6d922c2773b3ca2a494b6a0/test/images/after.png -------------------------------------------------------------------------------- /test/images/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/lcs-image-diff-rs/d29feda97e6365e4f6d922c2773b3ca2a494b6a0/test/images/before.png -------------------------------------------------------------------------------- /test/images/diff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/lcs-image-diff-rs/d29feda97e6365e4f6d922c2773b3ca2a494b6a0/test/images/diff.png -------------------------------------------------------------------------------- /test/images/marked_after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/lcs-image-diff-rs/d29feda97e6365e4f6d922c2773b3ca2a494b6a0/test/images/marked_after.png -------------------------------------------------------------------------------- /test/images/marked_before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/lcs-image-diff-rs/d29feda97e6365e4f6d922c2773b3ca2a494b6a0/test/images/marked_before.png --------------------------------------------------------------------------------