image_intensities 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f39c65351e014f893378d86f9c4228992806523aa89c416b67e7f337b7ddc2fb
4
+ data.tar.gz: d935cc5c3ed468f4413f6e932fe451d0f71ce82ee52b163a8e403f7d02d94a5b
5
+ SHA512:
6
+ metadata.gz: 9a185c393800449e75e372b0a39d984859952074a5bfdce1666a09f2e01cdbdb7b88135529295265d9204057b55d2dcfc12dca64f03cdddf198ef27846f845d3
7
+ data.tar.gz: 4cab25ae49fc490400b688436fa3879c3f393bd80331d127e390ab2ad68f4f182bbb702fff4fa47a4abef124012fb87a20063ea1b82cac0f14a0bdd48f3877b1
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ # Extconf
2
+ *.o
3
+ *.so
4
+ mkmf.log
5
+
6
+ # Rubygems
7
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/ext/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :image_intensities do
4
+ desc 'Compile libimage_intensities'
5
+ task :compile do
6
+ lib_path = if Dir.pwd.end_with?('/ext')
7
+ 'image_intensities'
8
+ else
9
+ 'ext/image_intensities'
10
+ end
11
+
12
+ cd lib_path do
13
+ make_program = ENV['MAKE'] || 'make'
14
+ sh "#{make_program} lib/libimage_intensities.so"
15
+ end
16
+ end
17
+ end
18
+
19
+ task default: 'image_intensities:compile'
@@ -0,0 +1,14 @@
1
+ CC ?= cc
2
+ RM ?= rm -rf
3
+ MKDIR ?= mkdir
4
+
5
+ .PHONY: all clean
6
+
7
+ all: lib/libimage_intensities.so
8
+
9
+ lib/libimage_intensities.so: definitions.h intensities.c png.c jpeg.c
10
+ $(MKDIR) -p lib
11
+ $(CC) intensities.c png.c jpeg.c -fPIC -shared -o lib/libimage_intensities.so -ljpeg -lpng
12
+
13
+ clean:
14
+ rm -rf lib
@@ -0,0 +1,49 @@
1
+ #ifndef _JPEG_H
2
+ #define _JPEG_H
3
+
4
+ #include "definitions.h"
5
+
6
+ #define MAX(a, b) (((a) > (b)) ? (a) : (b))
7
+
8
+ typedef struct rgb_pixel {
9
+ uint8_t r;
10
+ uint8_t g;
11
+ uint8_t b;
12
+ } rgb_pixel;
13
+
14
+ typedef struct intensity_sum {
15
+ uint64_t r;
16
+ uint64_t g;
17
+ uint64_t b;
18
+ } intensity_sum;
19
+
20
+ typedef struct quadrant_sums {
21
+ intensity_sum nw;
22
+ intensity_sum ne;
23
+ intensity_sum sw;
24
+ intensity_sum se;
25
+ } quadrant_sums;
26
+
27
+ typedef struct raster_data {
28
+ uint32_t width;
29
+ uint32_t height;
30
+ rgb_pixel *pixels;
31
+ int error;
32
+ } raster_data;
33
+
34
+ typedef struct intensity_data {
35
+ double nw;
36
+ double ne;
37
+ double sw;
38
+ double se;
39
+ int error;
40
+ } intensity_data;
41
+
42
+ raster_data read_jpeg_file(const char *file_name);
43
+ raster_data read_png_file(const char *file_name);
44
+ quadrant_sums rgb_sums(rgb_pixel *restrict pixels, uint32_t width, uint32_t height);
45
+
46
+ intensity_data jpeg_intensities(const char *file_name);
47
+ intensity_data png_intensities(const char *file_name);
48
+
49
+ #endif
@@ -0,0 +1,108 @@
1
+ #include <stdio.h>
2
+ #include <stdint.h>
3
+ #include <stdlib.h>
4
+ #include <stddef.h>
5
+ #include <fcntl.h>
6
+ #include <string.h>
7
+ #include <jpeglib.h>
8
+
9
+ #include "definitions.h"
10
+
11
+ quadrant_sums rgb_sums(rgb_pixel *restrict pixels, uint32_t width, uint32_t height)
12
+ {
13
+ quadrant_sums sums = {{}};
14
+
15
+ for (uint32_t i = 0; i < height; ++i) {
16
+ for (uint32_t j = 0; j < width; ++j) {
17
+ int nw = (i <= height / 2) && (j <= width / 2);
18
+ int ne = (i <= height / 2) && (j >= width / 2);
19
+ int sw = (i >= height / 2) && (j <= width / 2);
20
+ int se = (i >= height / 2) && (j >= width / 2);
21
+
22
+ rgb_pixel pix = pixels[i * width + j];
23
+
24
+ if (nw) {
25
+ sums.nw.r += pix.r;
26
+ sums.nw.g += pix.g;
27
+ sums.nw.b += pix.b;
28
+ }
29
+
30
+ if (ne) {
31
+ sums.ne.r += pix.r;
32
+ sums.ne.g += pix.g;
33
+ sums.ne.b += pix.b;
34
+ }
35
+
36
+ if (sw) {
37
+ sums.sw.r += pix.r;
38
+ sums.sw.g += pix.g;
39
+ sums.sw.b += pix.b;
40
+ }
41
+
42
+ if (se) {
43
+ sums.se.r += pix.r;
44
+ sums.se.g += pix.g;
45
+ sums.se.b += pix.b;
46
+ }
47
+ }
48
+ }
49
+
50
+ return sums;
51
+ }
52
+
53
+ static intensity_data rgb_to_luma(quadrant_sums sums, raster_data data)
54
+ {
55
+ double dim = MAX(data.width * data.height / 4.0, 1);
56
+
57
+ double nw_luma = ((sums.nw.r / dim * 0.2126) +
58
+ (sums.nw.g / dim * 0.7152) +
59
+ (sums.nw.b / dim * 0.0772)) / 3.0;
60
+
61
+ double ne_luma = ((sums.ne.r / dim * 0.2126) +
62
+ (sums.ne.g / dim * 0.7152) +
63
+ (sums.ne.b / dim * 0.0772)) / 3.0;
64
+
65
+ double sw_luma = ((sums.sw.r / dim * 0.2126) +
66
+ (sums.sw.g / dim * 0.7152) +
67
+ (sums.sw.b / dim * 0.0772)) / 3.0;
68
+
69
+ double se_luma = ((sums.se.r / dim * 0.2126) +
70
+ (sums.se.g / dim * 0.7152) +
71
+ (sums.se.b / dim * 0.0772)) / 3.0;
72
+
73
+ return (struct intensity_data) {
74
+ .nw = nw_luma,
75
+ .ne = ne_luma,
76
+ .sw = sw_luma,
77
+ .se = se_luma,
78
+ .error = 0
79
+ };
80
+ }
81
+
82
+ intensity_data jpeg_intensities(const char *file_name)
83
+ {
84
+ raster_data data = read_jpeg_file(file_name);
85
+ if (data.error)
86
+ return (struct intensity_data) { .error = 1 };
87
+
88
+ quadrant_sums sums = rgb_sums(data.pixels, data.width, data.height);
89
+ intensity_data ins = rgb_to_luma(sums, data);
90
+
91
+ free(data.pixels);
92
+
93
+ return ins;
94
+ }
95
+
96
+ intensity_data png_intensities(const char *file_name)
97
+ {
98
+ raster_data data = read_png_file(file_name);
99
+ if (data.error)
100
+ return (struct intensity_data) { .error = 1 };
101
+
102
+ quadrant_sums sums = rgb_sums(data.pixels, data.width, data.height);
103
+ intensity_data ins = rgb_to_luma(sums, data);
104
+
105
+ free(data.pixels);
106
+
107
+ return ins;
108
+ }
@@ -0,0 +1,71 @@
1
+ #include <stdio.h>
2
+ #include <stdint.h>
3
+ #include <stdlib.h>
4
+ #include <stddef.h>
5
+ #include <fcntl.h>
6
+ #include <string.h>
7
+ #include <jpeglib.h>
8
+ #include <setjmp.h>
9
+
10
+ #include "definitions.h"
11
+
12
+ struct my_error_mgr {
13
+ struct jpeg_error_mgr pub;
14
+ jmp_buf setjmp_buffer;
15
+ };
16
+
17
+ static void my_error_exit(j_common_ptr cinfo)
18
+ {
19
+ struct my_error_mgr *myerr = (struct my_error_mgr *) cinfo->err;
20
+ longjmp(myerr->setjmp_buffer, 1);
21
+ }
22
+
23
+ raster_data read_jpeg_file(const char *file_name)
24
+ {
25
+ struct jpeg_decompress_struct cinfo;
26
+ struct my_error_mgr jerr;
27
+ FILE *file;
28
+ JSAMPARRAY buffer;
29
+ int row_stride;
30
+
31
+ raster_data data = {};
32
+
33
+ file = fopen(file_name, "rb");
34
+ if (file == NULL) {
35
+ data.error = 1;
36
+ return data;
37
+ }
38
+
39
+ cinfo.err = jpeg_std_error(&jerr.pub);
40
+ jerr.pub.error_exit = my_error_exit;
41
+
42
+ if (setjmp(jerr.setjmp_buffer)) {
43
+ jpeg_destroy_decompress(&cinfo);
44
+ fclose(file);
45
+
46
+ return data;
47
+ }
48
+
49
+ jpeg_create_decompress(&cinfo);
50
+ jpeg_stdio_src(&cinfo, file);
51
+ jpeg_read_header(&cinfo, TRUE);
52
+ jpeg_start_decompress(&cinfo);
53
+
54
+ data.width = cinfo.output_width;
55
+ data.height = cinfo.output_height;
56
+ data.pixels = malloc(cinfo.output_components * cinfo.output_width * cinfo.output_height);
57
+
58
+ row_stride = cinfo.output_width * cinfo.output_components;
59
+ buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
60
+
61
+ for (size_t i = 0; cinfo.output_scanline < cinfo.output_height; ++i) {
62
+ jpeg_read_scanlines(&cinfo, buffer, 1);
63
+ memcpy(&data.pixels[i * cinfo.output_width], buffer[0], row_stride);
64
+ }
65
+
66
+ jpeg_finish_decompress(&cinfo);
67
+ jpeg_destroy_decompress(&cinfo);
68
+ fclose(file);
69
+
70
+ return data;
71
+ }
@@ -0,0 +1,75 @@
1
+ #include <png.h>
2
+ #include <stdio.h>
3
+ #include <stdint.h>
4
+ #include <stdlib.h>
5
+ #include <fcntl.h>
6
+
7
+ #include "definitions.h"
8
+
9
+ raster_data read_png_file(const char *file_name)
10
+ {
11
+ FILE *fp = NULL;
12
+ png_bytep *row_pointers = NULL;
13
+ int error = 0;
14
+ raster_data data = {};
15
+
16
+ fp = fopen(file_name, "rb");
17
+ if (fp == NULL) {
18
+ error = 1;
19
+ goto cleanup;
20
+ }
21
+
22
+ // These lines will not fail under any usual circumstances, so it's okay
23
+ // to leak the png ptr if the info ptr can't be allocated
24
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
25
+ png_infop info_ptr = png_create_info_struct(png_ptr);
26
+
27
+ if (!png_ptr || !info_ptr) {
28
+ error = 1;
29
+ goto cleanup;
30
+ }
31
+
32
+ // Set up error handler
33
+ if (setjmp(png_jmpbuf(png_ptr))) {
34
+ error = 1;
35
+ goto cleanup;
36
+ }
37
+
38
+ png_init_io(png_ptr, fp);
39
+ png_set_sig_bytes(png_ptr, 0);
40
+
41
+ png_read_info(png_ptr, info_ptr);
42
+
43
+ data.width = png_get_image_width(png_ptr, info_ptr);
44
+ data.height = png_get_image_height(png_ptr, info_ptr);
45
+
46
+ data.pixels = malloc(png_get_rowbytes(png_ptr, info_ptr) * data.height);
47
+
48
+ png_set_strip_16(png_ptr);
49
+ png_set_strip_alpha(png_ptr);
50
+ png_set_gray_to_rgb(png_ptr);
51
+ png_set_expand(png_ptr);
52
+ png_set_interlace_handling(png_ptr);
53
+ png_read_update_info(png_ptr, info_ptr);
54
+
55
+ row_pointers = png_malloc(png_ptr, data.height * sizeof(png_bytep));
56
+ for (size_t i = 0; i < data.height; ++i)
57
+ row_pointers[i] = (png_bytep) &data.pixels[data.width * i];
58
+
59
+ png_read_image(png_ptr, row_pointers);
60
+ png_free(png_ptr, row_pointers);
61
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
62
+
63
+ fclose(fp);
64
+
65
+ return data;
66
+
67
+ cleanup:
68
+
69
+ if (fp) fclose(fp);
70
+ if (row_pointers) png_free(png_ptr, row_pointers);
71
+ if (png_ptr && info_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
72
+
73
+ data.error = error;
74
+ return data;
75
+ }
@@ -0,0 +1,17 @@
1
+ require_relative 'lib/image_intensities/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'image_intensities'
5
+ s.version = ImageIntensities::VERSION
6
+ s.summary = ''
7
+ s.author = 'Liam P. White'
8
+ s.files = `git ls-files`.split("\n")
9
+ s.license = 'MIT'
10
+ s.extensions = %w[ext/Rakefile]
11
+ s.require_paths = %w[lib]
12
+
13
+ s.add_dependency 'ffi'
14
+ s.add_dependency 'rake'
15
+ s.add_dependency 'rb-libmagic'
16
+
17
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'magic'
4
+
5
+ module ImageIntensities
6
+ def self.file(path)
7
+ case magic.file(path)
8
+ when 'image/png'
9
+ ins = Native.png_intensities(path)
10
+ raise 'Processing error' if ins[:error] != 0
11
+
12
+ { nw: ins[:nw], ne: ins[:ne], sw: ins[:sw], se: ins[:se] }
13
+ when 'image/jpeg'
14
+ ins = Native.jpeg_intensities(path)
15
+ raise 'Processing error' if ins[:error] != 0
16
+
17
+ { nw: ins[:nw], ne: ins[:ne], sw: ins[:sw], se: ins[:se] }
18
+ else
19
+ raise "Unsupported file type `#{mime}'"
20
+ end
21
+ end
22
+
23
+ def self.magic
24
+ @magic ||= Magic.new(Magic::MIME_TYPE)
25
+ end
26
+ end
27
+
28
+ require 'image_intensities/native'
29
+ require 'image_intensities/version'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ module ImageIntensities
6
+ module Native
7
+ extend FFI::Library
8
+
9
+ spec = Gem.loaded_specs['image_intensities']
10
+ gem_root = spec.gem_dir
11
+ ffi_lib "#{gem_root}/ext/image_intensities/lib/libimage_intensities.so"
12
+
13
+ class IntensityData < FFI::Struct
14
+ layout :nw, :double,
15
+ :ne, :double,
16
+ :sw, :double,
17
+ :se, :double,
18
+ :error, :int
19
+ end
20
+
21
+ attach_function :jpeg_intensities, [:string], IntensityData.by_value
22
+ attach_function :png_intensities, [:string], IntensityData.by_value
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageIntensities
4
+ VERSION = '1.0.0'
5
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_intensities
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Liam P. White
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rb-libmagic
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ executables: []
58
+ extensions:
59
+ - ext/Rakefile
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - ext/Rakefile
65
+ - ext/image_intensities/Makefile
66
+ - ext/image_intensities/definitions.h
67
+ - ext/image_intensities/intensities.c
68
+ - ext/image_intensities/jpeg.c
69
+ - ext/image_intensities/png.c
70
+ - image_intensities.gemspec
71
+ - lib/image_intensities.rb
72
+ - lib/image_intensities/native.rb
73
+ - lib/image_intensities/version.rb
74
+ homepage:
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 3.0.3
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: ''
97
+ test_files: []