image_quality_check 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b5fbfb17831bb894d3890c10197aff1573ca1f6fbdcf0308f5ec1975cfd35cc6
4
+ data.tar.gz: d5f5fdf1a2cb61f92566b26cc5954582a94116c056004a24323b93984307507d
5
+ SHA512:
6
+ metadata.gz: 594ca886f91f517ef6b71fda2cbf350cb4c41991434c3071aaad6f9e63d0d48a71fb1acc08a01835f2e927dd9774cb19655a9d63c63b5829b82796f22e6f6304
7
+ data.tar.gz: 2b20142b04050a37d1b2655f6188ec69ac534f175acdf4234c4c07edc4760f3ec0b675f255afda053f080e836c852f93f69aa8c972441f80a58cabcff254bba4
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ spec/.examples.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in image_quality.gemspec
4
+ gemspec
5
+
6
+ gem 'pry'
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'rubocop'
10
+ gem 'solargraph'
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Stefan Wienert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,145 @@
1
+ # ImageQuality
2
+
3
+ Thin gem wrapper that uses ``imagemagick`` and ``python-opencv`` to help determine image quality.
4
+
5
+
6
+ ## Installation
7
+
8
+ System dependencies:
9
+
10
+ - Have imagemagick installed (Tool uses ``identify``).
11
+ - have python3 + OpenCV installed: Install OpenCV, e.g. on Ubuntu 18.04:
12
+
13
+ ```
14
+ apt-get install python3-pywt python3-opencv
15
+ ```
16
+
17
+ ---
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'image_quality'
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Direct usage without model integration
28
+
29
+ ```
30
+ result = ImageQualityCheck.analyze(path_to_file)
31
+
32
+ {
33
+ blur: {
34
+ LPScale: 100
35
+ }
36
+ face: null,
37
+ haseSmile: false,
38
+ width: 836,
39
+ format: 'jpeg',
40
+ height: 604,
41
+ quality: 90,
42
+ mime_type: 'image/jpeg',
43
+ }
44
+ ```
45
+
46
+ This tool uses a shipped Python executable from https://github.com/pedrofrodenas/blur-Detection-Haar-Wavelet
47
+
48
+ The ``blur.LPScale`` gives you info if the image is blurry on a scale of 0..100
49
+
50
+ ### DSL for defining rules for Models
51
+
52
+ **LIMITS**:
53
+
54
+ - currently only Paperclip attachment is implemented. Feel free to add other get-the-tmpfile adapter to ``ImageQualityCheck::DetermineQuality#read``
55
+
56
+ ---
57
+
58
+ Create a initializer in your app:
59
+
60
+ ```ruby
61
+ # config/initializers/image_quality.rb
62
+
63
+ # define your quality rules per attachment:
64
+ ImageQuality.define_rules_for Person, attachment: :photo do
65
+ # jpeg and png yields 100 score in this segment
66
+ preferred_formats_rule(jpeg: 100, png: 100)
67
+
68
+ # Sizes with w>400 and h>500 will give 100 score,
69
+ # otherwise linearly between 0 .. 100
70
+ preferred_size_rule(400, 500)
71
+ rule "Photo of a person" do |result, on_error|
72
+ if result[:blur].slice(:face, :hasSmile, :eyes).any?
73
+ # detector has either detected face, smile or eyes
74
+ 100
75
+ else
76
+ on_error.call("No face found")
77
+ 0
78
+ end
79
+ end
80
+
81
+ rule "Bluriness/Sharpness" do |result, on_error|
82
+ blur = result[:blur]
83
+ # overall picture is sharp (0...100)
84
+ if blur[:LPScale] > 70
85
+ 100
86
+ else
87
+ # also including: you could check LPScale for eyes, face only
88
+ result = (blur[:LPScale] / 80 * 100).round
89
+ on_error.call("Contrast or focus is not optimal")
90
+ result
91
+ end
92
+ end
93
+ end
94
+
95
+ ImageQuality.define_rules_for Organisation, attachment: :logo do
96
+ preferred_formats_rule(png: 100, svg: 100, jpeg: 50, gif: 50)
97
+ preferred_size_rule(600, 400)
98
+ end
99
+
100
+ ```
101
+
102
+ Then you can query for the quality rules to evaluate an attachment:
103
+
104
+ ```ruby
105
+ ImageQuality.determine_quality(some_organisation, :logo) do |result|
106
+ ```
107
+
108
+ ### Using example Result model
109
+
110
+ There is an ActiveRecord example model included in this Gem to save the ImageQuality Results to the database. It uses json column so that (modern) mysql/psql are both supported without any conditionals.
111
+
112
+ Run:
113
+
114
+ ```
115
+ bundle exec rake image_quality_check_engine:install:migrations
116
+ bin/rails db:migrate
117
+ ```
118
+
119
+ Then you can use this class like so (Example):
120
+
121
+ ```ruby
122
+ # add to initializer:
123
+ require 'image_quality/model'
124
+ ImageQuality.determine_quality(some_organisation, :logo) do |result|
125
+ ImageQualityCheck::Result.create_for_result(some_organisation, column, result)
126
+ end
127
+ ```
128
+
129
+ You also want to add a has_one relationship to the host models:
130
+
131
+ ```ruby
132
+ class Person < AR
133
+ has_one :image_quality_check_result, -> { where(attachable_column: :logo) }, as: :attachable, dependent: :destroy, class_name: "ImageQualityCheck::Result"
134
+ end
135
+ ```
136
+
137
+
138
+ ## Contributing
139
+
140
+ Bug reports and pull requests are welcome on GitHub at https://github.com/zealot128/image_quality.
141
+
142
+
143
+ ## License
144
+
145
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "image_quality"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ ---
2
+ de:
3
+ image_quality_check:
4
+ not_found: Kein Anhang/Bild gefunden
5
+ dsl:
6
+ no_qualities_defined_for: No qualities defined for [%{klass}, %{attachment}]
7
+ format: Format
8
+ format_ist_nutzen_sie: Format ist %{result_format} - Nutzen Sie %{formats_keys_map_upcase_jo}
9
+ gro_e: Größe
10
+ gro_e_ist_x_px_achten_sie: Größe ist %{result_width}x%{result_height}px - Achten
11
+ Sie auf eine Mindestgröße von min. %{expected_width}x%{expected_height}px
@@ -0,0 +1,10 @@
1
+ ---
2
+ en:
3
+ image_quality_check:
4
+ not_found: "No attachment/photo found"
5
+ dsl:
6
+ no_qualities_defined_for: No qualities defined for [%{klass}, %{attachment}]
7
+ format: Format
8
+ format_ist_nutzen_sie: Format is %{result_format} - please prefer %{formats_keys_map_upcase_jo}
9
+ gro_e: Image dimension
10
+ gro_e_ist_x_px_achten_sie: Size is %{result_width}x%{result_height}px - Please use a minimum size of %{expected_width}x%{expected_height}px
@@ -0,0 +1,15 @@
1
+ class CreateImageQualityChecks < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :image_quality_check_results do |t|
4
+ t.string :attachable_type
5
+ t.string :attachable_id
6
+ t.string :attachable_column
7
+ t.json :result
8
+ t.integer :quality
9
+
10
+ t.timestamps
11
+ t.index [:attachable_type, :attachable_id], name: 'index_image_quality_checks_on_attachable'
12
+ t.index [:attachable_type, :attachable_id, :attachable_column], unique: true, name: 'index_image_quality_checks_on_all'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import sys
4
+ import json
5
+ import numpy as np
6
+ import cv2
7
+
8
+ null = None
9
+
10
+ img = cv2.imread(sys.argv[1], 0)
11
+
12
+ clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
13
+ img = clahe.apply(img)
14
+
15
+ face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
16
+ faces_detect = face_cascade.detectMultiScale(img, scaleFactor=1.5, minNeighbors=3)
17
+
18
+ for (x, y, w, h) in faces_detect:
19
+ # cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
20
+ face = img[y:y + h, x:x + w]
21
+
22
+ out = {}
23
+
24
+ if len(faces_detect) > 0:
25
+ out['face'] = {}
26
+ local = np.max(cv2.convertScaleAbs(cv2.Laplacian(face,3)))
27
+ laplace_face = cv2.Laplacian(face, cv2.CV_64F).var()
28
+ out['face']['FaceBlurScale'] = int(local / 255 * 100)
29
+ out['face']['FaceBlurLaplaceVar'] = laplace_face
30
+ else:
31
+ out['face'] = null
32
+ face = img
33
+
34
+
35
+ smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_smile.xml')
36
+ smile_detect = smile_cascade.detectMultiScale(face, minNeighbors=40, scaleFactor=1.14)
37
+
38
+ eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
39
+ eye_detect = eye_cascade.detectMultiScale(face, minNeighbors=15)
40
+
41
+ out['hasSmile'] = len(smile_detect) > 0
42
+
43
+ if len(eye_detect) > 0:
44
+ for (x, y, w, h) in eye_detect:
45
+ eye = img[y:y + h, x:x + w]
46
+ local_laplace = np.max(cv2.convertScaleAbs(cv2.Laplacian(eye, 3)))
47
+ laplace_var = cv2.Laplacian(eye, cv2.CV_64F).var()
48
+ out['eyes'] = {
49
+ 'LPScale': int(local_laplace / 255 * 100),
50
+ 'LPVar': laplace_var
51
+ }
52
+ else:
53
+ out['eyes'] = null
54
+
55
+ laplace = cv2.Laplacian(img, cv2.CV_64F).var()
56
+ local_laplace = np.max(cv2.convertScaleAbs(cv2.Laplacian(img, 3)))
57
+ out['LPVar'] = laplace
58
+ out['LPScale'] = (local_laplace / 255 * 100)
59
+
60
+ print(json.dumps(out))
@@ -0,0 +1,28 @@
1
+ require_relative 'lib/image_quality_check/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "image_quality_check"
5
+ spec.version = ImageQualityCheck::VERSION
6
+ spec.authors = ["Stefan Wienert"]
7
+ spec.email = ["info@stefanwienert.de"]
8
+
9
+ spec.summary = %q{image quality}
10
+ spec.description = %q{image quality}
11
+ spec.homepage = "https://github.com/pludoni/image_quality_check"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = spec.homepage
17
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+ spec.add_runtime_dependency "i18n"
28
+ end
@@ -0,0 +1,61 @@
1
+ require "image_quality_check/version"
2
+ require 'image_quality_check/dsl'
3
+ require 'image_quality_check/determine_quality'
4
+ require 'shellwords'
5
+ require 'json'
6
+ require 'open3'
7
+
8
+ module ImageQualityCheck
9
+ extend ImageQualityCheck::DSL
10
+
11
+ def self.determine_quality(model, attachment, &block)
12
+ ImageQualityCheck::DetermineQuality.run(model, attachment, &block)
13
+ end
14
+
15
+ def self.analyze(path_to_image)
16
+ out = `convert #{Shellwords.escape path_to_image} json:`
17
+ # image magick gif delay bug invalid json
18
+ # https://github.com/ImageMagick/ImageMagick/issues/1624
19
+ out.gsub!(/("delay": "[^"]+")\n/m, "\\1,\n")
20
+ json = JSON.parse(out)['image']
21
+ background_is_transparent =
22
+ json.dig('channelDepth', 'alpha') &&
23
+ json['channelStatistics']['alpha']['min'] != json['channelStatistics']['alpha']['max']
24
+ {
25
+ format: json['format'].downcase,
26
+ mime_type: json['mimeType'],
27
+ background_is_transparent: background_is_transparent,
28
+ width: json.dig('geometry', 'width'),
29
+ height: json.dig('geometry', 'height'),
30
+ quality: json['quality'],
31
+ blur: blur_detect(path_to_image).map { |k, v| [ k.to_sym, v ] }.to_h
32
+ }
33
+ end
34
+
35
+ def self.blur_detect(path_to_image)
36
+ script = File.join(File.dirname(__FILE__), '..', 'exe', 'image_quality_blur')
37
+ out, err, value = Open3.capture3("#{script} #{Shellwords.escape(path_to_image)}")
38
+ if value.success?
39
+ JSON.parse(out.gsub('NaN', '0'))
40
+ else
41
+ if out[/^\{/]
42
+ JSON.parse(out)
43
+ else
44
+ {
45
+ error: err.to_s,
46
+ out: out.to_s
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ if defined?(Rails)
54
+ class ImageQualityCheck::Engine < Rails::Engine
55
+ end
56
+ else
57
+ require 'i18n'
58
+ I18n.load_path << Dir[File.expand_path("config/locales") + "/*.yml"]
59
+ I18n.default_locale ||= :en
60
+ end
61
+
@@ -0,0 +1,76 @@
1
+ require 'i18n'
2
+
3
+ class ImageQualityCheck::DetermineQuality
4
+ def self.run(model, column_name, tmp_file = nil, &block)
5
+ new(model, column_name, tmp_file).run(&block)
6
+ end
7
+
8
+ def initialize(model, column_name, tmp_file = nil)
9
+ @model = model
10
+ @column_name = column_name
11
+ @column = model.send(column_name)
12
+ @messages = []
13
+ @tmp_file = tmp_file
14
+ end
15
+
16
+ def run(&block)
17
+ unless @tmp_file
18
+ @tmp_file = Tempfile.new(['image_quality'])
19
+ unless read!(@tmp_file)
20
+ result = {
21
+ quality: 0,
22
+ details: {},
23
+ messages: [{ name: I18n.t('image_quality_check.not_found'), quality: 0 }]
24
+ }
25
+ yield(result) if block_given?
26
+ return result
27
+ end
28
+ end
29
+
30
+ @analyse_result = ImageQualityCheck.analyze(@tmp_file.path)
31
+ result = {
32
+ quality: determine_quality,
33
+ details: @analyse_result,
34
+ messages: @messages,
35
+ }
36
+ yield(result) if block_given?
37
+ result
38
+ end
39
+
40
+ private
41
+
42
+ def determine_quality
43
+ qualities = []
44
+ sum_of_weights = 0
45
+ ImageQualityCheck.rules_for(@model.class, @column_name).each do |qq|
46
+ error = nil
47
+ on_error = ->(msg) { error = msg }
48
+ result = instance_exec(@analyse_result, on_error, &qq[:block])
49
+ @messages << {
50
+ name: qq[:name],
51
+ quality: result,
52
+ message: error
53
+ }
54
+ if result
55
+ qualities << result * qq[:weight]
56
+ sum_of_weights += qq[:weight]
57
+ end
58
+ end
59
+
60
+ (qualities.sum / sum_of_weights.to_f).round
61
+ end
62
+
63
+ def read!(tmp_file)
64
+ case @column.class.to_s
65
+ when "Paperclip::Attachment"
66
+ if !@column.path || !File.exist?(@column.path)
67
+ false
68
+ else
69
+ FileUtils.cp(@column.path, tmp_file.path)
70
+ true
71
+ end
72
+ else
73
+ raise NotImplementedError, @column.class
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,56 @@
1
+ require 'i18n'
2
+ # rubocop:disable Style/ClassVars,Metrics/BlockLength
3
+ module ImageQualityCheck::DSL
4
+ def define_rules_for(klass, attachment:, &block)
5
+ @rules ||= {}
6
+ @rules[[klass.to_s, attachment.to_s]] = block
7
+ end
8
+
9
+ def rules_for(klass, attachment)
10
+ rule = @rules[[klass.to_s, attachment.to_s]]
11
+ unless rule
12
+ raise NotImplemented, I18n.t("image_quality_check.dsl.no_qualities_defined_for", klass: (klass), attachment: (attachment))
13
+ end
14
+ @current_rule = []
15
+ class_exec(&rule)
16
+ @current_rule
17
+ end
18
+
19
+ def rule(name, weight: 1, &block)
20
+ @current_rule << { name: name, block: block, weight: weight }
21
+ end
22
+
23
+ def preferred_formats_rule(formats, weight: 1)
24
+ rule I18n.t("image_quality_check.dsl.format"), weight: weight do |result, on_error|
25
+ final_score = nil
26
+ formats.each do |f, score|
27
+ if result[:format].downcase.to_s == f.downcase.to_s
28
+ final_score ||= score
29
+ end
30
+ end
31
+ final_score ||= 0
32
+ if final_score < 100
33
+ message = I18n.t("image_quality_check.dsl.format_ist_nutzen_sie",
34
+ result_format: (result[:format]),
35
+ formats_keys_map_upcase_jo: (formats.keys.map(&:upcase).join(', ')))
36
+ on_error.call(message)
37
+ end
38
+ final_score
39
+ end
40
+ end
41
+
42
+ def preferred_size_rule(expected_width, expected_height, weight: 2)
43
+ rule I18n.t("image_quality_check.dsl.gro_e"), weight: weight do |result, on_error|
44
+ if result[:width] >= expected_width && result[:height] >= expected_height
45
+ 100
46
+ else
47
+ target = expected_width * expected_height
48
+ current = result[:width] * result[:height]
49
+ on_error.call(
50
+ I18n.t("image_quality_check.dsl.gro_e_ist_x_px_achten_sie", result_width: (result[:width]), result_height: (result[:height]), expected_width: (expected_width), expected_height: (expected_height))
51
+ )
52
+ [current / target.to_f * 100, 90].min.round
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+
2
+ # == Schema Information
3
+ #
4
+ # Table name: image_quality_check_results
5
+ #
6
+ # id :bigint(8) not null, primary key
7
+ # attachable_column :string(255)
8
+ # attachable_type :string(255)
9
+ # quality :integer
10
+ # result :json
11
+ # created_at :datetime not null
12
+ # updated_at :datetime not null
13
+ # attachable_id :string(255)
14
+ #
15
+ # Indexes
16
+ #
17
+ # index_image_quality_checks_on_all (attachable_type,attachable_id,attachable_column) UNIQUE
18
+ # index_image_quality_checks_on_attachable (attachable_type,attachable_id)
19
+ #
20
+
21
+ class ImageQualityCheck::Result < ApplicationRecord
22
+ self.table_name = 'image_quality_check_results'
23
+ belongs_to :attachable, polymorphic: true
24
+
25
+ def self.create_for_result(attachable, column, result)
26
+ check = ImageQualityCheck::Result.where(attachable: attachable, attachable_column: column).first_or_initialize
27
+ check.quality = result[:quality]
28
+ check.result = {
29
+ details: result[:details],
30
+ messages: result[:messages],
31
+ }
32
+ check.save!
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module ImageQualityCheck
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_quality_check
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stefan Wienert
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: i18n
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
+ description: image quality
28
+ email:
29
+ - info@stefanwienert.de
30
+ executables:
31
+ - image_quality_blur
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".rspec"
37
+ - Gemfile
38
+ - LICENSE.txt
39
+ - README.md
40
+ - Rakefile
41
+ - bin/console
42
+ - bin/setup
43
+ - config/locales/image_quality.de.yml
44
+ - config/locales/image_quality.en.yml
45
+ - db/migrate/20201008124025_create_image_quality_checks.rb
46
+ - exe/image_quality_blur
47
+ - image_quality_check.gemspec
48
+ - lib/image_quality_check.rb
49
+ - lib/image_quality_check/determine_quality.rb
50
+ - lib/image_quality_check/dsl.rb
51
+ - lib/image_quality_check/model.rb
52
+ - lib/image_quality_check/version.rb
53
+ homepage: https://github.com/pludoni/image_quality_check
54
+ licenses:
55
+ - MIT
56
+ metadata:
57
+ homepage_uri: https://github.com/pludoni/image_quality_check
58
+ source_code_uri: https://github.com/pludoni/image_quality_check
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 2.3.0
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 2.7.6
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: image quality
79
+ test_files: []