├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── appveyor.yml ├── jpeg-archive ├── jpeg-compare.c ├── jpeg-hash.c ├── jpeg-recompress.c ├── src ├── edit.c ├── edit.h ├── hash.c ├── hash.h ├── iqa │ ├── CHANGELOG.txt │ ├── Doxyfile │ ├── Makefile │ ├── README.txt │ ├── doc │ │ ├── algorithms.h │ │ ├── build.h │ │ ├── footer.html │ │ ├── mainpage.h │ │ └── sample_code.h │ ├── include │ │ ├── convolve.h │ │ ├── decimate.h │ │ ├── iqa.h │ │ ├── iqa_os.h │ │ ├── math_utils.h │ │ └── ssim.h │ ├── iqa.sln │ ├── iqa.vcproj │ ├── resources │ │ ├── iqa_logo.bmp │ │ ├── iqa_logo.jpg │ │ ├── mse_eq.jpg │ │ └── psnr_eq.jpg │ ├── source │ │ ├── convolve.c │ │ ├── decimate.c │ │ ├── math_utils.c │ │ ├── ms_ssim.c │ │ ├── mse.c │ │ ├── psnr.c │ │ └── ssim.c │ └── test │ │ ├── Makefile │ │ ├── include │ │ ├── bmp.h │ │ ├── hptime.h │ │ ├── test_convolve.h │ │ ├── test_decimate.h │ │ ├── test_ms_ssim.h │ │ ├── test_mse.h │ │ ├── test_psnr.h │ │ └── test_ssim.h │ │ ├── octave │ │ ├── README.txt │ │ ├── ms_ssim.m │ │ ├── ssim_single.m │ │ ├── test_convolve.m │ │ ├── test_decimate.m │ │ ├── test_ms_ssim.m │ │ ├── test_mse.m │ │ ├── test_psnr.m │ │ └── test_ssim.m │ │ ├── resources │ │ ├── Courtright.bmp │ │ ├── Courtright_Noise.bmp │ │ ├── blur.bmp │ │ ├── contrast.bmp │ │ ├── einstein.bmp │ │ ├── flipvertical.bmp │ │ ├── impulse.bmp │ │ ├── jpg.bmp │ │ ├── meanshift.bmp │ │ └── skate_480x360.bmp │ │ ├── source │ │ ├── bmp.c │ │ ├── hptime.c │ │ ├── main.c │ │ ├── test_convolve.c │ │ ├── test_decimate.c │ │ ├── test_ms_ssim.c │ │ ├── test_mse.c │ │ ├── test_psnr.c │ │ └── test_ssim.c │ │ └── test.vcproj ├── smallfry.c ├── smallfry.h ├── test │ ├── assertion-macros.h │ └── describe.h ├── util.c └── util.h └── test ├── comparison.sh ├── test.c └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | src/*.o 2 | src/iqa/.svn 3 | src/iqa/build 4 | jpeg-recompress 5 | jpeg-compare 6 | jpeg-hash 7 | test-files 8 | test-output 9 | comparison 10 | comparison.png 11 | test/test.jpg 12 | test/test 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - clang 4 | - gcc 5 | before_install: 6 | - sudo apt-get update -qq 7 | - sudo apt-get install -qq build-essential autoconf nasm 8 | - git clone https://github.com/mozilla/mozjpeg.git 9 | - cd mozjpeg 10 | - git fetch origin refs/tags/v3.2:refs/tags/v3.2 11 | - git fetch --all 12 | - git checkout tags/v3.2 13 | - autoreconf -fiv 14 | - ./configure --with-jpeg8 15 | - make && sudo make install 16 | - sudo ln -s /opt/mozjpeg/lib64 /opt/mozjpeg/lib 17 | - cd .. 18 | script: 19 | - make && make test && cd test && ./test.sh 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | CFLAGS += -std=c99 -Wall -O3 3 | LDFLAGS += -lm 4 | MAKE ?= make 5 | PREFIX ?= /usr/local 6 | 7 | UNAME_S := $(shell uname -s) 8 | 9 | ifeq ($(UNAME_S),Linux) 10 | # Linux (e.g. Ubuntu) 11 | MOZJPEG_PREFIX ?= /opt/mozjpeg 12 | CFLAGS += -I$(MOZJPEG_PREFIX)/include 13 | 14 | ifneq ("$(wildcard $(MOZJPEG_PREFIX)/lib64/libjpeg.a)","") 15 | LIBJPEG = $(MOZJPEG_PREFIX)/lib64/libjpeg.a 16 | else 17 | LIBJPEG = $(MOZJPEG_PREFIX)/lib/libjpeg.a 18 | endif 19 | else ifeq ($(UNAME_S),Darwin) 20 | # Mac OS X 21 | MOZJPEG_PREFIX ?= /usr/local/opt/mozjpeg 22 | LIBJPEG = $(MOZJPEG_PREFIX)/lib/libjpeg.a 23 | CFLAGS += -I$(MOZJPEG_PREFIX)/include 24 | else ifeq ($(UNAME_S),FreeBSD) 25 | # FreeBSD 26 | LIBJPEG = $(PREFIX)/lib/mozjpeg/libjpeg.so 27 | CFLAGS += -I$(PREFIX)/include/mozjpeg 28 | else 29 | # Windows 30 | LIBJPEG = ../mozjpeg/libjpeg.a 31 | CFLAGS += -I../mozjpeg 32 | endif 33 | 34 | LIBIQA=src/iqa/build/release/libiqa.a 35 | 36 | all: jpeg-recompress jpeg-compare jpeg-hash 37 | 38 | $(LIBIQA): 39 | cd src/iqa; RELEASE=1 $(MAKE) 40 | 41 | jpeg-recompress: jpeg-recompress.c src/util.o src/edit.o src/smallfry.o $(LIBIQA) 42 | $(CC) $(CFLAGS) -o $@ $^ $(LIBJPEG) $(LDFLAGS) 43 | 44 | jpeg-compare: jpeg-compare.c src/util.o src/hash.o src/edit.o src/smallfry.o $(LIBIQA) 45 | $(CC) $(CFLAGS) -o $@ $^ $(LIBJPEG) $(LDFLAGS) 46 | 47 | jpeg-hash: jpeg-hash.c src/util.o src/hash.o 48 | $(CC) $(CFLAGS) -o $@ $^ $(LIBJPEG) $(LDFLAGS) 49 | 50 | %.o: %.c %.h 51 | $(CC) $(CFLAGS) -c -o $@ $< 52 | 53 | test: test/test.c src/util.o src/edit.o src/hash.o 54 | $(CC) $(CFLAGS) -o test/$@ $^ $(LIBJPEG) $(LDFLAGS) 55 | ./test/$@ 56 | 57 | install: all 58 | mkdir -p $(PREFIX)/bin 59 | cp jpeg-archive $(PREFIX)/bin/ 60 | cp jpeg-recompress $(PREFIX)/bin/ 61 | cp jpeg-compare $(PREFIX)/bin/ 62 | cp jpeg-hash $(PREFIX)/bin/ 63 | 64 | clean: 65 | rm -rf jpeg-recompress jpeg-compare jpeg-hash test/test src/*.o src/iqa/build 66 | 67 | .PHONY: test install clean 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JPEG Archive [![Build Status](http://img.shields.io/travis/danielgtaylor/jpeg-archive.svg?style=flat)](https://travis-ci.org/danielgtaylor/jpeg-archive) [![Build status](https://ci.appveyor.com/api/projects/status/1p7hrrq380xuqlyh?svg=true)](https://ci.appveyor.com/project/danielgtaylor/jpeg-archive) [![Version](http://img.shields.io/badge/version-2.2.0-blue.svg?style=flat)](https://github.com/danielgtaylor/jpeg-archive/releases) [![License](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](http://dgt.mit-license.org/) 2 | ============ 3 | Utilities for archiving photos for saving to long term storage or serving over the web. The goals are: 4 | 5 | * Use a common, well supported format (JPEG) 6 | * Minimize storage space and cost 7 | * Identify duplicates / similar photos 8 | 9 | Approach: 10 | 11 | * Command line utilities and scripts 12 | * Simple options and useful help 13 | * Good quality output via sane defaults 14 | 15 | Contributions to this project are very welcome. 16 | 17 | Download 18 | -------- 19 | You can download the latest source and binary releases from the [JPEG Archive releases page](https://github.com/danielgtaylor/jpeg-archive/releases). Windows binaries for the latest commit are available from the [Windows CI build server](https://ci.appveyor.com/project/danielgtaylor/jpeg-archive/build/artifacts). 20 | 21 | If you are looking for an easy way to run these utilities in parallel over many files to utilize all CPU cores, please also download [Ladon](https://github.com/danielgtaylor/ladon) or [GNU Parallel](https://www.gnu.org/software/parallel/). You can then use the `jpeg-archive` command below or use `ladon` directly. Example: 22 | 23 | ```bash 24 | # Re-compress JPEGs and replace the originals 25 | ladon "Photos/**/*.jpg" -- jpeg-recompress FULLPATH FULLPATH 26 | 27 | # Re-compress JPEGs into the new directory 'Comp' 28 | ladon -m Comp/RELDIR "Photos/**/*.jpg" -- jpeg-recompress FULLPATH Comp/RELPATH 29 | ``` 30 | 31 | Utilities 32 | --------- 33 | The following utilities are part of this project. All of them accept a `--help` parameter to see the available options. 34 | 35 | ### jpeg-archive 36 | Compress RAW and JPEG files in a folder utilizing all CPU cores. This is a simple shell script that uses the utilities below. It requires: 37 | 38 | * a POSIX-compatible shell such as Bash 39 | * [Ladon](https://github.com/danielgtaylor/ladon) or [GNU Parallel](https://www.gnu.org/software/parallel/) 40 | * [dcraw](http://www.cybercom.net/~dcoffin/dcraw/) 41 | * [exiftool](http://www.sno.phy.queensu.ca/~phil/exiftool/) 42 | * jpeg-recompress (part of this project) 43 | 44 | ```bash 45 | # Compress a folder of images 46 | cd path/to/photos 47 | jpeg-archive 48 | 49 | # Custom quality and metric 50 | jpeg-archive --quality medium --method smallfry 51 | ``` 52 | 53 | ### jpeg-recompress 54 | Compress JPEGs by re-encoding to the smallest JPEG quality while keeping _perceived_ visual quality the same and by making sure huffman tables are optimized. This is a __lossy__ operation, but the images are visually identical and it usually saves 30-70% of the size for JPEGs coming from a digital camera, particularly DSLRs. By default all EXIF/IPTC/XMP and color profile metadata is copied over, but this can be disabled to save more space if desired. 55 | 56 | There is no need for the input file to be a JPEG. In fact, you can use `jpeg-recompress` as a replacement for `cjpeg` by using PPM input and the `--ppm` option. 57 | 58 | The better the quality of the input image is, the better the output will be. 59 | 60 | Some basic photo-related editing options are available, such as removing fisheye lens distortion. 61 | 62 | #### Demo 63 | Below are two 100% crops of [Nikon's D3x Sample Image 2](http://static.nikonusa.com/D3X_gallery/index.html). The left shows the original image from the camera, while the others show the output of `jpeg-recompress` with the `medium` quality setting and various comparison methods. By default SSIM is used, which lowers the file size by **88%**. The recompression algorithm chooses a JPEG quality of 80. By comparison the `veryhigh` quality setting chooses a JPEG quality of 93 and saves 70% of the file size. 64 | 65 | ![JPEG recompression comparison](https://cloud.githubusercontent.com/assets/106826/3633843/5fde26b6-0eff-11e4-8c98-f18dbbf7b510.png) 66 | 67 | Why are they different sizes? The default quality settings are set to average out to similar visual quality over large data sets. They may differ on individual photos (like above) because each metric considers different parts of the image to be more or less important for compression. 68 | 69 | #### Image Comparison Metrics 70 | The following metrics are available when using `jpeg-recompress`. SSIM is the default. 71 | 72 | Name | Option | Description 73 | -------- | ------------- | ----------- 74 | MPE | `-m mpe` | Mean pixel error (as used by [imgmin](https://github.com/rflynn/imgmin)) 75 | SSIM | `-m ssim` | [Structural similarity](http://en.wikipedia.org/wiki/Structural_similarity) **DEFAULT** 76 | MS-SSIM* | `-m ms-ssim` | Multi-scale structural similarity (slow!) ([2008 paper](https://doi.org/10.1117/12.768060)) 77 | SmallFry | `-m smallfry` | Linear-weighted BBCQ-like ([original project](https://github.com/dwbuiten/smallfry), [2011 BBCQ paper](http://spie.org/Publications/Proceedings/Paper/10.1117/12.872231)) 78 | 79 | **Note**: The SmallFry algorithm may be [patented](http://www.jpegmini.com/main/technology) so use with caution. 80 | 81 | #### Subsampling 82 | The JPEG format allows for subsampling of the color channels to save space. For each 2x2 block of pixels per color channel (four pixels total) it can store four pixels (all of them), two pixels or a single pixel. By default, the JPEG encoder subsamples the non-luma channels to two pixels (often referred to as 4:2:0 subsampling). Most digital cameras do the same because of limitations in the human eye. This may lead to unintended behavior for specific use cases (see [#12](https://github.com/danielgtaylor/jpeg-archive/issues/12) for an example), so you can use `--subsample disable` to disable this subsampling. 83 | 84 | #### Example Commands 85 | 86 | ```bash 87 | # Default settings 88 | jpeg-recompress image.jpg compressed.jpg 89 | 90 | # High quality example settings 91 | jpeg-recompress --quality high --min 60 image.jpg compressed.jpg 92 | 93 | # Slow high quality settings (3-4x slower than above, slightly more accurate) 94 | jpeg-recompress --accurate --quality high --min 60 image.jpg compressed.jpg 95 | 96 | # Use SmallFry instead of SSIM 97 | jpeg-recompress --method smallfry image.jpg compressed.jpg 98 | 99 | # Use 4:4:4 sampling (disables subsampling). 100 | jpeg-recompress --subsample disable image.jpg compressed.jpg 101 | 102 | # Remove fisheye distortion (Tokina 10-17mm on APS-C @ 10mm) 103 | jpeg-recompress --defish 2.6 --zoom 1.2 image.jpg defished.jpg 104 | 105 | # Read from stdin and write to stdout with '-' as the filename 106 | jpeg-recompress - - compressed.jpg 107 | 108 | # Convert RAW to JPEG via PPM from stdin 109 | dcraw -w -q 3 -c IMG_1234.CR2 | jpeg-recompress --ppm - compressed.jpg 110 | 111 | # Disable progressive mode (not recommended) 112 | jpeg-recompress --no-progressive image.jpg compressed.jpg 113 | 114 | # Disable all output except for errors 115 | jpeg-recompress --quiet image.jpg compressed.jpg 116 | ``` 117 | 118 | ### jpeg-compare 119 | Compare two JPEG photos to judge how similar they are. The `fast` comparison method returns an integer from 0 to 99, where 0 is identical. PSNR, SSIM, and MS-SSIM return floats but require images to be the same dimensions. 120 | 121 | ```bash 122 | # Do a fast compare of two images 123 | jpeg-compare image1.jpg image2.jpg 124 | 125 | # Calculate PSNR 126 | jpeg-compare --method psnr image1.jpg image2.jpg 127 | 128 | # Calculate SSIM 129 | jpeg-compare --method ssim image1.jpg image2.jpg 130 | ``` 131 | 132 | ### jpeg-hash 133 | Create a hash of an image that can be used to compare it to other images quickly. 134 | 135 | ```bash 136 | jpeg-hash image.jpg 137 | ``` 138 | 139 | Building 140 | -------- 141 | ### Dependencies 142 | * [mozjpeg](https://github.com/mozilla/mozjpeg) 143 | 144 | #### Ubuntu 145 | Ubuntu users can install via `apt-get`: 146 | 147 | ```bash 148 | sudo apt-get install build-essential autoconf pkg-config nasm libtool 149 | git clone https://github.com/mozilla/mozjpeg.git 150 | cd mozjpeg 151 | autoreconf -fiv 152 | ./configure --with-jpeg8 153 | make 154 | sudo make install 155 | ``` 156 | 157 | #### Mac OS X 158 | Mac users can install it via [Homebrew](http://brew.sh/): 159 | 160 | ```bash 161 | brew install mozjpeg 162 | ``` 163 | 164 | #### FreeBSD 165 | 166 | ```bash 167 | pkg install mozjpeg 168 | git clone https://github.com/danielgtaylor/jpeg-archive.git 169 | cd jpeg-archive/ 170 | gmake 171 | sudo gmake install 172 | ``` 173 | 174 | #### Windows 175 | The `Makefile` should work with MinGW/Cygwin/etc and standard GCC. Patches welcome. 176 | 177 | To get everything you need to build, install these: 178 | 179 | * [CMake](https://cmake.org/download/) 180 | * [NASM](https://www.nasm.us/) 181 | * [MinGW](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/installer/mingw-w64-install.exe/download) (installed to e.g. `C:\mingw`) 182 | * [Github for Windows](https://windows.github.com/) 183 | 184 | Run Github for windows. In the settings, set **Git Bash** as the shell. Open Git Shell from the start menu. 185 | 186 | ```bash 187 | # Update PATH to include MinGW/NASM bin folder, location on your system may vary 188 | export PATH=/c/mingw/mingw32/bin:/c/Program\ Files \(x68\)/nasm:$PATH 189 | 190 | # Build mozjpeg or download https://www.dropbox.com/s/98jppfgds2xjblu/libjpeg.a 191 | git clone https://github.com/mozilla/mozjpeg.git 192 | cd mozjpeg 193 | cmake -G "MSYS Makefiles" -D CMAKE_C_COMPILER=gcc.exe -D CMAKE_MAKE_PROGRAM=mingw32-make.exe -D WITH_JPEG8=1 194 | mingw32-make 195 | cd .. 196 | 197 | # Build jpeg-archive 198 | git clone https://github.com/danielgtaylor/jpeg-archive 199 | cd jpeg-archive 200 | CC=gcc mingw32-make 201 | ``` 202 | 203 | JPEG-Archive should now be built. 204 | 205 | ### Compiling (Linux and Mac OS X) 206 | The `Makefile` should work as-is on Ubuntu and Mac OS X. Other platforms may need to set the location of `libjpeg.a` or make other tweaks. 207 | 208 | ```bash 209 | make 210 | ``` 211 | 212 | ### Installation 213 | Install the binaries into `/usr/local/bin`: 214 | 215 | ```bash 216 | sudo make install 217 | ``` 218 | 219 | Links / Alternatives 220 | -------------------- 221 | * https://github.com/rflynn/imgmin 222 | * https://news.ycombinator.com/item?id=803839 223 | 224 | License 225 | ------- 226 | * JPEG-Archive is copyright © 2015 Daniel G. Taylor 227 | * Image Quality Assessment (IQA) is copyright 2011, Tom Distler (http://tdistler.com) 228 | * SmallFry is copyright 2014, Derek Buitenhuis (https://github.com/dwbuiten) 229 | 230 | All are released under an MIT license. 231 | 232 | http://dgt.mit-license.org/ 233 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | environment: 3 | CC: gcc 4 | install: 5 | - cinst mingw 6 | - cd %APPVEYOR_BUILD_FOLDER%\.. 7 | - mkdir mozjpeg 8 | - cd mozjpeg 9 | - appveyor DownloadFile https://www.dropbox.com/s/b551b2a1qle5urf/mozjpeg.zip?dl=1 10 | - 7z e mozjpeg.zip -y > extract.log 11 | build_script: 12 | - cd %APPVEYOR_BUILD_FOLDER% 13 | - cd 14 | - set PATH=%PATH%;C:\tools\mingw64\bin 15 | - mingw32-make 16 | after_build: 17 | - 7z a jpeg-archive.zip %APPVEYOR_BUILD_FOLDER%\*.exe 18 | - appveyor PushArtifact jpeg-archive.zip 19 | test_script: 20 | - cd %APPVEYOR_BUILD_FOLDER% 21 | - set PATH=%PATH%;C:\tools\mingw64\bin 22 | - mingw32-make test 23 | -------------------------------------------------------------------------------- /jpeg-archive: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script requires ladon/parallel, dcraw, exiftool, and jpeg-recompress 4 | # Usage: 5 | # $ cd path/to/photos 6 | # $ jpeg-archive 7 | # Compressed JPEGs are now in ./Comp/* 8 | 9 | if [ "$1" = '-h' -o "$1" = '--help' ]; then 10 | echo 'JPEG-Archive - Compress RAW and JPEG images in the current folder' 11 | echo 'Usage: jpeg-archive [options]' 12 | echo '' 13 | echo 'Possible compression options:' 14 | echo "$(jpeg-recompress --help | tail -n+6)" 15 | exit 255 16 | elif [ "$1" = '-v' -o "$1" = '--version' ]; then 17 | jpeg-recompress --version 18 | exit 255 19 | fi 20 | 21 | LADON=$(command -v ladon) 22 | PARALLEL=$(command -v parallel) 23 | 24 | if [ -z "$LADON" -a -z "$PARALLEL" ]; then 25 | echo 'jpeg-archive: this script requires either Ladon or GNU Parallel' 26 | exit 1 27 | fi 28 | 29 | set -e 30 | 31 | # Cleanup old files 32 | rm -rf Comp /tmp/comp 33 | 34 | if [ -n "$LADON" ]; then 35 | echo 'Converting RAW files...' 36 | "$LADON" -m /tmp/comp/RELDIR '**/*.+(cr2|nef|dng)' -- "dcraw -w -q 3 -c RELPATH | jpeg-recompress ${@:--q high} --ppm - /tmp/comp/RELDIR/BASENAME.jpg" 37 | 38 | echo 'Copying EXIF data...' 39 | "$LADON" '**/*.+(cr2|nef|dng)' -- exiftool -overwrite_original -TagsFromFile RELPATH -all:all /tmp/comp/RELDIR/BASENAME.jpg 40 | 41 | echo 'Recompressing JPEG files' 42 | "$LADON" -m /tmp/comp/RELDIR '**/*.jpg' -- jpeg-recompress ${@:--q high} RELPATH /tmp/comp/RELPATH 43 | elif [ -n "$PARALLEL" ]; then 44 | echo 'Converting RAW files...' 45 | find . -name '*.cr2' -o -name '*.nef' -o -name '*.dng' | \ 46 | "$PARALLEL" --no-notice "mkdir -p /tmp/comp/{//}; dcraw -w -q 3 -c {} | jpeg-recompress ${@:--q high} --ppm - /tmp/comp/{.}.jpg" 47 | 48 | echo 'Copying EXIF data...' 49 | find . -name '*.cr2' -o -name '*.nef' -o -name '*.dng' | \ 50 | "$PARALLEL" --no-notice "exiftool -overwrite_original -TagsFromFile {} -all:all /tmp/comp/{.}.jpg" 51 | 52 | echo 'Recompressing JPEG files' 53 | find . -name '*.jpg' | \ 54 | "$PARALLEL" --no-notice "mkdir -p /tmp/comp/{//}; jpeg-recompress ${@:--q high} {} /tmp/comp/{}" 55 | fi 56 | 57 | mv /tmp/comp Comp 58 | 59 | echo 'Done! Compressed files are in the Comp folder.' 60 | -------------------------------------------------------------------------------- /jpeg-compare.c: -------------------------------------------------------------------------------- 1 | /* 2 | Compare two JPEG images. Several methods are available. PSNR, SSIM, 3 | and MS_SSIM require the images to be exactly the same size, while 4 | FAST works on any sized image. 5 | 6 | FAST compares two images and returns the difference on a 7 | scale of 0 - 99, where 0 would mean the images are identical. The 8 | comparison should be immune to exposure, saturation, white balance, 9 | scaling, minor crops and similar modifications. 10 | 11 | If the difference is 10 or less than the images are very likely 12 | different versions of the same image (e.g. a thumbnail or black 13 | and white edit) or just slightly different images. It is possible 14 | to get false positives, in which case a slower PSNR or SSIM 15 | comparison will help. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "src/edit.h" 25 | #include "src/hash.h" 26 | #include "src/iqa/include/iqa.h" 27 | #include "src/smallfry.h" 28 | #include "src/util.h" 29 | 30 | // Comparison method 31 | enum METHOD { 32 | UNKNOWN, 33 | FAST, 34 | PSNR, 35 | SSIM, 36 | MS_SSIM, 37 | SMALLFRY 38 | }; 39 | 40 | int method = FAST; 41 | 42 | /* Long command line options. */ 43 | enum longopts { 44 | OPT_SHORT = 1000 45 | }; 46 | 47 | int printPrefix = 1; 48 | 49 | // Hash size when method is FAST 50 | int size = 16; 51 | 52 | // Use PPM input? 53 | enum filetype inputFiletype1 = FILETYPE_AUTO; 54 | enum filetype inputFiletype2 = FILETYPE_AUTO; 55 | 56 | static enum METHOD parseMethod(const char *s) { 57 | if (!strcmp("fast", s)) 58 | return FAST; 59 | if (!strcmp("psnr", s)) 60 | return PSNR; 61 | if (!strcmp("ssim", s)) 62 | return SSIM; 63 | if (!strcmp("ms-ssim", s)) 64 | return MS_SSIM; 65 | if (!strcmp("smallfry", s)) 66 | return SMALLFRY; 67 | return UNKNOWN; 68 | } 69 | 70 | static enum filetype parseInputFiletype(const char *s) { 71 | if (!strcmp("auto", s)) 72 | return FILETYPE_AUTO; 73 | if (!strcmp("jpeg", s)) 74 | return FILETYPE_JPEG; 75 | if (!strcmp("ppm", s)) 76 | return FILETYPE_PPM; 77 | return FILETYPE_UNKNOWN; 78 | } 79 | 80 | int compareFastFromBuffer(unsigned char *imageBuf1, long bufSize1, unsigned char *imageBuf2, long bufSize2) { 81 | unsigned char *hash1, *hash2; 82 | 83 | // Generate hashes 84 | if (jpegHashFromBuffer(imageBuf1, bufSize1, &hash1, size)) { 85 | error("error hashing image 1!"); 86 | return 1; 87 | } 88 | 89 | if (jpegHashFromBuffer(imageBuf2, bufSize2, &hash2, size)) { 90 | error("error hashing image 2!"); 91 | return 1; 92 | } 93 | 94 | // Compare and print out hamming distance 95 | printf("%u\n", hammingDist(hash1, hash2, size * size) * 100 / (size * size)); 96 | 97 | // Cleanup 98 | free(hash1); 99 | free(hash2); 100 | 101 | return 0; 102 | } 103 | 104 | int compareFromBuffer(unsigned char *imageBuf1, long bufSize1, unsigned char *imageBuf2, long bufSize2) { 105 | unsigned char *image1, *image2, *image1Gray = NULL, *image2Gray = NULL; 106 | int width1, width2, height1, height2; 107 | int format, components; 108 | float diff; 109 | 110 | // Set requested pixel format 111 | switch (method) { 112 | case PSNR: 113 | format = JCS_RGB; 114 | components = 3; 115 | break; 116 | case SSIM: case MS_SSIM: default: 117 | format = JCS_GRAYSCALE; 118 | components = 1; 119 | break; 120 | } 121 | 122 | // Decode files 123 | if (!decodeFileFromBuffer(imageBuf1, bufSize1, &image1, inputFiletype1, &width1, &height1, format)) { 124 | error("invalid input reference file"); 125 | return 1; 126 | } 127 | 128 | if (1 == components && FILETYPE_PPM == inputFiletype1) { 129 | grayscale(image1, &image1Gray, width1, height1); 130 | free(image1); 131 | image1 = image1Gray; 132 | } 133 | 134 | if (!decodeFileFromBuffer(imageBuf2, bufSize2, &image2, inputFiletype2, &width2, &height2, format)) { 135 | error("invalid input query file"); 136 | return 1; 137 | } 138 | 139 | if (1 == components && FILETYPE_PPM == inputFiletype2) { 140 | grayscale(image2, &image2Gray, width2, height2); 141 | free(image2); 142 | image2 = image2Gray; 143 | } 144 | 145 | // Ensure width/height are equal 146 | if (width1 != width2 || height1 != height2) { 147 | error("images must be identical sizes for selected method!"); 148 | return 1; 149 | } 150 | 151 | // Calculate and print comparison 152 | switch (method) { 153 | case PSNR: 154 | diff = iqa_psnr(image1, image2, width1, height1, width1 * components); 155 | if (printPrefix) 156 | printf("PSNR: "); 157 | printf("%f\n", diff); 158 | break; 159 | case SMALLFRY: 160 | diff = smallfry_metric(image1, image2, width1, height1); 161 | if (printPrefix) 162 | printf("SMALLFRY: "); 163 | printf("%f\n", diff); 164 | break; 165 | case MS_SSIM: 166 | diff = iqa_ms_ssim(image1, image2, width1, height1, width1 * components, 0); 167 | if (printPrefix) 168 | printf("MS-SSIM: "); 169 | printf("%f\n", diff); 170 | break; 171 | case SSIM: default: 172 | diff = iqa_ssim(image1, image2, width1, height1, width1 * components, 0, 0); 173 | if (printPrefix) 174 | printf("SSIM: "); 175 | printf("%f\n", diff); 176 | break; 177 | } 178 | 179 | // Cleanup 180 | free(image1); 181 | free(image2); 182 | 183 | return 0; 184 | } 185 | 186 | void usage(void) { 187 | printf("usage: %s [options] image1.jpg image2.jpg\n\n", progname); 188 | printf("options:\n\n"); 189 | printf(" -V, --version output program version\n"); 190 | printf(" -h, --help output program help\n"); 191 | printf(" -s, --size [arg] set fast comparison image hash size\n"); 192 | printf(" -m, --method [arg] set comparison method to one of 'fast', 'psnr', 'ssim', or 'ms-ssim' [fast]\n"); 193 | printf(" -r, --ppm parse first input as PPM instead of JPEG\n"); 194 | printf(" -T, --input-filetype [arg] set first input file type to one of 'auto', 'jpeg', 'ppm' [auto]\n"); 195 | printf(" -U, --second-filetype [arg] set second input file type to one of 'auto', 'jpeg', 'ppm' [auto]\n"); 196 | printf(" --short do not prefix output with the name of the used method\n"); 197 | } 198 | 199 | int main (int argc, char **argv) { 200 | const char *optstring = "VhS:m:rT:U:"; 201 | static const struct option opts[] = { 202 | { "version", no_argument, 0, 'V' }, 203 | { "help", no_argument, 0, 'h' }, 204 | { "size", required_argument, 0, 'S' }, 205 | { "method", required_argument, 0, 'm' }, 206 | { "ppm", no_argument, 0, 'r' }, 207 | { "input-filetype", required_argument, 0, 'T' }, 208 | { "second-filetype", required_argument, 0, 'U' }, 209 | { "short", no_argument, 0, OPT_SHORT }, 210 | { 0, 0, 0, 0 } 211 | }; 212 | int opt, longind = 0; 213 | 214 | progname = "jpeg-compare"; 215 | 216 | while ((opt = getopt_long(argc, argv, optstring, opts, &longind)) != -1) { 217 | switch (opt) { 218 | case 'V': 219 | version(); 220 | return 0; 221 | case 'h': 222 | usage(); 223 | return 0; 224 | case 's': 225 | size = atoi(optarg); 226 | break; 227 | case 'm': 228 | method = parseMethod(optarg); 229 | break; 230 | case 'r': 231 | if (inputFiletype1 != FILETYPE_AUTO) { 232 | error("multiple file types specified for input file 1"); 233 | return 1; 234 | } 235 | inputFiletype1 = FILETYPE_PPM; 236 | break; 237 | case 'T': 238 | if (inputFiletype1 != FILETYPE_AUTO) { 239 | error("multiple file types specified for input file 1"); 240 | return 1; 241 | } 242 | inputFiletype1 = parseInputFiletype(optarg); 243 | break; 244 | case 'U': 245 | if (inputFiletype2 != FILETYPE_AUTO) { 246 | error("multiple file types specified for input file 2"); 247 | return 1; 248 | } 249 | inputFiletype2 = parseInputFiletype(optarg); 250 | break; 251 | case OPT_SHORT: 252 | printPrefix = 0; 253 | break; 254 | }; 255 | } 256 | 257 | if (argc - optind != 2) { 258 | usage(); 259 | return 255; 260 | } 261 | 262 | // Read the images 263 | unsigned char *imageBuf1, *imageBuf2; 264 | long bufSize1, bufSize2; 265 | 266 | char *fileName1 = argv[optind]; 267 | char *fileName2 = argv[optind + 1]; 268 | 269 | bufSize1 = readFile(fileName1, (void **)&imageBuf1); 270 | if (!bufSize1) { 271 | error("failed to read file: %s", fileName1); 272 | return 1; 273 | } 274 | 275 | bufSize2 = readFile(fileName2, (void **)&imageBuf2); 276 | if (!bufSize2) { 277 | error("failed to read file: %s", fileName2); 278 | return 1; 279 | } 280 | 281 | /* Detect input file types. */ 282 | if (inputFiletype1 == FILETYPE_AUTO) 283 | inputFiletype1 = detectFiletypeFromBuffer(imageBuf1, bufSize1); 284 | if (inputFiletype2 == FILETYPE_AUTO) 285 | inputFiletype2 = detectFiletypeFromBuffer(imageBuf2, bufSize2); 286 | 287 | // Calculate and print output 288 | switch (method) { 289 | case FAST: 290 | if (inputFiletype1 != FILETYPE_JPEG || inputFiletype2 != FILETYPE_JPEG) { 291 | error("fast comparison only works with JPEG files!"); 292 | return 255; 293 | } 294 | return compareFastFromBuffer(imageBuf1, bufSize1, imageBuf2, bufSize2); 295 | case PSNR: 296 | case SSIM: 297 | case MS_SSIM: 298 | case SMALLFRY: 299 | return compareFromBuffer(imageBuf1, bufSize1, imageBuf2, bufSize2); 300 | default: 301 | error("unknown comparison method!"); 302 | return 255; 303 | } 304 | 305 | // Cleanup resources 306 | free(imageBuf1); 307 | free(imageBuf2); 308 | } 309 | -------------------------------------------------------------------------------- /jpeg-hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | Hash a single JPEG file. The hash is based on tracking gradients 3 | between pixels in the image. The larger the hash size, the less 4 | likely you are to get collisions, but the more time it takes to 5 | calculate. 6 | */ 7 | #include 8 | #include 9 | #include 10 | 11 | #include "src/hash.h" 12 | #include "src/util.h" 13 | 14 | int size = 16; 15 | 16 | void usage(void) { 17 | printf("usage: %s [options] image.jpg\n\n", progname); 18 | printf("options:\n\n"); 19 | printf(" -V, --version output program version\n"); 20 | printf(" -h, --help output program help\n"); 21 | printf(" -s, --size [arg] set fast comparison image hash size\n"); 22 | } 23 | 24 | int main (int argc, char **argv) { 25 | unsigned char *hash; 26 | 27 | const char *optstring = "Vhs:"; 28 | static const struct option opts[] = { 29 | { "version", no_argument, 0, 'V' }, 30 | { "help", no_argument, 0, 'h' }, 31 | { "size", required_argument, 0, 's' }, 32 | { 0, 0, 0, 0 } 33 | }; 34 | int opt, longind = 0; 35 | 36 | progname = "jpeg-hash"; 37 | 38 | while ((opt = getopt_long(argc, argv, optstring, opts, &longind)) != -1) { 39 | switch (opt) { 40 | case 'V': 41 | version(); 42 | return 0; 43 | case 'h': 44 | usage(); 45 | return 0; 46 | case 's': 47 | size = atoi(optarg); 48 | break; 49 | }; 50 | } 51 | 52 | if (argc - optind != 1) { 53 | usage(); 54 | return 255; 55 | } 56 | 57 | // Generate the image hash 58 | if (jpegHash(argv[optind], &hash, size)) { 59 | error("error hashing image!"); 60 | return 1; 61 | } 62 | 63 | // Print out the hash a string of 1s and 0s 64 | for (int x = 0; x < size * size; x++) { 65 | printf("%c", hash[x] ? '1' : '0'); 66 | } 67 | printf("\n"); 68 | 69 | // Cleanup 70 | free(hash); 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /src/edit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | float clamp(float low, float value, float high) { 5 | return (value < low) ? low : ((value > high) ? high : value); 6 | } 7 | 8 | int interpolate(const unsigned char *image, int width, int components, float x, float y, int offset) { 9 | int stride = width * components; 10 | float px = x - floor(x); 11 | float py = y - floor(y); 12 | int x1 = floor(x); 13 | int x2 = ceil(x); 14 | int y1 = floor(y); 15 | int y2 = ceil(y); 16 | 17 | float top = (float) image[y1 * stride + x1 * components + offset] * (1.0 - px) + 18 | (float) image[y1 * stride + x2 * components + offset] * px; 19 | float bot = (float) image[y2 * stride + x1 * components + offset] * (1.0 - px) + 20 | (float) image[y2 * stride + x2 * components + offset] * px; 21 | 22 | return (top * (1.0 - py)) + (bot * py); 23 | } 24 | 25 | float meanPixelError(const unsigned char *original, const unsigned char *compressed, int width, int height, int components) { 26 | float pme = 0.0; 27 | 28 | for (int y = 0; y < height; y++) { 29 | for (int x = 0; x < width; x++) { 30 | for (int z = 0; z < components; z++) { 31 | int offset = y * width * components + x * components + z; 32 | pme += abs(original[offset] - compressed[offset]); 33 | } 34 | } 35 | } 36 | 37 | return pme / (width * height * components); 38 | } 39 | 40 | void defish(const unsigned char *input, unsigned char *output, int width, int height, int components, float strength, float zoom) { 41 | const int cx = width / 2; 42 | const int cy = height / 2; 43 | const float len = sqrt(width * width + height * height); 44 | 45 | for (int y = 0; y < height; y++) { 46 | for (int x = 0; x < width; x++) { 47 | float dx = (cx - x) * zoom; 48 | float dy = (cy - y) * zoom; 49 | float r = sqrt(dx * dx + dy * dy) / len * strength; 50 | float theta = 1.0; 51 | 52 | if (r != 0.0) { 53 | theta = atan(r) / r; 54 | } 55 | 56 | dx = clamp(0.0, (float) width / 2.0 - theta * dx, width); 57 | dy = clamp(0.0, (float) height / 2.0 - theta * dy, height); 58 | 59 | for (int z = 0; z < components; z++) { 60 | output[y * width * components + x * components + z] = interpolate(input, width, components, dx, dy, z); 61 | } 62 | } 63 | } 64 | } 65 | 66 | long grayscale(const unsigned char *input, unsigned char **output, int width, int height) { 67 | int stride = width * 3; 68 | 69 | *output = malloc(width * height); 70 | 71 | for (int y = 0; y < height; y++) { 72 | for (int x = 0; x < width; x++) { 73 | // Y = 0.299R + 0.587G + 0.114B 74 | (*output)[y * width + x] = input[y * stride + x * 3] * 0.299 + 75 | input[y * stride + x * 3 + 1] * 0.587 + 76 | input[y * stride + x * 3 + 2] * 0.114 + 0.5; 77 | } 78 | } 79 | 80 | return width * height; 81 | } 82 | -------------------------------------------------------------------------------- /src/edit.h: -------------------------------------------------------------------------------- 1 | /* 2 | Image editing functions 3 | */ 4 | #ifndef EDIT_H 5 | #define EDIT_H 6 | 7 | /* 8 | Clamp a value between low and high. 9 | */ 10 | float clamp(float low, float value, float high); 11 | 12 | /* 13 | Bilinear interpolation 14 | */ 15 | int interpolate(const unsigned char *image, int width, int components, float x, float y, int offset); 16 | 17 | /* 18 | Get mean error per pixel rate. 19 | */ 20 | float meanPixelError(const unsigned char *original, const unsigned char *compressed, int width, int height, int components); 21 | 22 | /* 23 | Remove fisheye distortion from an image. The amount of distortion is 24 | controlled by strength, while zoom controls where the image gets 25 | cropped. For example, the Tokina 10-17mm ATX fisheye on a Canon APS-C 26 | body set to 10mm looks good with strength=2.6 and zoom=1.2. 27 | */ 28 | void defish(const unsigned char *input, unsigned char *output, int width, int height, int components, float strength, float zoom); 29 | 30 | /* 31 | Convert an RGB image to grayscale. Assumes 8-bit color components, 32 | 3 color components and a row stride of width * 3. 33 | */ 34 | long grayscale(const unsigned char *input, unsigned char **output, int width, int height); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hash.h" 4 | #include "util.h" 5 | 6 | void scale(unsigned char *image, int width, int height, unsigned char **newImage, int newWidth, int newHeight) { 7 | *newImage = malloc((unsigned long) newWidth * newHeight); 8 | 9 | for (int y = 0; y < newHeight; y++) { 10 | for (int x = 0; x < newWidth; x++) { 11 | int oldY = (float) y / newHeight * height + 0.5; 12 | int oldX = (float) x / newWidth * width + 0.5; 13 | 14 | (*newImage)[y * newWidth + x] = image[oldY * width + oldX]; 15 | 16 | } 17 | } 18 | } 19 | 20 | void genHash(unsigned char *image, int width, int height, unsigned char **hash) { 21 | *hash = malloc((unsigned long) width * height); 22 | 23 | for (int y = 0; y < height; y++) { 24 | for (int x = 0; x < width; x++) { 25 | int pos = y * width + x; 26 | 27 | (*hash)[pos] = image[pos] < image[pos + 1]; 28 | } 29 | } 30 | } 31 | 32 | int jpegHash(const char *filename, unsigned char **hash, int size) { 33 | unsigned char *image; 34 | unsigned long imageSize = 0; 35 | unsigned char *scaled; 36 | int width, height; 37 | 38 | imageSize = decodeFile(filename, &image, FILETYPE_JPEG, &width, &height, JCS_GRAYSCALE); 39 | 40 | if (!imageSize) 41 | return 1; 42 | 43 | scale(image, width, height, &scaled, size, size); 44 | free(image); 45 | genHash(scaled, size, size, hash); 46 | free(scaled); 47 | 48 | return 0; 49 | } 50 | 51 | int jpegHashFromBuffer(unsigned char *imageBuf, long bufSize, unsigned char **hash, int size) { 52 | unsigned char *image; 53 | unsigned long imageSize = 0; 54 | unsigned char *scaled; 55 | int width, height; 56 | 57 | imageSize = decodeFileFromBuffer(imageBuf, bufSize, &image, FILETYPE_JPEG, &width, &height, JCS_GRAYSCALE); 58 | 59 | if (!imageSize) 60 | return 1; 61 | 62 | scale(image, width, height, &scaled, size, size); 63 | free(image); 64 | genHash(scaled, size, size, hash); 65 | free(scaled); 66 | 67 | return 0; 68 | } 69 | 70 | unsigned int hammingDist(const unsigned char *hash1, const unsigned char *hash2, int hashLength) { 71 | unsigned int dist = 0; 72 | 73 | for (unsigned int x = 0; x < hashLength; x++) { 74 | if (hash1[x] != hash2[x]) { 75 | dist++; 76 | } 77 | } 78 | 79 | return dist; 80 | } 81 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Image hashing functions 3 | */ 4 | #ifndef HASH_H 5 | #define HASH_H 6 | 7 | /* 8 | Generate an image hash given a filename. This is a convenience 9 | function which reads the file, decodes it to grayscale, 10 | scales the image, and generates the hash. 11 | */ 12 | int jpegHash(const char *filename, unsigned char **hash, int size); 13 | int jpegHashFromBuffer(unsigned char *imageBuf, long bufSize, unsigned char **hash, int size); 14 | 15 | /* 16 | Downscale an image with nearest-neighbor interpolation. 17 | http://jsperf.com/pixel-interpolation/2 18 | */ 19 | void scale(unsigned char *image, int width, int height, unsigned char **newImage, int newWidth, int newHeight); 20 | 21 | /* 22 | Generate an image hash based on gradients. 23 | http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html 24 | */ 25 | void genHash(unsigned char *image, int width, int height, unsigned char **hash); 26 | 27 | /* 28 | Calculate the hamming distance between two hashes. 29 | http://en.wikipedia.org/wiki/Hamming_distance 30 | */ 31 | unsigned int hammingDist(const unsigned char *hash1, const unsigned char *hash2, int hashLength); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/iqa/CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | 2011-07-06 1.1.2 (rev 40) 2 | 3 | - Fixed MS-SSIM seg-fault issue when height greater than width (#3349231). 4 | - Fixed linker warnings on Windows debug builds. 5 | 6 | 2011-04-15 1.1.1 (rev 36) 7 | 8 | - Added 64-bit build configurations on Windows. 9 | - Fixed 64-bit rounding bug in tests (#3286061). 10 | - Fixed MS-SSIM buffer overflow bug for images that don't scale to an even 11 | multiple of 2 (#3288043). Added corresponding unit test. 12 | 13 | 2011-04-09 1.1.0 (rev 30) 14 | 15 | - Added MS-SSIM algorithm. 16 | - Added MS-SSIM* algorithm. 17 | - Fixed bug (#3269715) that caused SSIM to return inflated results when using 18 | custom SSIM arguments. 19 | - Fixed bug (#3269702) where the SSIM algorithm was returning NAN instead of 20 | INFINITY on error. Since NAN always fails comparisons (even with itself), 21 | there was no way for an application to determine if an error occurred. 22 | - Improved the performace of the decimate operation. 23 | 24 | 2011-04-01 1.0.0 (rev 12) 25 | 26 | - First release. Supports MSE, PSNR, and SSIM. 27 | 28 | -------------------------------------------------------------------------------- /src/iqa/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR=./source 2 | SRC= \ 3 | $(SRCDIR)/convolve.c \ 4 | $(SRCDIR)/decimate.c \ 5 | $(SRCDIR)/math_utils.c \ 6 | $(SRCDIR)/mse.c \ 7 | $(SRCDIR)/psnr.c \ 8 | $(SRCDIR)/ssim.c \ 9 | $(SRCDIR)/ms_ssim.c 10 | 11 | OBJ = $(SRC:.c=.o) 12 | 13 | INCLUDES = -I./include 14 | CC ?= gcc 15 | 16 | ifeq ($(RELEASE),1) 17 | OUTDIR=./build/release 18 | CFLAGS=-O2 -Wall 19 | else 20 | OUTDIR=./build/debug 21 | CFLAGS=-g -Wall 22 | endif 23 | 24 | OUT = $(OUTDIR)/libiqa.a 25 | 26 | .c.o: 27 | $(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@ 28 | 29 | $(OUT): $(OBJ) 30 | mkdir -p $(OUTDIR) 31 | ar rcs $(OUT) $(OBJ) 32 | mv $(OBJ) $(OUTDIR) 33 | 34 | clean: 35 | rm -f $(OUTDIR)/*.o $(OUT) $(SRCDIR)/*.o 36 | cd test; $(MAKE) clean; 37 | 38 | .PHONY : test 39 | test: 40 | cd test; $(MAKE); 41 | -------------------------------------------------------------------------------- /src/iqa/README.txt: -------------------------------------------------------------------------------- 1 | Doxygen documentation can be found at: http://tdistler.com/iqa 2 | 3 | BUILD: 4 | 5 | All build artifacts end up in build/, where is 6 | 'debug' or 'release'. 7 | 8 | Windows: 9 | - Open iqa.sln, select 'Debug' or 'Release', and build. The output is a 10 | static library 'iqa.lib'. 11 | - To run the tests under the debugger, first right-click the 'test' project, 12 | select Properties -> Configuration Properties -> Debugging and set 13 | 'Working Directory' to '$(OutDir)'. Then start the application. 14 | 15 | Linux: 16 | - Change directories into the root of the IQA branch you want to build. 17 | - Type `make` for a debug build, or `make RELEASE=1` for a release build. 18 | The output is a static library 'libiqa.a'. 19 | - Type `make test` (or `make test RELEASE=1`) to build the unit tests. 20 | - Type `make clean` (or `make clean RELEASE=1`) to delete all build 21 | artifacts. 22 | - To run the tests, `cd` to the build/ directory and type 23 | `./test`. 24 | 25 | 26 | USE: 27 | 28 | - Include 'iqa.h' in your source file. 29 | - Call iqa_* methods. 30 | - Link against the IQA library. 31 | 32 | 33 | HELP & SUPPORT: 34 | 35 | Further help can be found at: https://sourceforge.net/projects/iqa/support 36 | 37 | -------------------------------------------------------------------------------- /src/iqa/doc/algorithms.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | /** 35 | * @page algorithms Algorithms 36 | * All of the algorithms described here are called full-reference algorithms. This means they required the original undistorted image to compare the distorted image against. 37 | * 38 | *
39 | *
40 | * @section mse MSE 41 | * Mean Squared Error is the average squared difference between a reference image and a distorted image. It is computed pixel-by-pixel by adding up the squared differences of all the pixels and dividing by the total pixel count. 42 | * 43 | * For images A = {a1 .. aM} and B = {b1 .. bM}, where M is the number of pixels: 44 | * @image html mse_eq.jpg 45 | * 46 | * The squaring of the differences dampens small differences between the 2 pixels but penalizes large ones. 47 | * 48 | * More info: http://en.wikipedia.org/wiki/Mean_squared_error 49 | * 50 | *
51 | *
52 | * @section psnr PSNR 53 | * Peak Signal-to-Noise Ratio is the ratio between the reference signal and the distortion signal in an image, given in decibels. The higher the PSNR, the closer the distorted image is to the original. In general, a higher PSNR value should correlate to a higher quality image, but tests have shown that this isn't always the case. However, PSNR is a popular quality metric because it's easy and fast to calculate while still giving okay results. 54 | * 55 | * For images A = {a1 .. aM}, B = {b1 .. bM}, and MAX equal to the maximum possible pixel value (2^8 - 1 = 255 for 8-bit images): 56 | * 57 | * @image html psnr_eq.jpg 58 | * 59 | * More info: http://en.wikipedia.org/wiki/PSNR 60 | * 61 | *
62 | *
63 | * @section ssim SSIM 64 | * Structural SIMilarity is based on the idea that the human visual system is highly adapted to process structural information, and the algorithm attepts to measure the change in this information between and reference and distorted image. Based on numberous tests, SSIM does a much better job at quantifying subjective image quality than MSE or PSNR. 65 | * 66 | * At a high level, SSIM attempts to measure the change in luminance, contrast, and structure in an image. Luminance is modeled as average pixel intensity, constrast by the variance between the reference and distorted image, and structure by the cross-correlation between the 2 images. The resulting values are combined (using exponents referred to as alpha, beta, and gamma) and averaged to generate a final SSIM index value. 67 | * 68 | * The original paper defined 2 methods for calculating each local SSIM value: an 8x8 linear or 11x11 circular Gaussian sliding window. IQA defaults to using the Gaussian window that the paper suggests for best results. However, the window type, stabilization constants, and exponents can all be set adjusted by the application. 69 | * 70 | * Original paper: https://ece.uwaterloo.ca/~z70wang/publications/ssim.html 71 | * 72 | * Here's an interesting article by the authors discussing the limitations of MSE and PSNR as compared to SSIM: https://ece.uwaterloo.ca/~z70wang/publications/SPM09.pdf 73 | * 74 | *
75 | *
76 | * @section ms_ssim MS-SSIM 77 | * Multi-Scale Structural SIMilarity is layered on SSIM. The algorithm calculates multiple SSIM values at multiple image scales (resolutions). By running the algorithm at different scales, the quality of the image is evaluated for different viewing distances. MS-SSIM also puts less emphasis on the luminance component compared to the contrast and structure components. In total, MS-SSIM has been shown to increase the correlation between the MS-SSIM index and subjective quality tests. However, the trade-off is that MS-SSIM takes a few times longer to run than the straight SSIM algorithm. 78 | * 79 | * @note MS-SSIM was proposed by Wang, et al, and later extended by Rouse/Hemami. IQA referes to MS-SSIM as 'wang'. 80 | * 81 | * Original paper: https://ece.uwaterloo.ca/~z70wang/publications/msssim.pdf 82 | * 83 | *
84 | *
85 | * @section ms_ssim_star MS-SSIM* 86 | * MS-SSIM* is an extension to the original MS-SSIM algorithm proposed by Wang, et al. MS-SSIM* does away with the stabilization constants and defines concrete values for the edge cases. The authors provide data that shows a modest increase in correlation between the MS-SSIM* index and subjective tests, as compared to MS-SSIM. MS-SSIM* is the default algorithm used by the iqa_ms_ssim() method. 87 | * 88 | * @note MS-SSIM* is sometimes referred to as Rouse/Hemami in IQA to differentiate it from MS-SSIM by Wang. 89 | * 90 | * Original paper: http://foulard.ece.cornell.edu/publications/dmr_hvei2008_paper.pdf 91 | * 92 | */ 93 | -------------------------------------------------------------------------------- /src/iqa/doc/build.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | /** 35 | * @page build Building IQA 36 | * All build artifacts end up in build/<configuration>, where <configuration> is 'debug' or 'release'. 37 | * 38 | * @section windows Windows 39 | * @li Open iqa.sln, select 'Debug' or 'Release', and build. The output is a static library 'iqa.lib'. 40 | * @li To run the tests under the debugger, first right-click the 'test' project, select Properties -> Configuration Properties -> Debugging and set 'Working Directory' to '$(OutDir)'. Then start the application. 41 | * 42 | * @section linux Linux 43 | * @li Change directories into the root of the IQA branch you want to build. 44 | * @li Type `make` for a debug build, or `make RELEASE=1` for a release build. The output is a static library 'libiqa.a'. 45 | * @li Type `make test` (or `make test RELEASE=1`) to build the unit tests. 46 | * @li Type `make clean` (or `make clean RELEASE=1`) to delete all build artifacts. 47 | * @li To run the tests, `cd` to the build/<configuration> directory and type `./test`. 48 | * 49 | * @code 50 | * > make clean 51 | * > make 52 | * > make test 53 | * > cd ./build/debug 54 | * > ./test 55 | * @endcode 56 | */ 57 | -------------------------------------------------------------------------------- /src/iqa/doc/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Copyright (c) 2011 by Tom Distler
4 |

5 | 6 | 8 | 9 |
10 |

11 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /src/iqa/doc/mainpage.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | /** 35 | * @mainpage 36 | * @section Overview 37 | * 38 | * The Image Quality Assessment (IQA) library implements popular algorithms for generating image/video quality metrics. There are several principles that guide IQA's development: 39 | * @li It must be easy to use (cover the common use cases... a.k.a. the 90% rule). 40 | * @li It must be accurate (results are checked against provided Matlab files in Octave). 41 | * @li It must be fast. 42 | * @li It must be portable. 43 | * @li It must be reliable (unit tested, Valgrind clean). 44 | * 45 | * The following algorithms are implemented in the current release: 46 | * @li @ref mse "MSE" 47 | * @li @ref psnr "PSNR" 48 | * @li @ref ssim "SSIM" 49 | * @li @ref ms_ssim "MS-SSIM" 50 | * @li @ref ms_ssim_star "MS-SSIM*" 51 | * 52 | * Check out the @ref algorithms "algorithms" page for information about each one. 53 | * 54 | *
55 | * 56 | * @section Download 57 | * Source: http://sourceforge.net/projects/iqa/files/ 58 | * 59 | * Browse the source tree: http://iqa.svn.sourceforge.net/viewvc/iqa/trunk 60 | * 61 | * SVN Checkout: @code svn co https://iqa.svn.sourceforge.net/svnroot/iqa/trunk iqa @endcode 62 | *
63 | * 64 | * @section get_started Getting Started 65 | * @li @ref algorithms "Algorithm Information" 66 | * @li @ref build "Building IQA" 67 | * @li @ref sample_code "Sample Code" 68 | * 69 | *
70 | * 71 | * @section problems Having Problems? 72 | * Submit a question or file a bug report at https://sourceforge.net/projects/iqa/support 73 | * 74 | *
75 | * 76 | * @section what What's New in 1.1.2 77 | * @li Fixed Windows linker warnings for debug builds. 78 | * @li Fixed seg-fault in MS-SSIM algorithm when height is greater than width (#3349231). 79 | * 80 | * See CHANGELOG.txt for a detailed list of changes in each version. 81 | *
82 | * 83 | */ 84 | -------------------------------------------------------------------------------- /src/iqa/doc/sample_code.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | /** 35 | * @page sample_code Sample Code 36 | * 37 | * All the IQA algorithm functions follow a similar prototype: 38 | * @code iqa_( , , , , , ); 39 | * @endcode 40 | * 41 | * @li iqa_mse() - Mean Squared Error 42 | * @li iqa_psnr() - Peak Signal-to-Noise Ration 43 | * @li iqa_ssim() - Structural SIMilarity 44 | * @li iqa_ms_ssim() - Multi-Scale Structure SIMilarity 45 | * 46 | * The only header file your code should need to include is "iqa.h". This file defines the main API for IQA. 47 | * 48 | * IQA is compiled as a static library so it is linked directly into your application (there is no *.so or *.dll). 49 | * 50 | *
51 | * @section example_1 Example 1 52 | * Here's a simple example showing how to calculate PSNR: 53 | * 54 | * @code 55 | * #include "iqa.h" 56 | * 57 | * // Load reference and modified images. 58 | * 59 | * float psnr = iqa_psnr(img_ref, img_mod, width, height, stride); 60 | * @endcode 61 | * 62 | * That's it (seriously). 63 | * 64 | *
65 | * @section example_2 Example 2 66 | * Some algorithms support additional configuration parameters. Here's an example that calculates the SSIM index using different weighting factors (a=0.5, b=1.34, c=0.5): 67 | * 68 | * @code 69 | * #include "iqa.h" 70 | * 71 | * float ssim; 72 | * struct iqa_ssim_args args; 73 | * args.alpha = 0.5f; 74 | * args.beta = 1.34f; 75 | * args.gamma = 0.5f; 76 | * args.L = 255; // default 77 | * args.K1 = 0.01; // default 78 | * args.K2 = 0.03; // default 79 | * args.f = 0; // default 80 | * 81 | * // Load reference and modified images. 82 | * 83 | * ssim = iqa_ssim(img_ref, img_mod, width, height, stride, 1, &args); 84 | * if (ssim == INFINITY) 85 | * // Error 86 | * @endcode 87 | */ 88 | -------------------------------------------------------------------------------- /src/iqa/include/convolve.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _CONVOLVE_H_ 35 | #define _CONVOLVE_H_ 36 | 37 | typedef float (*_iqa_get_pixel)(const float *img, int w, int h, int x, int y, float bnd_const); 38 | 39 | /** Out-of-bounds array values are a mirrored reflection of the border values*/ 40 | float KBND_SYMMETRIC(const float *img, int w, int h, int x, int y, float bnd_const); 41 | /** Out-of-bounds array values are set to the nearest border value */ 42 | float KBND_REPLICATE(const float *img, int w, int h, int x, int y, float bnd_const); 43 | /** Out-of-bounds array values are set to 'bnd_const' */ 44 | float KBND_CONSTANT(const float *img, int w, int h, int x, int y, float bnd_const); 45 | 46 | 47 | /** Defines a convolution kernel */ 48 | struct _kernel { 49 | float *kernel; /**< Pointer to the kernel values */ 50 | int w; /**< The kernel width */ 51 | int h; /**< The kernel height */ 52 | int normalized; /**< 1 if the kernel values add up to 1. 0 otherwise */ 53 | _iqa_get_pixel bnd_opt; /**< Defines how out-of-bounds image values are handled */ 54 | float bnd_const; /**< If 'bnd_opt' is KBND_CONSTANT, this specifies the out-of-bounds value */ 55 | }; 56 | 57 | /** 58 | * @brief Applies the specified kernel to the image. 59 | * The kernel will be applied to all areas where it fits completely within 60 | * the image. The resulting image will be smaller by half the kernel width 61 | * and height (w - kw/2 and h - kh/2). 62 | * 63 | * @param img Image to modify 64 | * @param w Image width 65 | * @param h Image height 66 | * @param k The kernel to apply 67 | * @param result Buffer to hold the resulting image ((w-kw)*(h-kh), where kw 68 | * and kh are the kernel width and height). If 0, the result 69 | * will be written to the original image buffer. 70 | * @param rw Optional. The width of the resulting image will be stored here. 71 | * @param rh Optional. The height of the resulting image will be stored here. 72 | */ 73 | void _iqa_convolve(float *img, int w, int h, const struct _kernel *k, float *result, int *rw, int *rh); 74 | 75 | /** 76 | * The same as _iqa_convolve() except the kernel is applied to the entire image. 77 | * In other words, the kernel is applied to all areas where the top-left corner 78 | * of the kernel is in the image. Out-of-bound pixel value (off the right and 79 | * bottom edges) are chosen based on the 'bnd_opt' and 'bnd_const' members of 80 | * the kernel structure. The resulting array is the same size as the input 81 | * image. 82 | * 83 | * @param img Image to modify 84 | * @param w Image width 85 | * @param h Image height 86 | * @param k The kernel to apply 87 | * @param result Buffer to hold the resulting image ((w-kw)*(h-kh), where kw 88 | * and kh are the kernel width and height). If 0, the result 89 | * will be written to the original image buffer. 90 | * @return 0 if successful. Non-zero otherwise. 91 | */ 92 | int _iqa_img_filter(float *img, int w, int h, const struct _kernel *k, float *result); 93 | 94 | /** 95 | * Returns the filtered version of the specified pixel. If no kernel is given, 96 | * the raw pixel value is returned. 97 | * 98 | * @param img Source image 99 | * @param w Image width 100 | * @param h Image height 101 | * @param x The x location of the pixel to filter 102 | * @param y The y location of the pixel to filter 103 | * @param k Optional. The convolution kernel to apply to the pixel. 104 | * @param kscale The scale of the kernel (for normalization). 1 for normalized 105 | * kernels. Required if 'k' is not null. 106 | * @return The filtered pixel value. 107 | */ 108 | float _iqa_filter_pixel(const float *img, int w, int h, int x, int y, const struct _kernel *k, const float kscale); 109 | 110 | 111 | #endif /*_CONVOLVE_H_*/ 112 | -------------------------------------------------------------------------------- /src/iqa/include/decimate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _DECIMATE_H_ 35 | #define _DECIMATE_H_ 36 | 37 | #include "convolve.h" 38 | 39 | /** 40 | * @brief Downsamples (decimates) an image. 41 | * 42 | * @param img Image to modify 43 | * @param w Image width 44 | * @param h Image height 45 | * @param factor Decimation factor 46 | * @param k The kernel to apply (e.g. low-pass filter). Can be 0. 47 | * @param result Buffer to hold the resulting image (w/factor*h/factor). If 0, 48 | * the result will be written to the original image buffer. 49 | * @param rw Optional. The width of the resulting image will be stored here. 50 | * @param rh Optional. The height of the resulting image will be stored here. 51 | * @return 0 on success. 52 | */ 53 | int _iqa_decimate(float *img, int w, int h, int factor, const struct _kernel *k, float *result, int *rw, int *rh); 54 | 55 | #endif /*_DECIMATE_H_*/ 56 | -------------------------------------------------------------------------------- /src/iqa/include/iqa.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _IQA_H_ 35 | #define _IQA_H_ 36 | 37 | #include "iqa_os.h" 38 | 39 | /** 40 | * Allows fine-grain control of the SSIM algorithm. 41 | */ 42 | struct iqa_ssim_args { 43 | float alpha; /**< luminance exponent */ 44 | float beta; /**< contrast exponent */ 45 | float gamma; /**< structure exponent */ 46 | int L; /**< dynamic range (2^8 - 1)*/ 47 | float K1; /**< stabilization constant 1 */ 48 | float K2; /**< stabilization constant 2 */ 49 | int f; /**< scale factor. 0=default scaling, 1=no scaling */ 50 | }; 51 | 52 | /** 53 | * Allows fine-grain control of the MS-SSIM algorithm. 54 | */ 55 | struct iqa_ms_ssim_args { 56 | int wang; /**< 1=original algorithm by Wang, et al. 0=MS-SSIM* by Rouse/Hemami (default). */ 57 | int gaussian; /**< 1=11x11 Gaussian window (default). 0=8x8 linear window. */ 58 | int scales; /**< Number of scaled images to use. Default is 5. */ 59 | const float *alphas; /**< Pointer to array of alpha values for each scale. Required if 'scales' isn't 5. */ 60 | const float *betas; /**< Pointer to array of beta values for each scale. Required if 'scales' isn't 5. */ 61 | const float *gammas; /**< Pointer to array of gamma values for each scale. Required if 'scales' isn't 5. */ 62 | }; 63 | 64 | /** 65 | * Calculates the Mean Squared Error between 2 equal-sized 8-bit images. 66 | * @note The images must have the same width, height, and stride. 67 | * @param ref Original reference image 68 | * @param cmp Distorted image 69 | * @param w Width of the images 70 | * @param h Height of the images 71 | * @param stride The length (in bytes) of each horizontal line in the image. 72 | * This may be different from the image width. 73 | * @return The MSE. 74 | */ 75 | float iqa_mse(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride); 76 | 77 | /** 78 | * Calculates the Peak Signal-to-Noise-Ratio between 2 equal-sized 8-bit 79 | * images. 80 | * @note The images must have the same width, height, and stride. 81 | * @param ref Original reference image 82 | * @param cmp Distorted image 83 | * @param w Width of the images 84 | * @param h Height of the images 85 | * @param stride The length (in bytes) of each horizontal line in the image. 86 | * This may be different from the image width. 87 | * @return The PSNR. 88 | */ 89 | float iqa_psnr(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride); 90 | 91 | /** 92 | * Calculates the Structural SIMilarity between 2 equal-sized 8-bit images. 93 | * 94 | * See https://ece.uwaterloo.ca/~z70wang/publications/ssim.html 95 | * @note The images must have the same width, height, and stride. 96 | * @param ref Original reference image 97 | * @param cmp Distorted image 98 | * @param w Width of the images 99 | * @param h Height of the images 100 | * @param stride The length (in bytes) of each horizontal line in the image. 101 | * This may be different from the image width. 102 | * @param gaussian 0 = 8x8 square window, 1 = 11x11 circular-symmetric Gaussian 103 | * weighting. 104 | * @param args Optional SSIM arguments for fine control of the algorithm. 0 for 105 | * defaults. Defaults are a=b=g=1.0, L=255, K1=0.01, K2=0.03 106 | * @return The mean SSIM over the entire image (MSSIM), or INFINITY if error. 107 | */ 108 | float iqa_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride, 109 | int gaussian, const struct iqa_ssim_args *args); 110 | 111 | /** 112 | * Calculates the Multi-Scale Structural SIMilarity between 2 equal-sized 8-bit 113 | * images. The default algorithm is MS-SSIM* proposed by Rouse/Hemami 2008. 114 | * 115 | * See https://ece.uwaterloo.ca/~z70wang/publications/msssim.pdf and 116 | * http://foulard.ece.cornell.edu/publications/dmr_hvei2008_paper.pdf 117 | * 118 | * @note 1. The images must have the same width, height, and stride. 119 | * @note 2. The minimum image width or height is 2^(scales-1) * filter, where 'filter' is 11 120 | * if a Gaussian window is being used, or 9 otherwise. 121 | * @param ref Original reference image 122 | * @param cmp Distorted image 123 | * @param w Width of the images. 124 | * @param h Height of the images. 125 | * @param stride The length (in bytes) of each horizontal line in the image. 126 | * This may be different from the image width. 127 | * @param args Optional MS-SSIM arguments for fine control of the algorithm. 0 128 | * for defaults. Defaults are wang=0, scales=5, gaussian=1. 129 | * @return The mean MS-SSIM over the entire image, or INFINITY if error. 130 | */ 131 | float iqa_ms_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride, 132 | const struct iqa_ms_ssim_args *args); 133 | 134 | #endif /*_IQA_H_*/ 135 | -------------------------------------------------------------------------------- /src/iqa/include/iqa_os.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _OS_H_ 35 | #define _OS_H_ 36 | 37 | /* Microsoft tends to implement features early, but they have a high legacy 38 | * cost because they won't break existing implementations. As such, certain 39 | * features we take for granted on other platforms (like C99) aren't fully 40 | * implemented. This file is meant to rectify that. 41 | */ 42 | 43 | #ifdef WIN32 44 | 45 | #include 46 | #define IQA_INLINE __inline 47 | 48 | #ifndef INFINITY 49 | #define INFINITY (float)HUGE_VAL /**< Defined in C99 (Windows is C89) */ 50 | #endif /*INFINITY*/ 51 | 52 | #ifndef NAN 53 | static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; 54 | #define NAN (*(const float *) __nan) /**< Defined in C99 (Windows is C99) */ 55 | #endif 56 | 57 | #define IQA_EXPORT __declspec(dllexport) 58 | 59 | #else /* !Windows */ 60 | 61 | #define IQA_INLINE inline 62 | #define IQA_EXPORT 63 | 64 | #endif 65 | 66 | #endif /* _OS_H_ */ 67 | -------------------------------------------------------------------------------- /src/iqa/include/math_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _MATH_UTILS_H_ 35 | #define _MATH_UTILS_H_ 36 | 37 | #include "iqa_os.h" 38 | #include 39 | 40 | /** 41 | * Rounds a float to the nearest integer. 42 | */ 43 | IQA_EXPORT IQA_INLINE int _round(float a); 44 | 45 | IQA_EXPORT IQA_INLINE int _max(int x, int y); 46 | 47 | IQA_EXPORT IQA_INLINE int _min(int x, int y); 48 | 49 | 50 | /** 51 | * Compares 2 floats to the specified digit of precision. 52 | * @return 0 if equal, 1 otherwise. 53 | */ 54 | IQA_EXPORT IQA_INLINE int _cmp_float(float a, float b, int digits); 55 | 56 | 57 | /** 58 | * Compares 2 matrices with the specified precision. 'b' is assumed to be the 59 | * same size as 'a' or smaller. 60 | * @return 0 if equal, 1 otherwise 61 | */ 62 | IQA_EXPORT IQA_INLINE int _matrix_cmp(const float *a, const float *b, int w, int h, int digits); 63 | 64 | #endif /*_MATH_UTILS_H_*/ 65 | -------------------------------------------------------------------------------- /src/iqa/include/ssim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _SSIM_H_ 35 | #define _SSIM_H_ 36 | 37 | #include "convolve.h" 38 | 39 | /* 40 | * Circular-symmetric Gaussian weighting. 41 | * h(x,y) = hg(x,y)/SUM(SUM(hg)) , for normalization to 1.0 42 | * hg(x,y) = e^( -0.5*( (x^2+y^2)/sigma^2 ) ) , where sigma was 1.5 43 | */ 44 | #define GAUSSIAN_LEN 11 45 | static const float g_gaussian_window[GAUSSIAN_LEN][GAUSSIAN_LEN] = { 46 | {0.000001f, 0.000008f, 0.000037f, 0.000112f, 0.000219f, 0.000274f, 0.000219f, 0.000112f, 0.000037f, 0.000008f, 0.000001f}, 47 | {0.000008f, 0.000058f, 0.000274f, 0.000831f, 0.001619f, 0.002021f, 0.001619f, 0.000831f, 0.000274f, 0.000058f, 0.000008f}, 48 | {0.000037f, 0.000274f, 0.001296f, 0.003937f, 0.007668f, 0.009577f, 0.007668f, 0.003937f, 0.001296f, 0.000274f, 0.000037f}, 49 | {0.000112f, 0.000831f, 0.003937f, 0.011960f, 0.023294f, 0.029091f, 0.023294f, 0.011960f, 0.003937f, 0.000831f, 0.000112f}, 50 | {0.000219f, 0.001619f, 0.007668f, 0.023294f, 0.045371f, 0.056662f, 0.045371f, 0.023294f, 0.007668f, 0.001619f, 0.000219f}, 51 | {0.000274f, 0.002021f, 0.009577f, 0.029091f, 0.056662f, 0.070762f, 0.056662f, 0.029091f, 0.009577f, 0.002021f, 0.000274f}, 52 | {0.000219f, 0.001619f, 0.007668f, 0.023294f, 0.045371f, 0.056662f, 0.045371f, 0.023294f, 0.007668f, 0.001619f, 0.000219f}, 53 | {0.000112f, 0.000831f, 0.003937f, 0.011960f, 0.023294f, 0.029091f, 0.023294f, 0.011960f, 0.003937f, 0.000831f, 0.000112f}, 54 | {0.000037f, 0.000274f, 0.001296f, 0.003937f, 0.007668f, 0.009577f, 0.007668f, 0.003937f, 0.001296f, 0.000274f, 0.000037f}, 55 | {0.000008f, 0.000058f, 0.000274f, 0.000831f, 0.001619f, 0.002021f, 0.001619f, 0.000831f, 0.000274f, 0.000058f, 0.000008f}, 56 | {0.000001f, 0.000008f, 0.000037f, 0.000112f, 0.000219f, 0.000274f, 0.000219f, 0.000112f, 0.000037f, 0.000008f, 0.000001f}, 57 | }; 58 | 59 | /* 60 | * Equal weight square window. 61 | * Each pixel is equally weighted (1/64) so that SUM(x) = 1.0 62 | */ 63 | #define SQUARE_LEN 8 64 | static const float g_square_window[SQUARE_LEN][SQUARE_LEN] = { 65 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 66 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 67 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 68 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 69 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 70 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 71 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 72 | {0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f, 0.015625f}, 73 | }; 74 | 75 | /* Holds intermediate SSIM values for map-reduce operation. */ 76 | struct _ssim_int { 77 | double l; 78 | double c; 79 | double s; 80 | }; 81 | 82 | /* Defines the pointers to the map-reduce functions. */ 83 | typedef int (*_map)(const struct _ssim_int *, void *); 84 | typedef float (*_reduce)(int, int, void *); 85 | 86 | /* Arguments for map-reduce. The 'context' is user-defined. */ 87 | struct _map_reduce { 88 | _map map; 89 | _reduce reduce; 90 | void *context; 91 | }; 92 | 93 | /** 94 | * Private method that calculates the SSIM value on a pre-processed image. 95 | * 96 | * The input images must have stride==width. This method does not scale. 97 | * 98 | * @note Image buffers are modified. 99 | * 100 | * Map-reduce is used for doing the final SSIM calculation. The map function is 101 | * called for every pixel, and the reduce is called at the end. The context is 102 | * caller-defined and *not* modified by this method. 103 | * 104 | * @param ref Original reference image 105 | * @param cmp Distorted image 106 | * @param w Width of the images 107 | * @param h Height of the images 108 | * @param k The kernel used as the window function 109 | * @param mr Optional map-reduce functions to use to calculate SSIM. Required 110 | * if 'args' is not null. Ignored if 'args' is null. 111 | * @param args Optional SSIM arguments for fine control of the algorithm. 0 for defaults. 112 | * Defaults are a=b=g=1.0, L=255, K1=0.01, K2=0.03 113 | * @return The mean SSIM over the entire image (MSSIM), or INFINITY if error. 114 | */ 115 | float _iqa_ssim(float *ref, float *cmp, int w, int h, const struct _kernel *k, const struct _map_reduce *mr, const struct iqa_ssim_args *args); 116 | 117 | #endif /* _SSIM_H_ */ 118 | -------------------------------------------------------------------------------- /src/iqa/iqa.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "iqa", "iqa.vcproj", "{3C7BB421-C147-4828-8EAE-53A5F1A0394A}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test.vcproj", "{CB10555C-6A76-4AD9-A5BA-2D2733115859}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A} = {3C7BB421-C147-4828-8EAE-53A5F1A0394A} 9 | EndProjectSection 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Win32 = Debug|Win32 14 | Debug|x64 = Debug|x64 15 | Release|Win32 = Release|Win32 16 | Release|x64 = Release|x64 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Debug|Win32.ActiveCfg = Debug|Win32 20 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Debug|Win32.Build.0 = Debug|Win32 21 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Debug|x64.ActiveCfg = Debug|x64 22 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Debug|x64.Build.0 = Debug|x64 23 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Release|Win32.ActiveCfg = Release|Win32 24 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Release|Win32.Build.0 = Release|Win32 25 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Release|x64.ActiveCfg = Release|x64 26 | {3C7BB421-C147-4828-8EAE-53A5F1A0394A}.Release|x64.Build.0 = Release|x64 27 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Debug|Win32.ActiveCfg = Debug|Win32 28 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Debug|Win32.Build.0 = Debug|Win32 29 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Debug|x64.ActiveCfg = Debug|x64 30 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Debug|x64.Build.0 = Debug|x64 31 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Release|Win32.ActiveCfg = Release|Win32 32 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Release|Win32.Build.0 = Release|Win32 33 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Release|x64.ActiveCfg = Release|x64 34 | {CB10555C-6A76-4AD9-A5BA-2D2733115859}.Release|x64.Build.0 = Release|x64 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /src/iqa/iqa.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 18 | 19 | 20 | 21 | 22 | 29 | 32 | 35 | 38 | 41 | 44 | 58 | 61 | 64 | 67 | 70 | 73 | 76 | 79 | 82 | 85 | 86 | 93 | 96 | 99 | 102 | 105 | 109 | 123 | 126 | 129 | 132 | 135 | 138 | 141 | 144 | 147 | 150 | 151 | 159 | 162 | 165 | 168 | 171 | 174 | 191 | 194 | 197 | 200 | 203 | 206 | 209 | 212 | 215 | 218 | 219 | 227 | 230 | 233 | 236 | 239 | 243 | 260 | 263 | 266 | 269 | 272 | 275 | 278 | 281 | 284 | 287 | 288 | 289 | 290 | 291 | 292 | 297 | 300 | 301 | 304 | 305 | 308 | 309 | 312 | 313 | 316 | 317 | 320 | 321 | 324 | 325 | 326 | 331 | 334 | 335 | 338 | 339 | 342 | 343 | 346 | 347 | 350 | 351 | 354 | 355 | 356 | 359 | 362 | 363 | 366 | 367 | 370 | 371 | 374 | 375 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | -------------------------------------------------------------------------------- /src/iqa/resources/iqa_logo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/resources/iqa_logo.bmp -------------------------------------------------------------------------------- /src/iqa/resources/iqa_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/resources/iqa_logo.jpg -------------------------------------------------------------------------------- /src/iqa/resources/mse_eq.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/resources/mse_eq.jpg -------------------------------------------------------------------------------- /src/iqa/resources/psnr_eq.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/resources/psnr_eq.jpg -------------------------------------------------------------------------------- /src/iqa/source/convolve.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "convolve.h" 35 | #include 36 | 37 | float KBND_SYMMETRIC(const float *img, int w, int h, int x, int y, float bnd_const) 38 | { 39 | if (x<0) x=-1-x; 40 | else if (x>=w) x=(w-(x-w))-1; 41 | if (y<0) y=-1-y; 42 | else if (y>=h) y=(h-(y-h))-1; 43 | return img[y*w + x]; 44 | } 45 | 46 | float KBND_REPLICATE(const float *img, int w, int h, int x, int y, float bnd_const) 47 | { 48 | if (x<0) x=0; 49 | if (x>=w) x=w-1; 50 | if (y<0) y=0; 51 | if (y>=h) y=h-1; 52 | return img[y*w + x]; 53 | } 54 | 55 | float KBND_CONSTANT(const float *img, int w, int h, int x, int y, float bnd_const) 56 | { 57 | if (x<0) x=0; 58 | if (y<0) y=0; 59 | if (x>=w || y>=h) 60 | return bnd_const; 61 | return img[y*w + x]; 62 | } 63 | 64 | static float _calc_scale(const struct _kernel *k) 65 | { 66 | int ii,k_len; 67 | double sum=0.0; 68 | 69 | if (k->normalized) 70 | return 1.0f; 71 | else { 72 | k_len = k->w * k->h; 73 | for (ii=0; iikernel[ii]; 75 | if (sum != 0.0) 76 | return (float)(1.0 / sum); 77 | return 1.0f; 78 | } 79 | } 80 | 81 | void _iqa_convolve(float *img, int w, int h, const struct _kernel *k, float *result, int *rw, int *rh) 82 | { 83 | int x,y,kx,ky,u,v; 84 | int uc = k->w/2; 85 | int vc = k->h/2; 86 | int kw_even = (k->w&1)?0:1; 87 | int kh_even = (k->h&1)?0:1; 88 | int dst_w = w - k->w + 1; 89 | int dst_h = h - k->h + 1; 90 | int img_offset,k_offset; 91 | double sum; 92 | float scale, *dst=result; 93 | 94 | if (!dst) 95 | dst = img; /* Convolve in-place */ 96 | 97 | /* Kernel is applied to all positions where the kernel is fully contained 98 | * in the image */ 99 | scale = _calc_scale(k); 100 | for (y=0; y < dst_h; ++y) { 101 | for (x=0; x < dst_w; ++x) { 102 | sum = 0.0; 103 | k_offset = 0; 104 | ky = y+vc; 105 | kx = x+uc; 106 | for (v=-vc; v <= vc-kh_even; ++v) { 107 | img_offset = (ky+v)*w + kx; 108 | for (u=-uc; u <= uc-kw_even; ++u, ++k_offset) { 109 | sum += img[img_offset+u] * k->kernel[k_offset]; 110 | } 111 | } 112 | dst[y*dst_w + x] = (float)(sum * scale); 113 | } 114 | } 115 | 116 | if (rw) *rw = dst_w; 117 | if (rh) *rh = dst_h; 118 | } 119 | 120 | int _iqa_img_filter(float *img, int w, int h, const struct _kernel *k, float *result) 121 | { 122 | int x,y; 123 | int img_offset; 124 | float scale, *dst=result; 125 | 126 | if (!k || !k->bnd_opt) 127 | return 1; 128 | 129 | if (!dst) { 130 | dst = (float*)malloc(w*h*sizeof(float)); 131 | if (!dst) 132 | return 2; 133 | } 134 | 135 | scale = _calc_scale(k); 136 | 137 | /* Kernel is applied to all positions where top-left corner is in the image */ 138 | for (y=0; y < h; ++y) { 139 | for (x=0; x < w; ++x) { 140 | dst[y*w + x] = _iqa_filter_pixel(img, w, h, x, y, k, scale); 141 | } 142 | } 143 | 144 | /* If no result buffer given, copy results to image buffer */ 145 | if (!result) { 146 | for (y=0; yw/2; 169 | vc = k->h/2; 170 | kw_even = (k->w&1)?0:1; 171 | kh_even = (k->h&1)?0:1; 172 | x_edge_left = uc; 173 | x_edge_right = w-uc; 174 | y_edge_top = vc; 175 | y_edge_bottom = h-vc; 176 | 177 | edge = 0; 178 | if (x < x_edge_left || y < y_edge_top || x >= x_edge_right || y >= y_edge_bottom) 179 | edge = 1; 180 | 181 | sum = 0.0; 182 | k_offset = 0; 183 | for (v=-vc; v <= vc-kh_even; ++v) { 184 | img_offset = (y+v)*w + x; 185 | for (u=-uc; u <= uc-kw_even; ++u, ++k_offset) { 186 | if (!edge) 187 | sum += img[img_offset+u] * k->kernel[k_offset]; 188 | else 189 | sum += k->bnd_opt(img, w, h, x+u, y+v, k->bnd_const) * k->kernel[k_offset]; 190 | } 191 | } 192 | return (float)(sum * kscale); 193 | } -------------------------------------------------------------------------------- /src/iqa/source/decimate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "decimate.h" 35 | #include 36 | 37 | int _iqa_decimate(float *img, int w, int h, int factor, const struct _kernel *k, float *result, int *rw, int *rh) 38 | { 39 | int x,y; 40 | int sw = w/factor + (w&1); 41 | int sh = h/factor + (h&1); 42 | int dst_offset; 43 | float *dst=img; 44 | 45 | if (result) 46 | dst = result; 47 | 48 | /* Downsample */ 49 | for (y=0; y 36 | 37 | int _round(float a) 38 | { 39 | int sign_a = a > 0.0f ? 1 : -1; 40 | return a-(int)a >= 0.5 ? (int)a + sign_a : (int)a; 41 | } 42 | 43 | int _max(int x, int y) 44 | { 45 | return x >= y ? x : y; 46 | } 47 | 48 | int _min(int x, int y) 49 | { 50 | return x <= y ? x : y; 51 | } 52 | 53 | int _cmp_float(float a, float b, int digits) 54 | { 55 | /* Round */ 56 | int sign_a = a > 0.0f ? 1 : -1; 57 | int sign_b = b > 0.0f ? 1 : -1; 58 | double scale = pow(10.0, (double)digits); 59 | double ax = a * scale; 60 | double bx = b * scale; 61 | int ai = ax-(int)ax >= 0.5 ? (int)ax + sign_a : (int)ax; 62 | int bi = bx-(int)bx >= 0.5 ? (int)bx + sign_b : (int)bx; 63 | 64 | /* Compare */ 65 | return ai == bi ? 0 : 1; 66 | } 67 | 68 | int _matrix_cmp(const float *a, const float *b, int w, int h, int digits) 69 | { 70 | int offset; 71 | int result=0; 72 | int len=w*h; 73 | for (offset=0; offset 38 | #include 39 | #include 40 | 41 | /* Default number of scales */ 42 | #define SCALES 5 43 | 44 | /* Low-pass filter for down-sampling (9/7 biorthogonal wavelet filter) */ 45 | #define LPF_LEN 9 46 | static const float g_lpf[LPF_LEN][LPF_LEN] = { 47 | { 0.000714f,-0.000450f,-0.002090f, 0.007132f, 0.016114f, 0.007132f,-0.002090f,-0.000450f, 0.000714f}, 48 | {-0.000450f, 0.000283f, 0.001316f,-0.004490f,-0.010146f,-0.004490f, 0.001316f, 0.000283f,-0.000450f}, 49 | {-0.002090f, 0.001316f, 0.006115f,-0.020867f,-0.047149f,-0.020867f, 0.006115f, 0.001316f,-0.002090f}, 50 | { 0.007132f,-0.004490f,-0.020867f, 0.071207f, 0.160885f, 0.071207f,-0.020867f,-0.004490f, 0.007132f}, 51 | { 0.016114f,-0.010146f,-0.047149f, 0.160885f, 0.363505f, 0.160885f,-0.047149f,-0.010146f, 0.016114f}, 52 | { 0.007132f,-0.004490f,-0.020867f, 0.071207f, 0.160885f, 0.071207f,-0.020867f,-0.004490f, 0.007132f}, 53 | {-0.002090f, 0.001316f, 0.006115f,-0.020867f,-0.047149f,-0.020867f, 0.006115f, 0.001316f,-0.002090f}, 54 | {-0.000450f, 0.000283f, 0.001316f,-0.004490f,-0.010146f,-0.004490f, 0.001316f, 0.000283f,-0.000450f}, 55 | { 0.000714f,-0.000450f,-0.002090f, 0.007132f, 0.016114f, 0.007132f,-0.002090f,-0.000450f, 0.000714f}, 56 | }; 57 | 58 | /* Alpha, beta, and gamma values for each scale */ 59 | static float g_alphas[] = { 0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.1333f }; 60 | static float g_betas[] = { 0.0448f, 0.2856f, 0.3001f, 0.2363f, 0.1333f }; 61 | static float g_gammas[] = { 0.0448f, 0.2856f, 0.3001f, 0.2363f, 0.1333f }; 62 | 63 | 64 | struct _context { 65 | double l; /* Luminance */ 66 | double c; /* Contrast */ 67 | double s; /* Structure */ 68 | float alpha; 69 | float beta; 70 | float gamma; 71 | }; 72 | 73 | /* Called for each pixel */ 74 | int _ms_ssim_map(const struct _ssim_int *si, void *ctx) 75 | { 76 | struct _context *ms_ctx = (struct _context*)ctx; 77 | ms_ctx->l += si->l; 78 | ms_ctx->c += si->c; 79 | ms_ctx->s += si->s; 80 | return 0; 81 | } 82 | 83 | /* Called to calculate the final result */ 84 | float _ms_ssim_reduce(int w, int h, void *ctx) 85 | { 86 | double size = (double)(w*h); 87 | struct _context *ms_ctx = (struct _context*)ctx; 88 | ms_ctx->l = pow(ms_ctx->l / size, (double)ms_ctx->alpha); 89 | ms_ctx->c = pow(ms_ctx->c / size, (double)ms_ctx->beta); 90 | ms_ctx->s = pow(fabs(ms_ctx->s / size), (double)ms_ctx->gamma); 91 | return (float)(ms_ctx->l * ms_ctx->c * ms_ctx->s); 92 | } 93 | 94 | /* Releases the scaled buffers */ 95 | void _free_buffers(float **buf, int scales) 96 | { 97 | int idx; 98 | for (idx=0; idxM]( Cj(x,y)^bj * Sj(x,y)^gj ) 122 | * where, 123 | * L = mean 124 | * C = variance 125 | * S = cross-correlation 126 | * 127 | * b1=g1=0.0448, b2=g2=0.2856, b3=g3=0.3001, b4=g4=0.2363, a5=b5=g5=0.1333 128 | */ 129 | float iqa_ms_ssim(const unsigned char *ref, const unsigned char *cmp, int w, int h, 130 | int stride, const struct iqa_ms_ssim_args *args) 131 | { 132 | int wang=0; 133 | int scales=SCALES; 134 | int gauss=1; 135 | const float *alphas=g_alphas, *betas=g_betas, *gammas=g_gammas; 136 | int idx,x,y,cur_w,cur_h; 137 | int offset,src_offset; 138 | float **ref_imgs, **cmp_imgs; /* Array of pointers to scaled images */ 139 | float msssim; 140 | struct _kernel lpf, window; 141 | struct iqa_ssim_args s_args; 142 | struct _map_reduce mr; 143 | struct _context ms_ctx; 144 | 145 | if (args) { 146 | wang = args->wang; 147 | gauss = args->gaussian; 148 | scales = args->scales; 149 | if (args->alphas) 150 | alphas = args->alphas; 151 | if (args->betas) 152 | betas = args->betas; 153 | if (args->gammas) 154 | gammas = args->gammas; 155 | } 156 | 157 | /* Make sure we won't scale below 1x1 */ 158 | cur_w = w; 159 | cur_h = h; 160 | for (idx=0; idx 36 | 37 | /* PSNR(a,b) = 10*log10(L^2 / MSE(a,b)), where L=2^b - 1 (8bit = 255) */ 38 | float iqa_psnr(const unsigned char *ref, const unsigned char *cmp, int w, int h, int stride) 39 | { 40 | const int L_sqd = 255 * 255; 41 | return (float)( 10.0 * log10( L_sqd / iqa_mse(ref,cmp,w,h,stride) ) ); 42 | } 43 | -------------------------------------------------------------------------------- /src/iqa/test/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR=./source 2 | SRC= \ 3 | $(SRCDIR)/bmp.c \ 4 | $(SRCDIR)/hptime.c \ 5 | $(SRCDIR)/main.c \ 6 | $(SRCDIR)/test_convolve.c \ 7 | $(SRCDIR)/test_decimate.c \ 8 | $(SRCDIR)/test_mse.c \ 9 | $(SRCDIR)/test_psnr.c \ 10 | $(SRCDIR)/test_ssim.c \ 11 | $(SRCDIR)/test_ms_ssim.c 12 | 13 | OBJ = $(SRC:.c=.o) 14 | 15 | INCLUDES = -I./include -I../include 16 | CC = gcc 17 | 18 | ifeq ($(RELEASE),1) 19 | OUTDIR=../build/release 20 | CFLAGS=-O2 -Wall 21 | else 22 | OUTDIR=../build/debug 23 | CFLAGS=-g -Wall 24 | endif 25 | 26 | OUT = $(OUTDIR)/test 27 | 28 | LFLAGS=-L$(OUTDIR) 29 | LIBS=$(OUTDIR)/libiqa.a -lm -lrt 30 | 31 | .c.o: 32 | $(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@ 33 | 34 | $(OUT): $(OBJ) 35 | mkdir -p $(OUTDIR) 36 | $(CC) $(INCLUDES) $(CFLAGS) $(LFLAGS) $^ $(LIBS) -o $@ 37 | cp ./resources/*.bmp $(OUTDIR) 38 | mv $(OBJ) $(OUTDIR) 39 | 40 | clean: 41 | rm -f $(OUTDIR)/*.o $(OUT) $(SRCDIR)/*.o 42 | rm -f $(OUTDIR)/*.bmp 43 | 44 | -------------------------------------------------------------------------------- /src/iqa/test/include/bmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _BMP_H_ 35 | #define _BMP_H_ 36 | 37 | struct bmp { 38 | int w; /* Image dimensions */ 39 | int h; 40 | int stride; 41 | int bit_depth; 42 | unsigned char *raw; /* Raw file data */ 43 | unsigned char *img; /* Points to the actual image data */ 44 | }; 45 | 46 | struct bmp_magic { 47 | unsigned char magic[2]; 48 | }; 49 | 50 | struct bmp_header { 51 | unsigned int filesz; 52 | unsigned short creator1; 53 | unsigned short creator2; 54 | unsigned int bmp_offset; 55 | }; 56 | 57 | struct bmp_info_hdr{ 58 | unsigned int header_sz; /* Only support 40 byte version */ 59 | int width; 60 | int height; 61 | unsigned short nplanes; 62 | unsigned short bitspp; 63 | unsigned int compress_type; 64 | unsigned int bmp_bytesz; 65 | int hres; 66 | int vres; 67 | unsigned int ncolors; 68 | unsigned int nimpcolors; 69 | }; 70 | 71 | int load_bmp(const char *fname, struct bmp *b); 72 | 73 | void free_bmp(struct bmp *b); 74 | 75 | #endif /*_BMP_H_*/ 76 | -------------------------------------------------------------------------------- /src/iqa/test/include/hptime.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _HPTIME_H_ 35 | #define _HPTIME_H_ 36 | 37 | /* 38 | * Returns the frequency of the high-performance clock. 39 | * The frequency doesn't vary over time, so it is safe to cache to this value. 40 | */ 41 | unsigned long long hpt_get_frequency(); 42 | 43 | /* 44 | * Returns the current clock value. 45 | */ 46 | unsigned long long hpt_get_time(); 47 | 48 | /* 49 | * Calculates the elapsed time. 50 | * @return The elapsed time in seconds. 51 | */ 52 | double hpt_elapsed_time(const unsigned long long start, const unsigned long long end, const unsigned long long freq); 53 | 54 | #endif /*_HPTIME_H_*/ 55 | -------------------------------------------------------------------------------- /src/iqa/test/include/test_convolve.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _TEST_CONVOLVE_H_ 35 | #define _TEST_CONVOLVE_H_ 36 | 37 | int test_convolve(); 38 | 39 | #endif /*_TEST_CONVOLVE_H_*/ 40 | -------------------------------------------------------------------------------- /src/iqa/test/include/test_decimate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _TEST_DECIMATE_H_ 35 | #define _TEST_DECIMATE_H_ 36 | 37 | int test_decimate(); 38 | 39 | #endif /*_TEST_DECIMATE_H_*/ 40 | -------------------------------------------------------------------------------- /src/iqa/test/include/test_ms_ssim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _TEST_MS_SSIM_H_ 35 | #define _TEST_MS_SSIM_H_ 36 | 37 | int test_ms_ssim(); 38 | 39 | #endif /*_TEST_MS_SSIM_H_*/ 40 | -------------------------------------------------------------------------------- /src/iqa/test/include/test_mse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _TEST_MSE_H_ 35 | #define _TEST_MSE_H_ 36 | 37 | int test_mse(); 38 | 39 | #endif /*_TEST_MSE_H_*/ 40 | -------------------------------------------------------------------------------- /src/iqa/test/include/test_psnr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _TEST_PSNR_H_ 35 | #define _TEST_PSNR_H_ 36 | 37 | int test_psnr(); 38 | 39 | #endif /*_TEST_PSNR_H_*/ 40 | -------------------------------------------------------------------------------- /src/iqa/test/include/test_ssim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef _TEST_SSIM_H_ 35 | #define _TEST_SSIM_H_ 36 | 37 | int test_ssim(); 38 | 39 | #endif /*_TEST_SSIM_H_*/ 40 | -------------------------------------------------------------------------------- /src/iqa/test/octave/README.txt: -------------------------------------------------------------------------------- 1 | The correct unit test values are derived by recreating the image quality 2 | algorithms in Octave (the open-source equivalent of Matlab). To run the scripts 3 | yourself, make sure (1) to set the current directory in Octave to where the 4 | scripts are located, and (2) make sure the path to the test bitmaps in the 5 | 'resources' directory is in Octave's IMAGE_PATH (I do this by running Octave 6 | with the --image-path= argument). 7 | . 8 | -------------------------------------------------------------------------------- /src/iqa/test/octave/ms_ssim.m: -------------------------------------------------------------------------------- 1 | %============================================================================== 2 | % Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | % All rights reserved. 4 | % 5 | % The BSD License 6 | % 7 | % Redistribution and use in source and binary forms, with or without 8 | % modification, are permitted provided that the following conditions are met: 9 | % 10 | % - Redistributions of source code must retain the above copyright notice, 11 | % this list of conditions and the following disclaimer. 12 | % 13 | % - Redistributions in binary form must reproduce the above copyright notice, 14 | % this list of conditions and the following disclaimer in the documentation 15 | % and/or other materials provided with the distribution. 16 | % 17 | % - Neither the name of the tdistler.com nor the names of its contributors may 18 | % be used to endorse or promote products derived from this software without 19 | % specific prior written permission. 20 | % 21 | % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | % AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | % IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | % ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | % LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | % CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | % SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | % INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | % CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | % POSSIBILITY OF SUCH DAMAGE. 32 | %============================================================================== 33 | 34 | function lcs = ssim(img1, img2, K, gauss, a, b, g) 35 | C1 = (K(1)*255)^2; 36 | C2 = (K(2)*255)^2; 37 | C3 = C2/2; 38 | 39 | if (gauss == 1) 40 | window = fspecial('gaussian', 11, 1.5); 41 | else 42 | window = ones(8,8); 43 | end 44 | window = window/sum(sum(window)); 45 | 46 | %Reduce to single precision float. 47 | window = round(window * 1000000.0)/1000000.0; 48 | 49 | mu1 = filter2(window, img1, 'valid'); 50 | mu2 = filter2(window, img2, 'valid'); 51 | mu1_sq = mu1.*mu1; 52 | mu2_sq = mu2.*mu2; 53 | mu1_mu2 = mu1.*mu2; 54 | sigma1_sq = filter2(window, img1.*img1, 'valid') - mu1_sq; 55 | sigma2_sq = filter2(window, img2.*img2, 'valid') - mu2_sq; 56 | sigma12 = filter2(window, img1.*img2, 'valid') - mu1_mu2; 57 | 58 | sigma1 = sigma1_sq.^0.5; 59 | sigma2 = sigma2_sq.^0.5; 60 | 61 | % Use MS-SSIM* if constants are 0 62 | if (C1==0 && C2==0) 63 | index = (mu1_sq + mu2_sq > 0); 64 | lum = ones(size(mu1_sq)); 65 | lum(index) = (2*mu1_mu2(index)) ./ (mu1_sq(index) + mu2_sq(index)); 66 | 67 | index = (sigma1_sq + sigma2_sq > 0); 68 | con = ones(size(mu1_sq)); 69 | con(index) = (2*sigma1(index).*sigma2(index)) ./ (sigma1_sq(index) + sigma2_sq(index)); 70 | 71 | % First set 1 and calculated values. 72 | index = (sigma1 ~= 0) & (sigma2 ~= 0); 73 | stc = ones(size(mu1_sq)); 74 | stc(index) = sigma12(index) ./ (sigma1(index).*sigma2(index)); 75 | % Now set 0 values. 76 | index = ~((sigma1~=0)&(sigma2==0)) | ((sigma1==0)&(sigma2~=0)); 77 | stc = index .* stc; 78 | else 79 | % MS-SSIM (Wang) 80 | lum = (2*mu1_mu2 + C1) ./ (mu1_sq + mu2_sq + C1); 81 | con = (2*sigma1.*sigma2 + C2) ./ (sigma1_sq + sigma2_sq + C2); 82 | stc = (sigma12 + C3) ./ (sigma1.*sigma2 + C3); 83 | end 84 | 85 | [M N] = size(lum); 86 | lum = sum(sum(lum))/(M*N); 87 | con = sum(sum(con))/(M*N); 88 | stc = sum(sum(stc))/(M*N); 89 | lcs = real(lum^a) * real(con^b) * real(stc^g); 90 | end 91 | 92 | 93 | %============================================================================== 94 | % Usage: ms_ssim(img1, img2, wang, gauss, scale, alphas, betas, gammas) 95 | % img1, img2: The images to compare. 96 | % wang: 1 to use original Wang algorithm, 0 to use Rouse/Hemami (MS-SSIM*) 97 | % gauss: 1 for 11x11 Gaussian window, 0 for 8x8 linear window 98 | % scale: Number of scales to use. 5 is default. 99 | % alphas: Alpha values to use at each scale. 100 | % betas: Beta values to use at each scale. 101 | % gammas: Gamma values to use at each scale. 102 | %============================================================================== 103 | function msssim = ms_ssim(img1, img2, wang, gauss, scale, alphas, betas, gammas) 104 | 105 | if (nargin < 2 || nargin > 8) 106 | msssim = -Inf; 107 | return; 108 | end 109 | 110 | if (size(img1) ~= size(img2)) 111 | msssim = -Inf; 112 | return; 113 | end 114 | 115 | if (nargin < 3) 116 | wang = 0; 117 | gauss = 1; 118 | scale = 5; 119 | end 120 | if (nargin < 4) 121 | gauss = 1; 122 | scale = 5; 123 | end 124 | if (nargin < 5) 125 | scale = 5; 126 | alphas = [0.0 0.0 0.0 0.0 0.1333]; 127 | betas = [0.0448 0.2856 0.3001 0.2363 0.1333]; 128 | gammas = [0.0448 0.2856 0.3001 0.2363 0.1333]; 129 | end 130 | 131 | [M N] = size(img1); 132 | img1 = double(img1); 133 | img2 = double(img2); 134 | 135 | % Filter for downsampling. 136 | lod = [0.0378 -0.0238 -0.1106 0.3774 0.8527 0.3774 -0.1106 -0.0238 0.0378]'; 137 | lpf = lod * lod'; 138 | lpf = lpf/sum(sum(lpf)); 139 | 140 | % Run SSIM on each scale. 141 | msssim = 1; 142 | for n = 1:scale 143 | if (wang == 1) 144 | msssim = msssim * ssim(img1,img2,[0.01 0.03],gauss,alphas(n),betas(n),gammas(n)); 145 | else 146 | msssim = msssim * ssim(img1,img2,[0 0],gauss,alphas(n),betas(n),gammas(n)); 147 | end 148 | 149 | % Downsample 150 | img1 = imfilter(img1,lpf,'symmetric','same'); 151 | img2 = imfilter(img2,lpf,'symmetric','same'); 152 | img1 = img1(1:2:end,1:2:end); 153 | img2 = img2(1:2:end,1:2:end); 154 | end 155 | 156 | return; -------------------------------------------------------------------------------- /src/iqa/test/octave/ssim_single.m: -------------------------------------------------------------------------------- 1 | % EDIT: Modified for IQA for single precision floating point (Matlab/Octave 2 | % default to double precision). 3 | 4 | function [mssim, ssim_map] = ssim_single(img1, img2, K, window, L, a, b, g) 5 | 6 | % ======================================================================== 7 | % SSIM Index with automatic downsampling, Version 1.0 8 | % Copyright(c) 2009 Zhou Wang 9 | % All Rights Reserved. 10 | % 11 | % ---------------------------------------------------------------------- 12 | % Permission to use, copy, or modify this software and its documentation 13 | % for educational and research purposes only and without fee is hereby 14 | % granted, provided that this copyright notice and the original authors' 15 | % names appear on all copies and supporting documentation. This program 16 | % shall not be used, rewritten, or adapted as the basis of a commercial 17 | % software or hardware product without first obtaining permission of the 18 | % authors. The authors make no representations about the suitability of 19 | % this software for any purpose. It is provided "as is" without express 20 | % or implied warranty. 21 | %---------------------------------------------------------------------- 22 | % 23 | % This is an implementation of the algorithm for calculating the 24 | % Structural SIMilarity (SSIM) index between two images 25 | % 26 | % Please refer to the following paper and the website with suggested usage 27 | % 28 | % Z. Wang, A. C. Bovik, H. R. Sheikh, and E. P. Simoncelli, "Image 29 | % quality assessment: From error visibility to structural similarity," 30 | % IEEE Transactios on Image Processing, vol. 13, no. 4, pp. 600-612, 31 | % Apr. 2004. 32 | % 33 | % http://www.ece.uwaterloo.ca/~z70wang/research/ssim/ 34 | % 35 | % Note: This program is different from ssim_index.m, where no automatic 36 | % downsampling is performed. (downsampling was done in the above paper 37 | % and was described as suggested usage in the above website.) 38 | % 39 | % Kindly report any suggestions or corrections to zhouwang@ieee.org 40 | % 41 | %---------------------------------------------------------------------- 42 | % 43 | %Input : (1) img1: the first image being compared 44 | % (2) img2: the second image being compared 45 | % (3) K: constants in the SSIM index formula (see the above 46 | % reference). defualt value: K = [0.01 0.03] 47 | % (4) window: local window for statistics (see the above 48 | % reference). default widnow is Gaussian given by 49 | % window = fspecial('gaussian', 11, 1.5); 50 | % (5) L: dynamic range of the images. default: L = 255 51 | % 52 | %Output: (1) mssim: the mean SSIM index value between 2 images. 53 | % If one of the images being compared is regarded as 54 | % perfect quality, then mssim can be considered as the 55 | % quality measure of the other image. 56 | % If img1 = img2, then mssim = 1. 57 | % (2) ssim_map: the SSIM index map of the test image. The map 58 | % has a smaller size than the input images. The actual size 59 | % depends on the window size and the downsampling factor. 60 | % 61 | %Basic Usage: 62 | % Given 2 test images img1 and img2, whose dynamic range is 0-255 63 | % 64 | % [mssim, ssim_map] = ssim(img1, img2); 65 | % 66 | %Advanced Usage: 67 | % User defined parameters. For example 68 | % 69 | % K = [0.05 0.05]; 70 | % window = ones(8); 71 | % L = 100; 72 | % [mssim, ssim_map] = ssim(img1, img2, K, window, L); 73 | % 74 | %Visualize the results: 75 | % 76 | % mssim %Gives the mssim value 77 | % imshow(max(0, ssim_map).^4) %Shows the SSIM index map 78 | %======================================================================== 79 | 80 | 81 | if (nargin < 2 || nargin > 8) 82 | mssim = -Inf; 83 | ssim_map = -Inf; 84 | return; 85 | end 86 | 87 | if (size(img1) ~= size(img2)) 88 | mssim = -Inf; 89 | ssim_map = -Inf; 90 | return; 91 | end 92 | 93 | [M N] = size(img1); 94 | 95 | if (nargin == 2) 96 | if ((M < 11) || (N < 11)) 97 | mssim = -Inf; 98 | ssim_map = -Inf; 99 | return 100 | end 101 | window = fspecial('gaussian', 11, 1.5); 102 | K(1) = 0.01; 103 | K(2) = 0.03; 104 | L = 255; 105 | end 106 | 107 | if (nargin == 3) 108 | if ((M < 11) || (N < 11)) 109 | mssim = -Inf; 110 | ssim_map = -Inf; 111 | return 112 | end 113 | window = fspecial('gaussian', 11, 1.5); 114 | L = 255; 115 | if (length(K) == 2) 116 | if (K(1) < 0 || K(2) < 0) 117 | mssim = -Inf; 118 | ssim_map = -Inf; 119 | return; 120 | end 121 | else 122 | mssim = -Inf; 123 | ssim_map = -Inf; 124 | return; 125 | end 126 | end 127 | 128 | if (nargin == 4) 129 | [H W] = size(window); 130 | if ((H*W) < 4 || (H > M) || (W > N)) 131 | mssim = -Inf; 132 | ssim_map = -Inf; 133 | return 134 | end 135 | L = 255; 136 | if (length(K) == 2) 137 | if (K(1) < 0 || K(2) < 0) 138 | mssim = -Inf; 139 | ssim_map = -Inf; 140 | return; 141 | end 142 | else 143 | mssim = -Inf; 144 | ssim_map = -Inf; 145 | return; 146 | end 147 | end 148 | 149 | if (nargin == 5) 150 | [H W] = size(window); 151 | if ((H*W) < 4 || (H > M) || (W > N)) 152 | mssim = -Inf; 153 | ssim_map = -Inf; 154 | return 155 | end 156 | if (length(K) == 2) 157 | if (K(1) < 0 || K(2) < 0) 158 | mssim = -Inf; 159 | ssim_map = -Inf; 160 | return; 161 | end 162 | else 163 | mssim = -Inf; 164 | ssim_map = -Inf; 165 | return; 166 | end 167 | end 168 | 169 | %EDIT: Added support for 8 args 170 | if (nargin == 8) 171 | [H W] = size(window); 172 | if ((H*W) < 4 || (H > M) || (W > N)) 173 | mssim = -Inf; 174 | ssim_map = -Inf; 175 | return 176 | end 177 | if (length(K) == 2) 178 | if (K(1) < 0 || K(2) < 0) 179 | mssim = -Inf; 180 | ssim_map = -Inf; 181 | return; 182 | end 183 | else 184 | mssim = -Inf; 185 | ssim_map = -Inf; 186 | return; 187 | end 188 | end 189 | 190 | if (nargin ~= 8) 191 | a = 1.0; 192 | b = 1.0; 193 | g = 1.0; 194 | end 195 | 196 | 197 | img1 = double(img1); 198 | img2 = double(img2); 199 | 200 | % automatic downsampling 201 | f = max(1,round(min(M,N)/256)); 202 | %downsampling by f 203 | %use a simple low-pass filter 204 | if(f>1) 205 | lpf = ones(f,f); 206 | lpf = lpf/sum(lpf(:)); 207 | img1 = imfilter(img1,lpf,'symmetric','same'); 208 | img2 = imfilter(img2,lpf,'symmetric','same'); 209 | 210 | img1 = img1(1:f:end,1:f:end); 211 | img2 = img2(1:f:end,1:f:end); 212 | end 213 | 214 | C1 = (K(1)*L)^2; 215 | C2 = (K(2)*L)^2; 216 | C3 = C2/2; 217 | window = window/sum(sum(window)); 218 | 219 | %EDIT: Reduce to single precision float. 220 | window = round(window * 1000000.0)/1000000.0; 221 | 222 | mu1 = filter2(window, img1, 'valid'); 223 | mu2 = filter2(window, img2, 'valid'); 224 | mu1_sq = mu1.*mu1; 225 | mu2_sq = mu2.*mu2; 226 | mu1_mu2 = mu1.*mu2; 227 | sigma1_sq = filter2(window, img1.*img1, 'valid') - mu1_sq; 228 | sigma2_sq = filter2(window, img2.*img2, 'valid') - mu2_sq; 229 | sigma12 = filter2(window, img1.*img2, 'valid') - mu1_mu2; 230 | 231 | if (nargin < 8 && C1 > 0 && C2 > 0) 232 | ssim_map = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))./((mu1_sq + mu2_sq + C1).*(sigma1_sq + sigma2_sq + C2)); 233 | %EDIT: Added support for alpha, beta, gamma 234 | elseif (nargin == 8) 235 | sigma1 = sigma1_sq.^0.5; 236 | sigma2 = sigma2_sq.^0.5; 237 | lum = (2*mu1_mu2 + C1) ./ (mu1_sq + mu2_sq + C1); 238 | con = (2*sigma1.*sigma2 + C2) ./ (sigma1_sq + sigma2_sq + C2); 239 | stc = (sigma12 + C3) ./ (sigma1.*sigma2 + C3); 240 | [M,N] = size(lum); 241 | aa = repmat(a,M,N); 242 | bb = repmat(b,M,N); 243 | gg = repmat(g,M,N); 244 | ssim_map = real(lum.^aa) .* real(con.^bb) .* real(stc.^gg); 245 | else 246 | numerator1 = 2*mu1_mu2 + C1; 247 | numerator2 = 2*sigma12 + C2; 248 | denominator1 = mu1_sq + mu2_sq + C1; 249 | denominator2 = sigma1_sq + sigma2_sq + C2; 250 | ssim_map = ones(size(mu1)); 251 | index = (denominator1.*denominator2 > 0); 252 | ssim_map(index) = (numerator1(index).*numerator2(index))./(denominator1(index).*denominator2(index)); 253 | index = (denominator1 ~= 0) & (denominator2 == 0); 254 | ssim_map(index) = numerator1(index)./denominator1(index); 255 | end 256 | 257 | mssim = mean2(ssim_map); 258 | 259 | return -------------------------------------------------------------------------------- /src/iqa/test/octave/test_convolve.m: -------------------------------------------------------------------------------- 1 | % This file is used to generate the correct output data for the iqa_convolve and 2 | % iqa_img_filter tests. 3 | 4 | img_1x1 = [128]; 5 | img_2x2 = [255 0; 0 255]; 6 | img_4x3 = [255 128 64 0; 128 64 0 255; 64 0 255 128]; 7 | img_4x4 = [255 128 64 0; 128 64 0 255; 64 0 255 128; 0 255 128 64]; 8 | 9 | kernel_1x1 = ones(1); 10 | kernel_2x2 = ones(2); 11 | kernel_3x3 = ones(3); 12 | 13 | % normalize 14 | kernel_2x2 = kernel_2x2/sum(kernel_2x2(:)); 15 | kernel_3x3 = kernel_3x3/sum(kernel_3x3(:)); 16 | 17 | disp("\n--- CONVOLVE ---"); 18 | disp("\n1x1 image with 1x1 kernel:"); 19 | filter2(kernel_1x1, img_1x1, 'valid') 20 | 21 | disp("\n2x2 image with 1x1 kernel:"); 22 | filter2(kernel_1x1, img_2x2, 'valid') 23 | 24 | disp("\n2x2 image with 3x3 kernel:"); 25 | filter2(kernel_3x3, img_2x2, 'valid') 26 | 27 | disp("\n4x3 image with 2x2 kernel:"); 28 | filter2(kernel_2x2, img_4x3, 'valid') 29 | 30 | disp("\n4x4 image with 3x3 kernel:"); 31 | filter2(kernel_3x3, img_4x4, 'valid') 32 | 33 | 34 | disp("\n--- IMG_FILTER ---"); 35 | disp("\n1x1 image with 1x1 kernel:"); 36 | imfilter(img_1x1, kernel_1x1, 'symmetric', 'same') 37 | 38 | disp("\n2x2 image with 1x1 kernel:"); 39 | imfilter(img_2x2, kernel_1x1, 'symmetric', 'same') 40 | 41 | disp("\n4x3 image with 2x2 kernel:"); 42 | imfilter(img_4x3, kernel_2x2, 'symmetric', 'same') 43 | 44 | disp("\n4x4 image with 3x3 kernel:"); 45 | imfilter(img_4x4, kernel_3x3, 'symmetric', 'same') 46 | -------------------------------------------------------------------------------- /src/iqa/test/octave/test_decimate.m: -------------------------------------------------------------------------------- 1 | % This file is used to generate the correct output data for the iqa_decimate 2 | % tests. 3 | 4 | img_4x4 = [255 128 64 0; 128 64 0 255; 64 0 255 128; 0 255 128 64]; 5 | img_5x5 = [0 1 2 3 5; 73 79 83 89 97; 127 131 137 139 149; 179 181 191 193 197; 233 239 241 251 255]; 6 | 7 | lpf_avg_2x2 = ones(2); 8 | lpf_gaussian_3x3 = [0.07511 0.12384 0.07511; 0.12384 0.20418 0.12384; 0.07511 0.12384 0.07511]; % normalized 9 | 10 | % normalize 11 | lpf_avg_2x2 = lpf_avg_2x2/sum(lpf_avg_2x2(:)); 12 | 13 | disp("\n--- DECIMATE ---\n"); 14 | 15 | lpf_avg_2x2 16 | lpf_gaussian_3x3 17 | 18 | img_4x4 19 | 20 | disp("\n4x4 image with linear 2x2 kernel and 2x factor:"); 21 | img = imfilter(img_4x4, lpf_avg_2x2, 'symmetric', 'same'); 22 | img(1:2:end,1:2:end) 23 | 24 | disp("\n4x4 image with gaussian 3x3 kernel and 2x factor:"); 25 | img = imfilter(img_4x4, lpf_gaussian_3x3, 'symmetric', 'same'); 26 | img(1:2:end,1:2:end) 27 | 28 | img_5x5 29 | 30 | disp("\n5x5 image with linear 2x2 kernel and 2x factor:"); 31 | img = imfilter(img_5x5, lpf_avg_2x2, 'symmetric', 'same'); 32 | img(1:2:end,1:2:end) 33 | 34 | disp("\n5x5 image with gaussian 3x3 kernel and 2x factor:"); 35 | img = imfilter(img_5x5, lpf_gaussian_3x3, 'symmetric', 'same'); 36 | img(1:2:end,1:2:end) 37 | 38 | disp("\n5x5 image with gaussian 3x3 kernel and 3x factor:"); 39 | img = imfilter(img_5x5, lpf_gaussian_3x3, 'symmetric', 'same'); 40 | img(1:3:end,1:3:end) 41 | 42 | -------------------------------------------------------------------------------- /src/iqa/test/octave/test_ms_ssim.m: -------------------------------------------------------------------------------- 1 | % This file is used to generate the correct output data for the 2 | % iqa_ms_ssim tests. 3 | 4 | source('ms_ssim.m'); 5 | 6 | 7 | img_orig = imread('einstein.bmp'); 8 | img_blur = imread('blur.bmp'); 9 | img_contrast = imread('contrast.bmp'); 10 | img_flipvert = imread('flipvertical.bmp'); 11 | img_impulse = imread('impulse.bmp'); 12 | img_jpg = imread('jpg.bmp'); 13 | img_meanshift = imread('meanshift.bmp'); 14 | img_cr_orig = imread('Courtright.bmp'); 15 | img_cr_noise = imread('Courtright_Noise.bmp'); 16 | 17 | 18 | disp("\n\n--- MS-SSIM Rouse/Hemami --\n"); 19 | 20 | disp("\nEinstein (identical):"); 21 | mssim = ms_ssim(img_orig, img_orig, 0) 22 | 23 | disp("\nEinstein (blur):"); 24 | mssim = ms_ssim(img_orig, img_blur, 0) 25 | 26 | disp("\nEinstein (contrast):"); 27 | mssim = ms_ssim(img_orig, img_contrast, 0) 28 | 29 | disp("\nEinstein (flip vertical):"); 30 | mssim = ms_ssim(img_orig, img_flipvert, 0) 31 | 32 | disp("\nEinstein (impulse):"); 33 | mssim = ms_ssim(img_orig, img_impulse, 0) 34 | 35 | disp("\nEinstein (jpg):"); 36 | mssim = ms_ssim(img_orig, img_jpg, 0) 37 | 38 | disp("\nEinstein (meanshift):"); 39 | mssim = ms_ssim(img_orig, img_meanshift, 0) 40 | 41 | disp("\nCourtright (identical):"); 42 | mssim = ms_ssim(img_cr_orig, img_cr_orig, 0) 43 | 44 | disp("\nCourtright (noise):"); 45 | mssim = ms_ssim(img_cr_orig, img_cr_noise, 0) 46 | 47 | 48 | disp("\n\n--- MS-SSIM Wang --\n"); 49 | 50 | disp("\nEinstein (identical):"); 51 | mssim = ms_ssim(img_orig, img_orig, 1) 52 | 53 | disp("\nEinstein (blur):"); 54 | mssim = ms_ssim(img_orig, img_blur, 1) 55 | 56 | disp("\nEinstein (contrast):"); 57 | mssim = ms_ssim(img_orig, img_contrast, 1) 58 | 59 | disp("\nEinstein (flip vertical):"); 60 | mssim = ms_ssim(img_orig, img_flipvert, 1) 61 | 62 | disp("\nEinstein (impulse):"); 63 | mssim = ms_ssim(img_orig, img_impulse, 1) 64 | 65 | disp("\nEinstein (jpg):"); 66 | mssim = ms_ssim(img_orig, img_jpg, 1) 67 | 68 | disp("\nEinstein (meanshift):"); 69 | mssim = ms_ssim(img_orig, img_meanshift, 1) 70 | 71 | 72 | disp("\n\n--- MS-SSIM Linear Window (R/H) --\n"); 73 | 74 | disp("\nEinstein (identical):"); 75 | mssim = ms_ssim(img_orig, img_orig, 0, 0) 76 | 77 | disp("\nEinstein (blur):"); 78 | mssim = ms_ssim(img_orig, img_blur, 0, 0) 79 | 80 | disp("\nEinstein (contrast):"); 81 | mssim = ms_ssim(img_orig, img_contrast, 0, 0) 82 | 83 | disp("\nEinstein (flip vertical):"); 84 | mssim = ms_ssim(img_orig, img_flipvert, 0, 0) 85 | 86 | disp("\nEinstein (impulse):"); 87 | mssim = ms_ssim(img_orig, img_impulse, 0, 0) 88 | 89 | disp("\nEinstein (jpg):"); 90 | mssim = ms_ssim(img_orig, img_jpg, 0, 0) 91 | 92 | disp("\nEinstein (meanshift):"); 93 | mssim = ms_ssim(img_orig, img_meanshift, 0, 0) 94 | 95 | 96 | disp("\n\n--- MS-SSIM Scale=4 --\n"); 97 | scale = 4; 98 | a = [0 0 0 0.25]; 99 | b = [0.25 0.25 0.25 0.25]; 100 | g = [0.25 0.25 0.25 0.25]; 101 | 102 | disp("\nEinstein (identical):"); 103 | mssim = ms_ssim(img_orig, img_orig, 0, 1, scale, a, b, g) 104 | 105 | disp("\nEinstein (blur):"); 106 | mssim = ms_ssim(img_orig, img_blur, 0, 1, scale, a, b, g) 107 | 108 | disp("\nEinstein (contrast):"); 109 | mssim = ms_ssim(img_orig, img_contrast, 0, 1, scale, a, b, g) 110 | 111 | disp("\nEinstein (flip vertical):"); 112 | mssim = ms_ssim(img_orig, img_flipvert, 0, 1, scale, a, b, g) 113 | 114 | disp("\nEinstein (impulse):"); 115 | mssim = ms_ssim(img_orig, img_impulse, 0, 1, scale, a, b, g) 116 | 117 | disp("\nEinstein (jpg):"); 118 | mssim = ms_ssim(img_orig, img_jpg, 0, 1, scale, a, b, g) 119 | 120 | disp("\nEinstein (meanshift):"); 121 | mssim = ms_ssim(img_orig, img_meanshift, 0, 1, scale, a, b, g) -------------------------------------------------------------------------------- /src/iqa/test/octave/test_mse.m: -------------------------------------------------------------------------------- 1 | % This file is used to generate the correct output data for the iqa_mse tests. 2 | 3 | img_1x1 = [128]; 4 | img_2x2 = [0 128 192 255]; 5 | 6 | function mse = _mse(img1, img2) 7 | mse = 0; 8 | for idx = 1:numel(img1) 9 | mse = mse + ((img1(idx) - img2(idx))^2); 10 | end 11 | mse = mse/numel(img1); 12 | endfunction 13 | 14 | img_1x1 15 | img_2x2 16 | 17 | disp("\n1x1 Identical:"); 18 | _mse(img_1x1,img_1x1) 19 | 20 | disp("\n1x1 Different:"); 21 | img_1x1_mod = img_1x1 + [8]; 22 | _mse(img_1x1,img_1x1_mod) 23 | 24 | disp("\n2x2 Identical:"); 25 | _mse(img_2x2,img_2x2) 26 | 27 | disp("\n2x2 Different:"); 28 | img_2x2_mod = img_2x2; 29 | img_2x2_mod(3) -= 13; 30 | _mse(img_2x2,img_2x2_mod) 31 | -------------------------------------------------------------------------------- /src/iqa/test/octave/test_psnr.m: -------------------------------------------------------------------------------- 1 | % This file is used to generate the correct output data for the iqa_psnr tests. 2 | 3 | img_1x1 = [128]; 4 | img_2x2 = [0 128 192 255]; 5 | 6 | function mse = _mse(img1, img2) 7 | mse = 0; 8 | for idx = 1:numel(img1) 9 | mse = mse + ((img1(idx) - img2(idx))^2); 10 | end 11 | mse = mse/numel(img1); 12 | endfunction 13 | 14 | function psnr = _psnr(img1, img2) 15 | L = 255; 16 | mse = _mse(img1,img2); 17 | psnr = 10 * log10( (L^2)/mse ); 18 | endfunction 19 | 20 | img_1x1 21 | img_2x2 22 | 23 | disp("\n1x1 Identical:"); 24 | _psnr(img_1x1,img_1x1) 25 | 26 | disp("\n1x1 Different:"); 27 | img_1x1_mod = img_1x1 + [8]; 28 | _psnr(img_1x1,img_1x1_mod) 29 | 30 | disp("\n2x2 Identical:"); 31 | _psnr(img_2x2,img_2x2) 32 | 33 | disp("\n2x2 Different:"); 34 | img_2x2_mod = img_2x2; 35 | img_2x2_mod(3) -= 19; 36 | _psnr(img_2x2,img_2x2_mod) 37 | -------------------------------------------------------------------------------- /src/iqa/test/octave/test_ssim.m: -------------------------------------------------------------------------------- 1 | % This file is used to generate the correct output data for the iqa_ssim 2 | % tests. 3 | 4 | source('ssim_single.m'); 5 | 6 | % 4CIF 704x480 scaled down 32x 7 | img_22x15 = [ 8 | 27 25 83 56 139 147 119 153 147 132 113 147 160 163 169 151 148 120 113 149 132 145; 9 | 0 0 25 25 73 49 148 145 126 127 113 110 116 102 99 118 161 149 149 129 114 137; 10 | 136 154 0 0 24 29 76 63 143 137 114 117 116 117 147 142 119 98 163 139 147 137; 11 | 114 117 141 147 0 0 23 23 66 51 144 144 102 95 104 130 147 161 129 125 85 131; 12 | 97 111 119 107 132 144 0 0 24 26 36 104 135 147 110 136 101 163 170 172 150 124; 13 | 122 135 118 85 116 120 136 147 0 0 24 21 30 51 148 151 120 33 66 79 164 95; 14 | 50 132 130 76 117 142 120 130 137 143 0 0 25 24 48 50 147 150 145 43 120 147; 15 | 170 136 169 155 105 132 43 118 120 143 141 153 0 0 23 23 44 46 144 158 135 149; 16 | 110 94 122 114 149 136 74 57 92 104 99 149 166 166 0 0 25 27 61 46 143 163; 17 | 137 151 132 64 163 174 145 74 205 91 101 88 94 95 97 95 0 0 21 26 150 39; 18 | 149 75 89 108 111 98 19 21 80 229 26 94 100 102 106 94 110 101 0 0 21 24; 19 | 161 38 99 110 95 112 126 94 89 63 108 228 119 111 102 99 97 105 83 99 0 0; 20 | 24 21 155 81 112 93 71 104 111 95 111 235 64 228 105 110 113 108 98 105 112 112; 21 | 0 0 23 20 61 92 91 69 74 107 97 98 99 117 66 92 104 104 106 106 101 74; 22 | 86 83 0 0 21 88 83 92 95 86 94 93 86 111 98 106 86 118 110 108 113 107 23 | ]; 24 | 25 | img_orig = imread('einstein.bmp'); 26 | img_blur = imread('blur.bmp'); 27 | img_contrast = imread('contrast.bmp'); 28 | img_flipvert = imread('flipvertical.bmp'); 29 | img_impulse = imread('impulse.bmp'); 30 | img_jpg = imread('jpg.bmp'); 31 | img_meanshift = imread('meanshift.bmp'); 32 | img_cr_orig = imread('Courtright.bmp'); 33 | img_cr_noise = imread('Courtright_Noise.bmp'); 34 | 35 | 36 | disp("\n--- 11x11 Gaussian sampling --\n"); 37 | disp("\n22x15 Identical:"); 38 | mssim = ssim_single(img_22x15, img_22x15) 39 | 40 | disp("\n22x15 Different (mean shift +7):"); 41 | img_22x15_mod = img_22x15 + [7]; 42 | mssim = ssim_single(img_22x15, img_22x15_mod) 43 | 44 | disp("\n22x15 Different (2x2 low-pass):"); 45 | lpf = ones(2,2); 46 | lpf = lpf/sum(lpf(:)); 47 | img_22x15_mod = imfilter(img_22x15, lpf, 'symmetric', 'same'); 48 | img_22x15_mod = round(img_22x15_mod); 49 | mssim = ssim_single(img_22x15, img_22x15_mod) 50 | 51 | disp("\nEinstein (identical):"); 52 | mssim = ssim_single(img_orig, img_orig) 53 | 54 | disp("\nEinstein (blur):"); 55 | mssim = ssim_single(img_orig, img_blur) 56 | 57 | disp("\nEinstein (contrast):"); 58 | mssim = ssim_single(img_orig, img_contrast) 59 | 60 | disp("\nEinstein (flip vertical):"); 61 | mssim = ssim_single(img_orig, img_flipvert) 62 | 63 | disp("\nEinstein (impulse):"); 64 | mssim = ssim_single(img_orig, img_impulse) 65 | 66 | disp("\nEinstein (jpg):"); 67 | mssim = ssim_single(img_orig, img_jpg) 68 | 69 | disp("\nEinstein (meanshift):"); 70 | mssim = ssim_single(img_orig, img_meanshift) 71 | 72 | disp("\nCourtright (identical):"); 73 | mssim = ssim_single(img_cr_orig, img_cr_orig) 74 | 75 | disp("\nCourtright (noise):"); 76 | mssim = ssim_single(img_cr_orig, img_cr_noise) 77 | 78 | 79 | disp("\n--- 8x8 Linear sampling --\n"); 80 | K = [0.01 0.03]; 81 | window = ones(8); 82 | L = 255; 83 | 84 | disp("\n22x15 Identical:"); 85 | mssim = ssim_single(img_22x15, img_22x15, K, window, L) 86 | 87 | disp("\n22x15 Different (mean shift +7):"); 88 | img_22x15_mod = img_22x15 + [7]; 89 | mssim = ssim_single(img_22x15, img_22x15_mod, K, window, L) 90 | 91 | disp("\n22x15 Different (2x2 low-pass):"); 92 | lpf = ones(2,2); 93 | lpf = lpf/sum(lpf(:)); 94 | img_22x15_mod = imfilter(img_22x15, lpf, 'symmetric', 'same'); 95 | img_22x15_mod = round(img_22x15_mod); 96 | mssim = ssim_single(img_22x15, img_22x15_mod, K, window, L) 97 | 98 | disp("\nEinstein (identical):"); 99 | mssim = ssim_single(img_orig, img_orig, K, window, L) 100 | 101 | disp("\nEinstein (blur):"); 102 | mssim = ssim_single(img_orig, img_blur, K, window, L) 103 | 104 | disp("\nEinstein (contrast):"); 105 | mssim = ssim_single(img_orig, img_contrast, K, window, L) 106 | 107 | disp("\nEinstein (flip vertical):"); 108 | mssim = ssim_single(img_orig, img_flipvert, K, window, L) 109 | 110 | disp("\nEinstein (impulse):"); 111 | mssim = ssim_single(img_orig, img_impulse, K, window, L) 112 | 113 | disp("\nEinstein (jpg):"); 114 | mssim = ssim_single(img_orig, img_jpg, K, window, L) 115 | 116 | disp("\nEinstein (meanshift):"); 117 | mssim = ssim_single(img_orig, img_meanshift, K, window, L) 118 | 119 | 120 | disp("\n--- SIMM with modified parameters --\n"); 121 | K = [0.025987 0.0173]; 122 | window = fspecial('gaussian', 11, 1.5); 123 | L = 187; 124 | a = 0.39; 125 | b = 0.731; 126 | g = 1.12; 127 | 128 | disp("\n22x15 Identical:"); 129 | mssim = ssim_single(img_22x15, img_22x15, K, window, L, a, b, g) 130 | 131 | disp("\n22x15 Different (mean shift +7):"); 132 | img_22x15_mod = img_22x15 + [7]; 133 | mssim = ssim_single(img_22x15, img_22x15_mod, K, window, L, a, b, g) 134 | 135 | disp("\n22x15 Different (2x2 low-pass):"); 136 | lpf = ones(2,2); 137 | lpf = lpf/sum(lpf(:)); 138 | img_22x15_mod = imfilter(img_22x15, lpf, 'symmetric', 'same'); 139 | img_22x15_mod = round(img_22x15_mod); 140 | mssim = ssim_single(img_22x15, img_22x15_mod, K, window, L, a, b, g) 141 | 142 | disp("\nEinstein (identical):"); 143 | mssim = ssim_single(img_orig, img_orig, K, window, L, a, b, g) 144 | 145 | disp("\nEinstein (blur):"); 146 | mssim = ssim_single(img_orig, img_blur, K, window, L, a, b, g) 147 | 148 | disp("\nEinstein (contrast):"); 149 | mssim = ssim_single(img_orig, img_contrast, K, window, L, a, b, g) 150 | 151 | disp("\nEinstein (flip vertical):"); 152 | mssim = ssim_single(img_orig, img_flipvert, K, window, L, a, b, g) 153 | 154 | disp("\nEinstein (impulse):"); 155 | mssim = ssim_single(img_orig, img_impulse, K, window, L, a, b, g) 156 | 157 | disp("\nEinstein (jpg):"); 158 | mssim = ssim_single(img_orig, img_jpg, K, window, L, a, b, g) 159 | 160 | disp("\nEinstein (meanshift):"); 161 | mssim = ssim_single(img_orig, img_meanshift, K, window, L, a, b, g) 162 | 163 | -------------------------------------------------------------------------------- /src/iqa/test/resources/Courtright.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/Courtright.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/Courtright_Noise.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/Courtright_Noise.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/blur.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/blur.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/contrast.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/contrast.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/einstein.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/einstein.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/flipvertical.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/flipvertical.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/impulse.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/impulse.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/jpg.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/jpg.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/meanshift.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/meanshift.bmp -------------------------------------------------------------------------------- /src/iqa/test/resources/skate_480x360.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielgtaylor/jpeg-archive/5ae3e1187937227e8d90329fdd8334960f74c825/src/iqa/test/resources/skate_480x360.bmp -------------------------------------------------------------------------------- /src/iqa/test/source/bmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include "bmp.h" 38 | 39 | int load_bmp(const char *fname, struct bmp *b) 40 | { 41 | long len=0; 42 | unsigned char *hdr=0; 43 | FILE *file; 44 | int row; 45 | unsigned char *flip, *orig, *btm; 46 | 47 | file = fopen(fname, "rb"); 48 | if (!file) 49 | return 1; 50 | 51 | fseek(file, 0, SEEK_END); 52 | len = ftell(file); 53 | fseek(file, 0, SEEK_SET); 54 | 55 | b->raw = (unsigned char*)malloc(len); 56 | if (!b->raw) { 57 | fclose(file); 58 | return 2; 59 | } 60 | 61 | if (len != fread(b->raw, 1, len, file)) { 62 | free(b->raw); 63 | fclose(file); 64 | return 3; 65 | } 66 | fclose(file); 67 | 68 | /* Parse the headers */ 69 | hdr = b->raw + sizeof(struct bmp_magic); 70 | orig = b->raw + ((struct bmp_header*)hdr)->bmp_offset; 71 | hdr += sizeof(struct bmp_header); 72 | b->w = ((struct bmp_info_hdr*)hdr)->width; 73 | b->h = ((struct bmp_info_hdr*)hdr)->height; 74 | b->bit_depth = ((struct bmp_info_hdr*)hdr)->bitspp; 75 | b->stride = (b->w * (b->bit_depth/8) + 3) & ~3; 76 | 77 | /* Bitmaps are stored bottom-up... so flip. */ 78 | flip = b->img = (unsigned char*)malloc(b->h * b->stride); 79 | if (!b->img) { 80 | free_bmp(b); 81 | return 4; 82 | } 83 | btm = orig + ((b->h - 1) * b->stride); /* Point to bottom row */ 84 | for (row=0; row < b->h; ++row) { 85 | memcpy(flip, btm, b->stride); 86 | flip += b->stride; 87 | btm -= b->stride; 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | void free_bmp(struct bmp *b) 94 | { 95 | if (b) { 96 | if (b->raw) 97 | free(b->raw); 98 | if (b->img) 99 | free(b->img); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/iqa/test/source/hptime.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "hptime.h" 35 | 36 | #ifdef WIN32 37 | #include 38 | #else 39 | #include 40 | #define HPT_CLOCK_TYPE CLOCK_MONOTONIC 41 | #define HPT_NANO_SEC 1000000000 42 | #endif 43 | 44 | unsigned long long hpt_get_frequency() 45 | { 46 | unsigned long long freq; 47 | 48 | #ifdef WIN32 49 | QueryPerformanceFrequency((LARGE_INTEGER*)&freq); 50 | #else 51 | struct timespec cfreq; 52 | clock_getres(HPT_CLOCK_TYPE, &cfreq); 53 | /* 1/tick-duration converts time-between-ticks to ticks-per-second. */ 54 | freq = (unsigned long long)( 1.0 / ((double)cfreq.tv_sec + ((double)cfreq.tv_nsec / (double)HPT_NANO_SEC)) ); 55 | #endif 56 | 57 | return freq; 58 | } 59 | 60 | unsigned long long hpt_get_time() 61 | { 62 | unsigned long long now; 63 | 64 | #ifdef WIN32 65 | QueryPerformanceCounter((LARGE_INTEGER*)&now); 66 | #else 67 | struct timespec cnow; 68 | clock_gettime(HPT_CLOCK_TYPE, &cnow); 69 | /* Convert to nanosecond ticks. */ 70 | now = (unsigned long long)cnow.tv_sec; 71 | now *= HPT_NANO_SEC; 72 | now += cnow.tv_nsec; 73 | #endif 74 | 75 | return now; 76 | } 77 | 78 | double hpt_elapsed_time(const unsigned long long start, const unsigned long long end, const unsigned long long freq) 79 | { 80 | return (double)(end - start) / (double)freq; 81 | } 82 | -------------------------------------------------------------------------------- /src/iqa/test/source/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "test_convolve.h" 35 | #include "test_decimate.h" 36 | #include "test_mse.h" 37 | #include "test_psnr.h" 38 | #include "test_ssim.h" 39 | #include "test_ms_ssim.h" 40 | #include 41 | 42 | int main() 43 | { 44 | int failures=0; 45 | 46 | printf("\n"); 47 | failures += test_convolve(); 48 | failures += test_decimate(); 49 | failures += test_mse(); 50 | failures += test_psnr(); 51 | failures += test_ssim(); 52 | failures += test_ms_ssim(); 53 | 54 | if (failures) 55 | printf("\n\nRESULT: *** FAIL (%i) ***\n\n", failures); 56 | else 57 | printf("\n\nRESULT: Success\n\n"); 58 | 59 | return failures; 60 | } 61 | -------------------------------------------------------------------------------- /src/iqa/test/source/test_decimate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "decimate.h" 35 | #include "test_decimate.h" 36 | #include "math_utils.h" 37 | #include 38 | #include 39 | 40 | static float lpf_avg_2x2[] = { 41 | 0.25f, 0.25f, 42 | 0.25f, 0.25f 43 | }; 44 | static float lpf_gaussian_3x3[] = { 45 | 0.075110f, 0.123840f, 0.075110f, 46 | 0.123840f, 0.204180f, 0.123840f, 47 | 0.075110f, 0.123840f, 0.075110f 48 | }; 49 | 50 | static float img_4x4[] = { 51 | 255.0f, 128.0f, 64.0f, 0.0f, 52 | 128.0f, 64.0f, 0.0f, 255.0f, 53 | 64.0f, 0.0f, 255.0f, 128.0f, 54 | 0.0f, 255.0f, 128.0f, 64.0f 55 | }; 56 | static float img_5x5[] = { 57 | 0.0f, 1.0f, 2.0f, 3.0f, 5.0f, 58 | 73.0f, 79.0f, 83.0f, 89.0f, 97.0f, 59 | 127.0f, 131.0f, 137.0f, 139.0f, 149.0f, 60 | 179.0f, 181.0f, 191.0f, 193.0f, 197.0f, 61 | 233.0f, 239.0f, 241.0f, 251.0f, 255.0f 62 | }; 63 | 64 | 65 | static int _test_decimate_2x_4x4(); 66 | static int _test_decimate_2x_5x5(); 67 | static int _test_decimate_3x_5x5(); 68 | 69 | 70 | /*---------------------------------------------------------------------------- 71 | * TEST ENTRY POINT 72 | *---------------------------------------------------------------------------*/ 73 | int test_decimate() 74 | { 75 | int failure = 0; 76 | 77 | printf("\nDecimate:\n"); 78 | failure += _test_decimate_2x_4x4(); 79 | failure += _test_decimate_2x_5x5(); 80 | failure += _test_decimate_3x_5x5(); 81 | 82 | return failure; 83 | } 84 | 85 | /*---------------------------------------------------------------------------- 86 | * _test_decimate_2x_4x4 87 | *---------------------------------------------------------------------------*/ 88 | int _test_decimate_2x_4x4() 89 | { 90 | int rw, rh, passed, failures=0; 91 | struct _kernel k_linear, k_gaussian; 92 | float img_tmp_4x4[16]; 93 | 94 | float result_linear[] = { 95 | 255.0f, 96.0f, 96 | 96.0f, 79.750f 97 | }; 98 | 99 | float result_gaussian[] = { 100 | 190.116f, 70.419f, 101 | 70.419f, 131.689f 102 | }; 103 | 104 | k_linear.w = k_linear.h = 2; 105 | k_linear.kernel = lpf_avg_2x2; 106 | k_linear.normalized = 1; 107 | k_linear.bnd_opt = KBND_SYMMETRIC; 108 | 109 | k_gaussian.w = k_gaussian.h = 3; 110 | k_gaussian.kernel = lpf_gaussian_3x3; 111 | k_gaussian.normalized = 1; 112 | k_gaussian.bnd_opt = KBND_SYMMETRIC; 113 | 114 | printf("\t4x4 image, 2x2 linear filter, 2x factor:\n"); 115 | 116 | /* With result buffer */ 117 | printf("\t w/ result no rw/rh: "); 118 | memset(img_tmp_4x4,0,sizeof(img_tmp_4x4)); 119 | _iqa_decimate(img_4x4, 4, 4, 2, &k_linear, img_tmp_4x4, 0, 0); 120 | passed = 0; 121 | if (_matrix_cmp(img_tmp_4x4, result_linear, 2, 2, 3) == 0) 122 | passed = 1; 123 | printf("\t%s\n", passed?"PASS":"FAILED"); 124 | failures += passed?0:1; 125 | 126 | printf("\t w/ result w/ rw/rh: "); 127 | memset(img_tmp_4x4,0,sizeof(img_tmp_4x4)); 128 | _iqa_decimate(img_4x4, 4, 4, 2, &k_linear, img_tmp_4x4, &rw, &rh); 129 | passed = 0; 130 | if (_matrix_cmp(img_tmp_4x4, result_linear, 2, 2, 3) == 0 && 131 | rw == 2 && 132 | rh == 2) 133 | passed = 1; 134 | printf("\t%s\n", passed?"PASS":"FAILED"); 135 | failures += passed?0:1; 136 | 137 | /* In-place*/ 138 | printf("\t in-place no rw/rh: "); 139 | memcpy(img_tmp_4x4, img_4x4, sizeof(img_4x4)); 140 | _iqa_decimate(img_tmp_4x4, 4, 4, 2, &k_linear, 0, 0, 0); 141 | passed = 0; 142 | if (_matrix_cmp(img_tmp_4x4, result_linear, 2, 2, 3) == 0) 143 | passed = 1; 144 | printf("\t%s\n", passed?"PASS":"FAILED"); 145 | failures += passed?0:1; 146 | 147 | 148 | printf("\t4x4 image, 3x3 Gaussian filter, 2x factor:\n"); 149 | 150 | /* With result buffer */ 151 | printf("\t w/ result no rw/rh: "); 152 | memset(img_tmp_4x4,0,sizeof(img_tmp_4x4)); 153 | _iqa_decimate(img_4x4, 4, 4, 2, &k_gaussian, img_tmp_4x4, 0, 0); 154 | passed = 0; 155 | if (_matrix_cmp(img_tmp_4x4, result_gaussian, 2, 2, 3) == 0) 156 | passed = 1; 157 | printf("\t%s\n", passed?"PASS":"FAILED"); 158 | failures += passed?0:1; 159 | 160 | return failures; 161 | } 162 | 163 | /*---------------------------------------------------------------------------- 164 | * _test_decimate_2x_5x5 165 | *---------------------------------------------------------------------------*/ 166 | int _test_decimate_2x_5x5() 167 | { 168 | int rw, rh, passed, failures=0; 169 | struct _kernel k_linear, k_gaussian; 170 | float img_tmp_5x5[25]; 171 | 172 | float result_linear[] = { 173 | 0.0f, 1.5f, 4.0f, 174 | 100.0f, 107.5f, 118.5f, 175 | 206.0f, 213.0f, 224.0f 176 | }; 177 | 178 | float result_gaussian[] = { 179 | 20.656f, 24.349f, 29.215f, 180 | 127.546f, 136.051f, 145.761f, 181 | 219.540f, 228.283f, 238.003f 182 | }; 183 | 184 | k_linear.w = k_linear.h = 2; 185 | k_linear.kernel = lpf_avg_2x2; 186 | k_linear.normalized = 1; 187 | k_linear.bnd_opt = KBND_SYMMETRIC; 188 | 189 | k_gaussian.w = k_gaussian.h = 3; 190 | k_gaussian.kernel = lpf_gaussian_3x3; 191 | k_gaussian.normalized = 1; 192 | k_gaussian.bnd_opt = KBND_SYMMETRIC; 193 | 194 | printf("\t5x5 image, 2x2 linear filter, 2x factor:\n"); 195 | 196 | /* With result buffer */ 197 | printf("\t w/ result no rw/rh: "); 198 | memset(img_tmp_5x5,0,sizeof(img_tmp_5x5)); 199 | _iqa_decimate(img_5x5, 5, 5, 2, &k_linear, img_tmp_5x5, 0, 0); 200 | passed = 0; 201 | if (_matrix_cmp(img_tmp_5x5, result_linear, 3, 3, 3) == 0) 202 | passed = 1; 203 | printf("\t%s\n", passed?"PASS":"FAILED"); 204 | failures += passed?0:1; 205 | 206 | printf("\t w/ result w/ rw/rh: "); 207 | memset(img_tmp_5x5,0,sizeof(img_tmp_5x5)); 208 | _iqa_decimate(img_5x5, 5, 5, 2, &k_linear, img_tmp_5x5, &rw, &rh); 209 | passed = 0; 210 | if (_matrix_cmp(img_tmp_5x5, result_linear, 3, 3, 3) == 0 && 211 | rw == 3 && 212 | rh == 3) 213 | passed = 1; 214 | printf("\t%s\n", passed?"PASS":"FAILED"); 215 | failures += passed?0:1; 216 | 217 | /* In-place*/ 218 | printf("\t in-place no rw/rh: "); 219 | memcpy(img_tmp_5x5, img_5x5, sizeof(img_5x5)); 220 | _iqa_decimate(img_tmp_5x5, 5, 5, 2, &k_linear, 0, 0, 0); 221 | passed = 0; 222 | if (_matrix_cmp(img_tmp_5x5, result_linear, 3, 3, 3) == 0) 223 | passed = 1; 224 | printf("\t%s\n", passed?"PASS":"FAILED"); 225 | failures += passed?0:1; 226 | 227 | 228 | printf("\t5x5 image, 3x3 Gaussian filter, 2x factor:\n"); 229 | 230 | /* With result buffer */ 231 | printf("\t w/ result no rw/rh: "); 232 | memset(img_tmp_5x5,0,sizeof(img_tmp_5x5)); 233 | _iqa_decimate(img_5x5, 5, 5, 2, &k_gaussian, img_tmp_5x5, 0, 0); 234 | passed = 0; 235 | if (_matrix_cmp(img_tmp_5x5, result_gaussian, 3, 3, 3) == 0) 236 | passed = 1; 237 | printf("\t%s\n", passed?"PASS":"FAILED"); 238 | failures += passed?0:1; 239 | 240 | return failures; 241 | } 242 | 243 | /*---------------------------------------------------------------------------- 244 | * _test_decimate_3x_5x5 245 | *---------------------------------------------------------------------------*/ 246 | int _test_decimate_3x_5x5() 247 | { 248 | int rw, rh, passed, failures=0; 249 | struct _kernel k_gaussian; 250 | float img_tmp_5x5[25]; 251 | 252 | float result_gaussian[] = { 253 | 20.656f, 26.918f, 254 | 180.543f, 194.490f 255 | }; 256 | 257 | k_gaussian.w = k_gaussian.h = 3; 258 | k_gaussian.kernel = lpf_gaussian_3x3; 259 | k_gaussian.normalized = 1; 260 | k_gaussian.bnd_opt = KBND_SYMMETRIC; 261 | 262 | printf("\t5x5 image, 3x3 Gaussian filter, 3x factor:\n"); 263 | 264 | /* With result buffer */ 265 | printf("\t w/ result no rw/rh: "); 266 | memset(img_tmp_5x5,0,sizeof(img_tmp_5x5)); 267 | _iqa_decimate(img_5x5, 5, 5, 3, &k_gaussian, img_tmp_5x5, 0, 0); 268 | passed = 0; 269 | if (_matrix_cmp(img_tmp_5x5, result_gaussian, 2, 2, 3) == 0) 270 | passed = 1; 271 | printf("\t%s\n", passed?"PASS":"FAILED"); 272 | failures += passed?0:1; 273 | 274 | printf("\t w/ result w/ rw/rh: "); 275 | memset(img_tmp_5x5,0,sizeof(img_tmp_5x5)); 276 | _iqa_decimate(img_5x5, 5, 5, 3, &k_gaussian, img_tmp_5x5, &rw, &rh); 277 | passed = 0; 278 | if (_matrix_cmp(img_tmp_5x5, result_gaussian, 2, 2, 3) == 0 && 279 | rw == 2 && 280 | rh == 2) 281 | passed = 1; 282 | printf("\t%s\n", passed?"PASS":"FAILED"); 283 | failures += passed?0:1; 284 | 285 | return failures; 286 | } 287 | -------------------------------------------------------------------------------- /src/iqa/test/source/test_mse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "iqa.h" 35 | #include "test_mse.h" 36 | #include "hptime.h" 37 | #include 38 | #include 39 | 40 | static unsigned char img_1x1[1] = { 128 }; 41 | static unsigned char img_2x2[4] = { 0, 128, 192, 255 }; 42 | 43 | int test_mse() 44 | { 45 | int passed, failures=0; 46 | float result; 47 | unsigned long long start, end; 48 | unsigned char img2[4]; 49 | 50 | printf("\nMSE:\n"); 51 | 52 | printf("\t1x1 Identical: "); 53 | memcpy(img2, img_1x1, sizeof(img_1x1)); 54 | start = hpt_get_time(); 55 | result = iqa_mse(img_1x1, img2, 1, 1, 1); 56 | end = hpt_get_time(); 57 | passed = result==0.0f ? 1 : 0; 58 | printf("%.5f (%.3lf ms)\t%s\n", 59 | result, 60 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 61 | passed?"PASS":"FAILED"); 62 | failures += passed?0:1; 63 | 64 | printf("\t1x1 Different: "); 65 | img2[0] += 8; 66 | start = hpt_get_time(); 67 | result = iqa_mse(img_1x1, img2, 1, 1, 1); 68 | end = hpt_get_time(); 69 | passed = result==64.0f ? 1 : 0; 70 | printf("%.5f (%.3lf ms)\t%s\n", 71 | result, 72 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 73 | passed?"PASS":"FAILED"); 74 | failures += passed?0:1; 75 | 76 | printf("\t2x2 Identical: "); 77 | memcpy(img2, img_2x2, sizeof(img_2x2)); 78 | start = hpt_get_time(); 79 | result = iqa_mse(img_2x2, img2, 2, 2, 2); 80 | end = hpt_get_time(); 81 | passed = result==0.0f ? 1 : 0; 82 | printf("%.5f (%.3lf ms)\t%s\n", 83 | result, 84 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 85 | passed?"PASS":"FAILED"); 86 | failures += passed?0:1; 87 | 88 | printf("\t2x2 Different: "); 89 | img2[2] -= 13; 90 | start = hpt_get_time(); 91 | result = iqa_mse(img_2x2, img2, 2, 2, 2); 92 | end = hpt_get_time(); 93 | passed = result==42.250f ? 1 : 0; 94 | printf("%.5f (%.3lf ms)\t%s\n", 95 | result, 96 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 97 | passed?"PASS":"FAILED"); 98 | failures += passed?0:1; 99 | 100 | return failures; 101 | } 102 | -------------------------------------------------------------------------------- /src/iqa/test/source/test_psnr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Tom Distler (http://tdistler.com) 3 | * All rights reserved. 4 | * 5 | * The BSD License 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * - Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * - Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * - Neither the name of the tdistler.com nor the names of its contributors may 18 | * be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include "iqa.h" 35 | #include "test_psnr.h" 36 | #include "hptime.h" 37 | #include "math_utils.h" 38 | #include 39 | #include 40 | 41 | static unsigned char img_1x1[1] = { 128 }; 42 | static unsigned char img_2x2[4] = { 0, 128, 192, 255 }; 43 | 44 | int test_psnr() 45 | { 46 | int passed, failures=0; 47 | float result; 48 | unsigned long long start, end; 49 | unsigned char img2[4]; 50 | 51 | printf("\nPSNR:\n"); 52 | 53 | printf("\t1x1 Identical: "); 54 | memcpy(img2, img_1x1, sizeof(img_1x1)); 55 | start = hpt_get_time(); 56 | result = iqa_psnr(img_1x1, img2, 1, 1, 1); 57 | end = hpt_get_time(); 58 | passed = result==INFINITY ? 1 : 0; 59 | printf("%.5f (%.3lf ms)\t%s\n", 60 | result, 61 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 62 | passed?"PASS":"FAILED"); 63 | failures += passed?0:1; 64 | 65 | printf("\t1x1 Different: "); 66 | img2[0] += 8; 67 | start = hpt_get_time(); 68 | result = iqa_psnr(img_1x1, img2, 1, 1, 1); 69 | end = hpt_get_time(); 70 | passed = _cmp_float(result, 30.06900f, 5) ? 0 : 1; 71 | printf("%.5f (%.3lf ms)\t%s\n", 72 | result, 73 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 74 | passed?"PASS":"FAILED"); 75 | failures += passed?0:1; 76 | 77 | printf("\t2x2 Identical: "); 78 | memcpy(img2, img_2x2, sizeof(img_2x2)); 79 | start = hpt_get_time(); 80 | result = iqa_psnr(img_2x2, img2, 2, 2, 2); 81 | end = hpt_get_time(); 82 | passed = result==INFINITY ? 1 : 0; 83 | printf("%.5f (%.3lf ms)\t%s\n", 84 | result, 85 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 86 | passed?"PASS":"FAILED"); 87 | failures += passed?0:1; 88 | 89 | printf("\t2x2 Different: "); 90 | img2[2] -= 19; 91 | start = hpt_get_time(); 92 | result = iqa_psnr(img_2x2, img2, 2, 2, 2); 93 | end = hpt_get_time(); 94 | passed = _cmp_float(result, 28.57633f, 5) ? 0 : 1; 95 | printf("%.5f (%.3lf ms)\t%s\n", 96 | result, 97 | hpt_elapsed_time(start,end,hpt_get_frequency()) * 1000.0, 98 | passed?"PASS":"FAILED"); 99 | failures += passed?0:1; 100 | 101 | return failures; 102 | } 103 | -------------------------------------------------------------------------------- /src/smallfry.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Derek Buitenhuis 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "smallfry.h" 22 | 23 | #define MAX(a, b) (a > b ? a : b) 24 | #define MIN(a, b) (a < b ? a : b) 25 | 26 | static double psnr_factor(uint8_t *orig, uint8_t *cmp, int orig_stride, 27 | int cmp_stride, int width, int height, uint8_t max) 28 | { 29 | uint8_t *old, *new; 30 | double ret; 31 | int sum; 32 | int i, j; 33 | 34 | sum = 0; 35 | old = orig; 36 | new = cmp; 37 | 38 | for (i = 0; i < height; i++) { 39 | for (j = 0; j < width; j++) 40 | sum += (old[j] - new[j]) * (old[j] - new[j]); 41 | 42 | old += orig_stride; 43 | new += cmp_stride; 44 | } 45 | 46 | ret = (double) sum / (double) (width * height); 47 | ret = 10.0 * log10(65025.0 / ret); 48 | 49 | if (max > 128) 50 | ret /= 50.0; 51 | else 52 | ret /= (0.0016 * (double) (max * max)) - (0.38 * (double) max + 72.5); 53 | 54 | return MAX(MIN(ret, 1.0), 0.0); 55 | } 56 | 57 | #define DVAL(i) (abs(old[i] - new[i])) 58 | #define HDVAL(i,j) (abs(old[i + j * orig_stride] - new[i + j * cmp_stride])) 59 | static double aae_factor(uint8_t *orig, uint8_t *cmp, int orig_stride, 60 | int cmp_stride, int width, int height, uint8_t max) 61 | { 62 | uint8_t *old, *new; 63 | double ret; 64 | double sum; 65 | double cfmax, cf; 66 | int i, j; 67 | int cnt; 68 | 69 | sum = 0.0; 70 | cnt = 0; 71 | old = orig; 72 | new = cmp; 73 | 74 | for (i = 0; i < height; i++) { 75 | for (j = 7; j < width - 1; j += 8) { 76 | double calc; 77 | 78 | cnt++; 79 | 80 | calc = abs(DVAL(j) - DVAL(j + 1)); 81 | calc /= (abs(DVAL(j - 1) - DVAL(j)) + abs(DVAL(j + 1) - DVAL(j + 2)) + 0.0001) / 2.0; 82 | 83 | if (calc > 5.0) 84 | sum += 1.0; 85 | else if (calc > 2.0) 86 | sum += (calc - 2.0) / (5.0 - 2.0); 87 | } 88 | 89 | old += orig_stride; 90 | new += cmp_stride; 91 | } 92 | 93 | old = orig + 7 * orig_stride; 94 | new = cmp + 7 * cmp_stride; 95 | 96 | for (i = 7; i < height - 2; i += 8) { 97 | for (j = 0; j < width; j++) { 98 | double calc; 99 | 100 | cnt++; 101 | 102 | calc = abs(DVAL(j) - HDVAL(j, 1)); 103 | calc /= (abs(HDVAL(j, -1) - DVAL(j)) + abs(HDVAL(j, 1) - HDVAL(j, 2)) + 0.0001) / 2.0; 104 | 105 | if (calc > 5.0) 106 | sum += 1.0; 107 | else if (calc > 2.0) 108 | sum += (calc - 2.0) / (5.0 - 2.0); 109 | } 110 | 111 | old += 8 * orig_stride; 112 | new += 8 * cmp_stride; 113 | } 114 | 115 | ret = 1 - (sum / (double) cnt); 116 | 117 | if (max > 128) 118 | cfmax = 0.65; 119 | else 120 | cfmax = 0.65 + 0.35 * ((128.0 - (double) max) / 128.0); 121 | 122 | cf = MAX(cfmax, MIN(1, 0.25 + (1000.0 * (double) cnt) / sum)); 123 | 124 | return ret * cf; 125 | } 126 | 127 | static uint8_t maxluma(uint8_t *buf, int stride, int width, int height) 128 | { 129 | uint8_t *in = buf; 130 | uint8_t max = 0; 131 | int i, j; 132 | 133 | for (i = 0; i < height; i++) { 134 | for (j = 0; j < width; j++) 135 | max = MAX(in[j], max); 136 | 137 | in += stride; 138 | } 139 | 140 | return max; 141 | } 142 | 143 | double smallfry_metric(uint8_t *inbuf, uint8_t *outbuf, int width, int height) 144 | { 145 | double p, a, b; 146 | uint8_t max; 147 | 148 | max = maxluma(inbuf, width, width, height); 149 | 150 | p = psnr_factor(inbuf, outbuf, width, width, width, height, max); 151 | a = aae_factor(inbuf, outbuf, width, width, width, height, max); 152 | 153 | b = p * 37.1891885161239 + a * 78.5328607296973; 154 | 155 | return b; 156 | } 157 | -------------------------------------------------------------------------------- /src/smallfry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Derek Buitenhuis 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef METRIC_H 18 | #define METRIC_H 19 | 20 | double smallfry_metric(uint8_t *inbuf, uint8_t *outbuf, int width, int height); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/test/assertion-macros.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // assertion-macros.h 4 | // 5 | // Copyright (c) 2013 Stephen Mathieson 6 | // MIT licensed 7 | // 8 | 9 | 10 | #ifndef __ASSERTION_MACROS_H__ 11 | #define __ASSERTION_MACROS_H__ 1 12 | 13 | #define ASSERTIONS_VERSION "0.0.1" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | int __assert_bail = 0; 20 | int __assert_failures = 0; 21 | 22 | /* 23 | * Bail at first failing assertion 24 | */ 25 | 26 | #define assert_bail() __assert_bail = !__assert_bail; 27 | 28 | /* 29 | * Get the number of failed assertions 30 | */ 31 | 32 | #define assert_failures() __assert_failures 33 | 34 | /* 35 | * Reset the number of failed assertions 36 | */ 37 | 38 | #define assert_reset() ({ \ 39 | __assert_failures = 0; \ 40 | }) 41 | 42 | // don't clobber assert 43 | #ifndef assert 44 | #define assert assert_ok 45 | #endif 46 | 47 | /* 48 | * Assert that `expr` evaluates to something truthy 49 | */ 50 | 51 | #define assert_ok(expr) ({ \ 52 | if (!(expr)) {\ 53 | __assert_failures++; \ 54 | fprintf(stderr, \ 55 | "Assertion error: %s (%s:%d)\n", \ 56 | #expr, __FILE__, __LINE__); \ 57 | if (__assert_bail) abort(); \ 58 | } \ 59 | }) 60 | 61 | /* 62 | * Assert that `a` is equal to `b` 63 | */ 64 | 65 | #define assert_equal(a, b) ({ \ 66 | if (a != b) {\ 67 | __assert_failures++; \ 68 | fprintf(stderr, \ 69 | "Assertion error: %d == %d (%s:%d)\n", \ 70 | a, b, __FILE__, __LINE__); \ 71 | if (__assert_bail) abort(); \ 72 | } \ 73 | }) 74 | 75 | /* 76 | * Assert that `a` is equal to `b` 77 | */ 78 | 79 | #define assert_equal_float(a, b) ({ \ 80 | if (a != b) {\ 81 | __assert_failures++; \ 82 | fprintf(stderr, \ 83 | "Assertion error: %f == %f (%s:%d)\n", \ 84 | a, b, __FILE__, __LINE__); \ 85 | if (__assert_bail) abort(); \ 86 | } \ 87 | }) 88 | 89 | /* 90 | * Assert that `a` is not equal to `b` 91 | */ 92 | 93 | #define assert_not_equal(a, b) ({ \ 94 | if (a == b) {\ 95 | __assert_failures++; \ 96 | fprintf(stderr, \ 97 | "Assertion error: %d != %d (%s:%d)\n", \ 98 | a, b, __FILE__, __LINE__); \ 99 | if (__assert_bail) abort(); \ 100 | } \ 101 | }) 102 | 103 | /* 104 | * Assert that `a` is equal to `b` 105 | */ 106 | 107 | #define assert_str_equal(a, b) ({ \ 108 | if (0 != strcmp(a, b)) {\ 109 | __assert_failures++; \ 110 | fprintf(stderr, \ 111 | "Assertion error: \"%s\" == \"%s\" (%s:%d)\n", \ 112 | a, b, __FILE__, __LINE__); \ 113 | if (__assert_bail) abort(); \ 114 | } \ 115 | }) 116 | 117 | /* 118 | * Assert that `a` is not equal to `b` 119 | */ 120 | 121 | #define assert_str_not_equal(a, b) ({ \ 122 | if (0 == strcmp(a, b)) {\ 123 | __assert_failures++; \ 124 | fprintf(stderr, \ 125 | "Assertion error: \"%s\" != \"%s\" (%s:%d)\n", \ 126 | a, b, __FILE__, __LINE__); \ 127 | if (__assert_bail) abort(); \ 128 | } \ 129 | }) 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /src/test/describe.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // describe.h 4 | // 5 | // Copyright (c) 2013 Stephen Mathieson 6 | // MIT licensed 7 | // 8 | 9 | 10 | #ifndef DESCRIBE_H 11 | #define DESCRIBE_H 1 12 | 13 | #include "assertion-macros.h" 14 | 15 | #define DESCRIBE_VERSION "0.0.1" 16 | #define DESCRIBE_OK "✓" 17 | #define DESCRIBE_FAIL "✖" 18 | 19 | /* 20 | * Describe `fn` with `title` 21 | */ 22 | 23 | #define describe(title, fn) int main(void) { \ 24 | printf("\n %s\n", title); \ 25 | fn; \ 26 | printf("\n"); \ 27 | return assert_failures(); \ 28 | } 29 | 30 | /* 31 | * Describe `fn` with `specification` 32 | */ 33 | 34 | #define it(specification, fn) ({ \ 35 | int before = assert_failures(); \ 36 | fn; \ 37 | if (assert_failures() == before) { \ 38 | printf(" \e[92m%s \e[90m%s\e[0m\n", DESCRIBE_OK, specification); \ 39 | } else { \ 40 | printf(" \e[90m%s \e[90m%s\e[0m\n", DESCRIBE_FAIL, specification); \ 41 | } \ 42 | }); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | Utility functions 3 | */ 4 | #ifndef UTIL_H 5 | #define UTIL_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 12 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 13 | 14 | extern const char *VERSION; 15 | extern const char *progname; 16 | 17 | // Subsampling method, which defines how much of the data from 18 | // each color channel is included in the image per 2x2 block. 19 | // A value of 4 means all four pixels are included, while 2 20 | // means that only two of the four are included (hence the term 21 | // subsampling). Subsampling works really well for photos, but 22 | // can have issues with crisp colored borders (e.g. red text). 23 | enum SUBSAMPLING_METHOD { 24 | // Default is 4:2:0 25 | SUBSAMPLE_DEFAULT, 26 | // Using 4:4:4 is more detailed and will prevent fine text 27 | // from getting blurry (e.g. screenshots) 28 | SUBSAMPLE_444 29 | }; 30 | 31 | enum filetype { 32 | FILETYPE_UNKNOWN, 33 | FILETYPE_AUTO, 34 | FILETYPE_JPEG, 35 | FILETYPE_PPM 36 | }; 37 | 38 | /* Print program version to stdout. */ 39 | void version(void); 40 | 41 | /* Print an error message. */ 42 | void error(const char *format, ...); 43 | 44 | /* 45 | Read a file into a buffer and return the length. 46 | */ 47 | long readFile(char *name, void **buffer); 48 | 49 | /* 50 | Decode a buffer into a JPEG image with the given pixel format. 51 | Returns the size of the image pixel array. 52 | See libjpeg.txt for a (very long) explanation. 53 | */ 54 | int checkJpegMagic(const unsigned char *buf, unsigned long size); 55 | unsigned long decodeJpeg(unsigned char *buf, unsigned long bufSize, unsigned char **image, int *width, int *height, int pixelFormat); 56 | 57 | /* 58 | Decode buffer into a PPM image. 59 | Returns the size of the image pixel array. 60 | */ 61 | int checkPpmMagic(const unsigned char *buf, unsigned long size); 62 | unsigned long decodePpm(unsigned char *buf, unsigned long bufSize, unsigned char **image, int *width, int *height); 63 | 64 | /* 65 | Encode a buffer of image pixels into a JPEG. 66 | */ 67 | unsigned long encodeJpeg(unsigned char **jpeg, unsigned char *buf, int width, int height, int pixelFormat, int quality, int progressive, int optimize, int subsample); 68 | 69 | /* Automatically detect the file type of a given file. */ 70 | enum filetype detectFiletype(const char *filename); 71 | enum filetype detectFiletypeFromBuffer(unsigned char *buf, long bufSize); 72 | 73 | /* Decode an image file with a given format. */ 74 | unsigned long decodeFile(const char *filename, unsigned char **image, enum filetype type, int *width, int *height, int pixelFormat); 75 | unsigned long decodeFileFromBuffer(unsigned char *buf, long bufSize, unsigned char **image, enum filetype type, int *width, int *height, int pixelFormat); 76 | 77 | /* 78 | Get JPEG metadata (EXIF, IPTC, XMP, etc) and return a buffer 79 | with just this data, suitable for writing out to a new file. 80 | Reads in all APP0-APP15 markers as well as COM markers. 81 | Reads up to 20 markers. 82 | 83 | If comment is not NULL, then returns 1 if the comment is 84 | encountered, allowing scripts to detect if they have previously 85 | modified the file. 86 | */ 87 | int getMetadata(const unsigned char *buf, unsigned int bufSize, unsigned char **meta, unsigned int *metaSize, const char *comment); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /test/comparison.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script requires ImageMagick and Ghostscript 4 | 5 | mkdir -p comparison 6 | cd comparison 7 | 8 | if [ ! -f nikon_d3x.jpg ]; then 9 | curl http://static.nikonusa.com/D3X_gallery/images/pic_004b.jpg >nikon_d3x.jpg 10 | fi 11 | 12 | # Create encodings 13 | ../../jpeg-recompress -m mpe nikon_d3x.jpg test-mpe.jpg 14 | ../../jpeg-recompress -m ssim nikon_d3x.jpg test-ssim.jpg 15 | ../../jpeg-recompress -m ms-ssim nikon_d3x.jpg test-ms-ssim.jpg 16 | ../../jpeg-recompress -m smallfry nikon_d3x.jpg test-smallfry.jpg 17 | 18 | # Crop images 19 | convert nikon_d3x.jpg -crop 360x400+1604+1934! -gravity northwest -fill white -pointsize 16 -annotate +10+10 "Original from camera \(`du -k nikon_d3x.jpg | python2 -c 'kb = raw_input().split()[0]; print("%.2f MiB" % (float(kb) / 1024))'`\)" crop-orig.png 20 | convert test-mpe.jpg -crop 360x400+1604+1934! -gravity northwest -fill white -pointsize 16 -annotate +10+10 "Compressed MPE \(`du -k test-mpe.jpg | python2 -c 'kb = raw_input().split()[0]; print("%.2f MiB" % (float(kb) / 1024))'`\)" crop-mpe.png 21 | convert test-ssim.jpg -crop 360x400+1604+1934! -gravity northwest -fill white -pointsize 16 -annotate +10+10 "Compressed SSIM \(`du -k test-ssim.jpg | python2 -c 'kb = raw_input().split()[0]; print("%.2f MiB" % (float(kb) / 1024))'`\)" crop-ssim.png 22 | convert test-ms-ssim.jpg -crop 360x400+1604+1934! -gravity northwest -fill white -pointsize 16 -annotate +10+10 "Compressed MS-SSIM \(`du -k test-ms-ssim.jpg | python2 -c 'kb = raw_input().split()[0]; print("%.2f MiB" % (float(kb) / 1024))'`\)" crop-ms-ssim.png 23 | convert test-smallfry.jpg -crop 360x400+1604+1934! -gravity northwest -fill white -pointsize 16 -annotate +10+10 "Compressed SmallFry \(`du -k test-smallfry.jpg | python2 -c 'kb = raw_input().split()[0]; print("%.2f MiB" % (float(kb) / 1024))'`\)" crop-smallfry.png 24 | 25 | # Create montage 26 | montage -geometry +0+0 crop-orig.png crop-ssim.png crop-ms-ssim.png crop-smallfry.png crop-mpe.png ../comparison.png 27 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include "../src/edit.h" 2 | #include "../src/hash.h" 3 | #include "../src/util.h" 4 | 5 | #include "../src/test/describe.h" 6 | 7 | describe ("Unit Tests", { 8 | it ("Should clamp values", { 9 | assert_equal_float(0.0, clamp(0.0, -10.0, 100.0)); 10 | assert_equal_float(32.5, clamp(0.0, 32.5, 100.0)); 11 | assert_equal_float(100.0, clamp(0.0, 150.3, 100.0)); 12 | }); 13 | 14 | it ("Should interpolate pixels", { 15 | unsigned char *image; 16 | int value; 17 | 18 | image = malloc(4); 19 | 20 | image[0] = 0; 21 | image[1] = 255; 22 | image[2] = 127; 23 | image[3] = 66; 24 | 25 | value = interpolate(image, 2, 1, 0.3, 0.6, 0); 26 | 27 | assert_equal(95, value); 28 | 29 | free(image); 30 | }); 31 | 32 | it ("Should scale an image", { 33 | unsigned char *image; 34 | unsigned char *scaled; 35 | 36 | /* 37 | [ 0 1 2 3 38 | 4 5 6 7 39 | 8 9 10 11 40 | 12 13 14 15 ] 41 | */ 42 | 43 | image = malloc(16); 44 | 45 | for (int x = 0; x < 16; x++) { 46 | image[x] = (unsigned char) x; 47 | } 48 | 49 | /* 50 | [ 0 2 51 | 8 10 ] 52 | */ 53 | scale(image, 4, 4, &scaled, 2, 2); 54 | 55 | assert_equal(image[2], scaled[1]); 56 | assert_equal(image[8], scaled[2]); 57 | 58 | free(scaled); 59 | free(image); 60 | }); 61 | 62 | it ("Should generate an image hash", { 63 | unsigned char *image; 64 | unsigned char *hash; 65 | 66 | image = malloc(16); 67 | 68 | /* 69 | [ 0 15 2 13 70 | 4 11 6 9 71 | 8 7 10 5 72 | 12 3 14 1 ] 73 | */ 74 | 75 | for (int x = 0; x < 16; x++) { 76 | image[x] = (unsigned char) ((x % 2) ? 16 - x : x); 77 | } 78 | 79 | // Hash should be 101010100101010 80 | genHash(image, 4, 4, &hash); 81 | 82 | assert_equal(1, hash[0]); 83 | assert_equal(0, hash[1]); 84 | assert_equal(0, hash[5]); 85 | assert_equal(1, hash[9]); 86 | 87 | free(hash); 88 | free(image); 89 | }); 90 | 91 | it ("Should calculate hamming distance", { 92 | int dist = hammingDist((unsigned char *) "101010", (unsigned char *) "111011", 6); 93 | assert_equal(2, dist); 94 | }); 95 | 96 | it ("Should decode a PPM", { 97 | char *image = "P6\n2 2\n255\n\x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc"; 98 | unsigned char *imageData; 99 | int width; 100 | int height; 101 | 102 | decodePpm((unsigned char *) image, 23, (unsigned char **) &imageData, &width, &height); 103 | 104 | assert_equal(2, width); 105 | assert_equal(2, height); 106 | assert_equal('\x1', imageData[0]); 107 | assert_equal('\xc', imageData[11]); 108 | 109 | free(imageData); 110 | }) 111 | }); 112 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | mkdir -p test-output 6 | 7 | if [ ! -d test-files ]; then 8 | curl -O -L https://www.dropbox.com/s/hb3ah7p5hcjvhc1/jpeg-archive-test-files.zip 9 | unzip jpeg-archive-test-files.zip 10 | fi 11 | 12 | for file in test-files/*; do 13 | ../jpeg-recompress "$file" "test-output/`basename $file`" 14 | done 15 | --------------------------------------------------------------------------------