├── .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 [](https://travis-ci.org/danielgtaylor/jpeg-archive) [](https://ci.appveyor.com/project/danielgtaylor/jpeg-archive) [](https://github.com/danielgtaylor/jpeg-archive/releases) [](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 | 
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 |
--------------------------------------------------------------------------------