Skip to content

Commit

Permalink
Add defish edit operation
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgtaylor committed Jan 8, 2014
1 parent db8b5b3 commit fd09975
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ all: jpeg-recompress jpeg-compare jpeg-hash
$(LIBIQA):
cd src/iqa; RELEASE=1 make

jpeg-recompress: jpeg-recompress.c src/util.o src/commander.o $(LIBIQA)
jpeg-recompress: jpeg-recompress.c src/util.o src/edit.o src/commander.o $(LIBIQA)
$(CC) $(CFLAGS) -o $@ $^ $(LIBJPEG) $(LDFLAGS)

jpeg-compare: jpeg-compare.c src/util.o src/hash.o src/commander.o $(LIBIQA)
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@ Compress JPEGs by re-encoding to the smallest JPEG quality while keeping _percei

The better the quality of the input image is, the better the output will be.

Some basic photo-related editing options are available, such as removing fisheye lens distortion.

```bash
# Default settings
jpeg-recompress image.jpg compressed.jpg

# High quality example settings
jpeg-recompress --quality high --min 60 image.jpg compressed.jpg

# Remove fisheye distortion (Tokina 10-17mm on APS-C @ 10mm)
jpeg-recompress --defish 2.6 --zoom 1.2 image.jpg defished.jpg
```

### jpeg-compare
Expand Down Expand Up @@ -50,14 +55,22 @@ Building
### Dependencies
* libjpeg (or libjpeg-turbo is recommended)

#### Ubuntu
Ubuntu users can install via `apt-get`:

```bash
sudo apt-get install build-essential libjpeg-turbo8 libjpeg-turbo8-dev
```

#### Mac OS X
Mac users can install it via [Homebrew](http://brew.sh/):

```bash
brew install libjpeg-turbo
```

### Compiling
Modify the `Makefile` to point to your installation of `libjpeg.a`. If you are using turbo then point to it.
The `Makefile` should work as-is on Ubuntu and Mac OS X. Other platforms may need to set the location of `libjpeg.a`.

```bash
make
Expand All @@ -67,7 +80,7 @@ make
Install the binaries into `/usr/local/bin`:

```bash
make install
sudo make install
```

Links / Alternatives
Expand Down
40 changes: 38 additions & 2 deletions jpeg-recompress.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string.h>

#include "src/commander.h"
#include "src/edit.h"
#include "src/iqa/include/iqa.h"
#include "src/util.h"

Expand All @@ -32,6 +33,10 @@ int progressive = 0;
// Strip metadata from the file?
int strip = 0;

// Defish the image?
float defishStrength = 0.0;
float defishZoom = 1.0;

static void setAttempts(command_t *self) {
attempts = atoi(self->arg);
}
Expand Down Expand Up @@ -70,6 +75,14 @@ static void setStrip(command_t *self) {
strip = 1;
}

static void setDefish(command_t *self) {
defishStrength = atof(self->arg);
}

static void setZoom(command_t *self) {
defishZoom = atof(self->arg);
}

int main (int argc, char **argv) {
unsigned char *buf;
long bufSize = 0;
Expand All @@ -81,6 +94,7 @@ int main (int argc, char **argv) {
unsigned long compressedSize = 0;
unsigned char *compressedGray;
long compressedGraySize = 0;
unsigned char *tmpImage;
int width, height;
unsigned char *metaBuf;
unsigned int metaSize = 0;
Expand All @@ -93,9 +107,11 @@ int main (int argc, char **argv) {
command_option(&cmd, "-q", "--quality [arg]", "Set a quality preset: low, medium, high, veryhigh [medium]", setQuality);
command_option(&cmd, "-n", "--min [arg]", "Minimum JPEG quality [40]", setMinimum);
command_option(&cmd, "-m", "--max [arg]", "Maximum JPEG quality [95]", setMaximum);
command_option(&cmd, "-l", "--loops [arg]", "Set the number of runs to attempt [5]", setAttempts);
command_option(&cmd, "-l", "--loops [arg]", "Set the number of runs to attempt [6]", setAttempts);
command_option(&cmd, "-p", "--progressive", "Set progressive JPEG output", setProgressive);
command_option(&cmd, "-s", "--strip", "Strip metadata", setStrip);
command_option(&cmd, "-d", "--defish [arg]", "Set defish strength [0.0]", setDefish);
command_option(&cmd, "-z", "--zoom [arg]", "Set defish zoom [1.0]", setZoom);
command_parse(&cmd, argc, argv);

if (cmd.argc < 2) {
Expand All @@ -110,7 +126,27 @@ int main (int argc, char **argv) {

// Decode the JPEG
originalSize = decodeJpeg(buf, bufSize, &original, &width, &height, JCS_RGB);
originalGraySize = decodeJpeg(buf, bufSize, &originalGray, &width, &height, JCS_GRAYSCALE);

if (defishStrength) {
printf("Defishing...\n");
tmpImage = malloc(width * height * 3);
defish(original, tmpImage, width, height, 3, defishStrength, defishZoom);
free(original);
original = tmpImage;
}

// Convert RGB input into ITU-R luma
originalGraySize = width * height;
originalGray = malloc(originalGraySize);
int stride = width * 3;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Y = 0.2126R + 0.7152G + 0.0722B
originalGray[y * width + x] = original[y * stride + x * 3] * 0.2126 +
original[y * stride + x * 3 + 1] * 0.7152 +
original[y * stride + x * 3 + 2] * 0.0722;
}
}

// Read metadata (EXIF / IPTC / XMP tags)
if (getMetadata(buf, bufSize, &metaBuf, &metaSize, COMMENT)) {
Expand Down
48 changes: 48 additions & 0 deletions src/edit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include <math.h>

float clamp(float low, float value, float high) {
return (value < low) ? low : ((value > high) ? high : value);
}

int interpolate(const unsigned char *image, int width, int components, float x, float y, int offset) {
int stride = width * components;
float px = x - floor(x);
float py = y - floor(y);
int x1 = floor(x);
int x2 = ceil(x);
int y1 = floor(y);
int y2 = ceil(y);

float top = (float) image[y1 * stride + x1 * components + offset] * (1.0 - px) +
(float) image[y1 * stride + x2 * components + offset] * px;
float bot = (float) image[y2 * stride + x1 * components + offset] * (1.0 - px) +
(float) image[y2 * stride + x2 * components + offset] * px;

return (top * (1.0 - py)) + (bot * py);
}

void defish(const unsigned char *input, unsigned char *output, int width, int height, int components, float strength, float zoom) {
const int cx = width / 2;
const int cy = height / 2;
const float len = sqrt(width * width + height * height);

for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float dx = (cx - x) * zoom;
float dy = (cy - y) * zoom;
float r = sqrt(dx * dx + dy * dy) / len * strength;
float theta = 1.0;

if (r != 0.0) {
theta = atan(r) / r;
}

dx = clamp(0.0, (float) width / 2.0 - theta * dx, width);
dy = clamp(0.0, (float) height / 2.0 - theta * dy, height);

for (int z = 0; z < components; z++) {
output[y * width * 3 + x * 3 + z] = interpolate(input, width, components, dx, dy, z);
}
}
}
}
25 changes: 25 additions & 0 deletions src/edit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Image editing functions
*/
#ifndef EDIT_H
#define EDIT_H

/*
Clamp a value between low and high.
*/
float clamp(float low, float value, float high);

/*
Bilinear interpolation
*/
int interpolate(const unsigned char *image, int width, int components, float x, float y, int offset);

/*
Remove fisheye distortion from an image. The amount of distortion is
controlled by strength, while zoom controls where the image gets
cropped. For example, the Tokina 10-17mm ATX fisheye on a Canon APS-C
body set to 10mm looks good with strength=2.6 and zoom=1.2.
*/
void defish(const unsigned char *input, unsigned char *output, int width, int height, int components, float strength, float zoom);

#endif

0 comments on commit fd09975

Please sign in to comment.