is_dark 0.2.3
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 +7 -0
- data/lib/is_dark.rb +166 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9dad19d8626d41d0e19a099e5da1439a1159fb484ff836c4c8b3fe42f8f990dd
|
4
|
+
data.tar.gz: f456d8b95650357c374a74b5f5909574d7a8b7f5540fed03f5e2652a51bf2530
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 414aef11db695594ebf025376724235e489a1d8843e93fec57b1862cff0ea5e68e59cf6f7f7d7082dab30cdf12e1339ab4695ec120294524da632fd4026b1362
|
7
|
+
data.tar.gz: 91cdfa5191f92ea826f60ef951df06f5d204d444943ad06b998a3092a7642fbc796824594a090127903135469767eba5a5580ab8098386be29d13b124407ac73
|
data/lib/is_dark.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rmagick'
|
4
|
+
|
5
|
+
# The `Is Dark` class is designed to determine whether a given color or pixel is dark based on luminance
|
6
|
+
# standards defined by the W3C. It utilizes the `rmagick` library to handle image processing tasks.
|
7
|
+
# The class includes methods to analyze colors from hexadecimal values, individual pixels,
|
8
|
+
# and areas within images blobs. It also supports debugging features to visualize the analysis process.
|
9
|
+
#
|
10
|
+
# Key features include:
|
11
|
+
# - **Color Analysis**: Determines if a color is dark based on its luminance.
|
12
|
+
# - **- **Pixel Analysis**: Analysis individual pixels from an image.
|
13
|
+
# - **Area Analysis**: Evaluates the darkness of a specified area within an image blob.
|
14
|
+
# - **Debugging**: Provides options to enable debugging information and visualize the analysis on a PDF file.
|
15
|
+
class IsDark
|
16
|
+
BLUE_LUMINANCE_COEFFICIENT = 0.0722
|
17
|
+
GREEN_LUMINANCE_COEFFICIENT = 0.7152
|
18
|
+
HIGH_LUMINANCE_DIVIDER = 1.055
|
19
|
+
HIGH_LUMINANCE_POWER = 2.4
|
20
|
+
LOW_LUMINANCE_DIVIDER = 12.92
|
21
|
+
LUMINANCE_THRESHOLD = 0.05
|
22
|
+
MAXIMUM_COLOR_DEPTH = 255
|
23
|
+
MAX_COLOR_VALUE_MULTIPLIER = 655
|
24
|
+
MAX_COLOR_VALUE = MAX_COLOR_VALUE_MULTIPLIER * MAXIMUM_COLOR_DEPTH
|
25
|
+
LINEAR_LUMINANCE_THRESHOLD = (1 / (LOW_LUMINANCE_DIVIDER * 100.0)) * MAXIMUM_COLOR_DEPTH
|
26
|
+
NONLINEAR_TRANSFORM_DIVIDER = 1.055
|
27
|
+
NONLINEAR_TRANSFORM_OFFSET = 0.055
|
28
|
+
RED_LUMINANCE_COEFFICIENT = 0.2126
|
29
|
+
DEFAULT_PERCENT_OF_DOTS = 80
|
30
|
+
DEFAULT_MATRIX_RANGE = (0..10).freeze
|
31
|
+
DEFAULT_DEBUG_FILE_PATH = '/tmp/is_dark_debug_file.pdf'
|
32
|
+
|
33
|
+
@r = 0
|
34
|
+
@g = 0
|
35
|
+
@b = 0
|
36
|
+
@colorset = MAXIMUM_COLOR_DEPTH
|
37
|
+
@percent = DEFAULT_PERCENT_OF_DOTS
|
38
|
+
@matrix = DEFAULT_MATRIX_RANGE
|
39
|
+
@luminance = LUMINANCE_THRESHOLD
|
40
|
+
@with_not_detected_as_white = true
|
41
|
+
@with_debug = false
|
42
|
+
@with_debug_file = false
|
43
|
+
@debug_file_path = DEFAULT_DEBUG_FILE_PATH
|
44
|
+
|
45
|
+
def initialize(settings = {})
|
46
|
+
configure(settings)
|
47
|
+
end
|
48
|
+
|
49
|
+
def configure(settings = {})
|
50
|
+
@percent = settings[:percent] || DEFAULT_PERCENT_OF_DOTS
|
51
|
+
@matrix = settings[:matrix] || DEFAULT_MATRIX_RANGE
|
52
|
+
@luminance = settings[:luminance] || LUMINANCE_THRESHOLD
|
53
|
+
@with_not_detected_as_white = settings[:with_not_detected_as_white] || true
|
54
|
+
@with_debug = settings[:with_debug] || false
|
55
|
+
@with_debug_file = settings[:with_debug_file] || false
|
56
|
+
@debug_file_path = settings[:debug_file_path] || DEFAULT_DEBUG_FILE_PATH
|
57
|
+
end
|
58
|
+
|
59
|
+
def color(hex)
|
60
|
+
@r, @g, @b = hex.match(/^#(..)(..)(..)$/).captures.map(&:hex)
|
61
|
+
@colorset = MAXIMUM_COLOR_DEPTH
|
62
|
+
dark?
|
63
|
+
end
|
64
|
+
|
65
|
+
def magick_pixel(pix, x = nil, y = nil)
|
66
|
+
@r = pix.red.to_f
|
67
|
+
@g = pix.green.to_f
|
68
|
+
@b = pix.blue.to_f
|
69
|
+
@colorset = MAX_COLOR_VALUE
|
70
|
+
dark?(x, y)
|
71
|
+
end
|
72
|
+
|
73
|
+
def magick_pixel_from_blob(x, y, blob)
|
74
|
+
image = Magick::Image.read(blob).first
|
75
|
+
pix = image.pixel_color(x, y)
|
76
|
+
magick_pixel(pix, x, y)
|
77
|
+
end
|
78
|
+
|
79
|
+
# (x, y) is the left corner of an element over a blob, height and width is the element's size
|
80
|
+
def magick_area_from_blob(x, y, blob, height, width)
|
81
|
+
image = Magick::Image.read(blob).first
|
82
|
+
dark = false
|
83
|
+
dots = []
|
84
|
+
@matrix.each do |xx|
|
85
|
+
@matrix.each do |yy|
|
86
|
+
dots << { x: (x + (width * xx / @matrix.count)).to_i, y: (y + (height * yy / @matrix.count)).to_i }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
points = 0
|
91
|
+
if @with_debug_file
|
92
|
+
old_x = false
|
93
|
+
old_y = false
|
94
|
+
end
|
95
|
+
p '==================================================================================' if @with_debug
|
96
|
+
dots.each do |dot|
|
97
|
+
x = dot[:x].to_i
|
98
|
+
y = dot[:y].to_i
|
99
|
+
pix = image.pixel_color(x, y)
|
100
|
+
l = magick_pixel(pix, x, y)
|
101
|
+
points += 1 if l
|
102
|
+
next unless @with_debug_file
|
103
|
+
|
104
|
+
draw_debug_files(image, x, y, old_x, old_y)
|
105
|
+
old_y = y
|
106
|
+
old_x = x
|
107
|
+
end
|
108
|
+
dark = true if points >= (dots.length / 100) * @percent
|
109
|
+
if @with_debug
|
110
|
+
percent_calculated = points/(dots.length / 100)
|
111
|
+
p '=================================================================================='
|
112
|
+
p "Total Points: #{dots.length}, dark points amount:#{points}"
|
113
|
+
p "Is \"invert to white not detectd pixels\" option enabled?:#{@with_not_detected_as_white}"
|
114
|
+
p "Percent of dark dots in the matrix: #{percent_calculated}%"
|
115
|
+
p "Percent to consider as a dark area from settings: #{@percent}%"
|
116
|
+
p "Luminance value is: #{@luminance}"
|
117
|
+
p "Is Area Dark?: #{dark}"
|
118
|
+
p "have a look on #{@debug_file_path} file to see your tested area of a blob" if @with_debug_file
|
119
|
+
p '=================================================================================='
|
120
|
+
end
|
121
|
+
dark
|
122
|
+
end
|
123
|
+
|
124
|
+
def draw_debug_files(image, x, y, old_x, old_y)
|
125
|
+
return unless old_x && old_y
|
126
|
+
|
127
|
+
gc = Magick::Draw.new
|
128
|
+
gc.line(x, y, old_x, old_y)
|
129
|
+
gc.stroke('black')
|
130
|
+
gc.draw(image)
|
131
|
+
image.write(@debug_file_path)
|
132
|
+
end
|
133
|
+
|
134
|
+
# detects a dark color based on luminance W3 standarts ( https://www.w3.org/TR/WCAG20/#relativeluminancedef )
|
135
|
+
def dark?(x = nil, y = nil)
|
136
|
+
dark = false
|
137
|
+
inverted = false
|
138
|
+
pixel = [@r.to_f, @g.to_f, @b.to_f]
|
139
|
+
return true if pixel == [0.00, 0.00, 0.00] # hardcoded exception
|
140
|
+
|
141
|
+
if @with_not_detected_as_white && pixel[0] == 0.0 && pixel[1] == 0.0 && pixel[2] == 0.0
|
142
|
+
pixel = [MAXIMUM_COLOR_DEPTH, MAXIMUM_COLOR_DEPTH, MAXIMUM_COLOR_DEPTH]
|
143
|
+
inverted = true
|
144
|
+
end
|
145
|
+
calculated = []
|
146
|
+
pixel.each do |color|
|
147
|
+
color /= @colorset
|
148
|
+
if color <= LINEAR_LUMINANCE_THRESHOLD
|
149
|
+
color /= LOW_LUMINANCE_DIVIDER
|
150
|
+
else
|
151
|
+
color = ((color + NONLINEAR_TRANSFORM_OFFSET) / NONLINEAR_TRANSFORM_DIVIDER)**HIGH_LUMINANCE_POWER
|
152
|
+
end
|
153
|
+
calculated << color
|
154
|
+
end
|
155
|
+
l = (RED_LUMINANCE_COEFFICIENT * calculated[0]) +
|
156
|
+
(GREEN_LUMINANCE_COEFFICIENT * calculated[1]) +
|
157
|
+
(BLUE_LUMINANCE_COEFFICIENT * calculated[2])
|
158
|
+
dark = true if l <= @luminance
|
159
|
+
if @with_debug
|
160
|
+
debug = { X: x, Y: y, R: @r, G: @g, B: @b, 'luminance value': l, dark?: dark,
|
161
|
+
'inverted to white': inverted }
|
162
|
+
p debug
|
163
|
+
end
|
164
|
+
dark
|
165
|
+
end
|
166
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: is_dark
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergei Illarionov
|
8
|
+
- Liamshin Ilia
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2025-02-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rmagick
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '5.2'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '5.2'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rspec
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3.13'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.13'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rubocop
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.69'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.69'
|
56
|
+
description: "Detects a dark color based on luminance W3 standarts ( https://www.w3.org/TR/WCAG20/#relativeluminancedef
|
57
|
+
). \n\n It has these options: \n * is a hex color dark \n * is an Imagick pixel
|
58
|
+
dark \n * is an Imagick pixel from a blob dark \n * is an area in a blob over a
|
59
|
+
dark background (uses Imagick for it too). \n\n An example practical aspect: it
|
60
|
+
can be useful to understand will a black colored text be visible or not over an
|
61
|
+
area."
|
62
|
+
email: butteff.ru@gmail.com
|
63
|
+
executables: []
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- lib/is_dark.rb
|
68
|
+
homepage: https://butteff.ru/en/
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata:
|
72
|
+
source_code_uri: https://github.com/butteff/is_dark_ruby_gem
|
73
|
+
rubygems_mfa_required: 'true'
|
74
|
+
post_install_message: You can find docs about is_dark gem on https://butteff.ru/en/extensions
|
75
|
+
or on https://github.com/butteff/is_dark_ruby_gem
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 2.7.0
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubygems_version: 3.4.19
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: Detects a dark background under an area or by a color code
|
94
|
+
test_files: []
|