cuterb 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 016aeb88be1f56f01e4083e2d395b72fcf5ecb8894c9f801eba918bbc8abd520
4
- data.tar.gz: e0b596e08a8abece9c32b032ef90b651582cc696c5d9b37a4460e27126a185d7
3
+ metadata.gz: 54a6a1d3dfe28a92a4ab84de75e2103bb7ef9cc809df6bedd74f65eda8087aed
4
+ data.tar.gz: 62c6f5dcf4000786583e31539ce6b4bb81ef7c1dd78470fcc2c6d312e9ff11c8
5
5
  SHA512:
6
- metadata.gz: 2e03434376382e3c9a8b2f916845e46572222e2f15683e45215c83fd21740d18c29e1b2b8e15b975cd5c49924b0ad4c4bff4334e1f3a5f3cb84d3994a9c5851f
7
- data.tar.gz: c91e3bf27d89a5f8d795382e84aa5dfda72d3e7e6c440cb539aa88a31de159fd45b7d9c57679c7bf1458defc0d1aa16c54ef658d9c9d7e3fe8f7e7488e613210
6
+ metadata.gz: 879568b92cfe196013f82fc0915b074132c71da09d2e9e4888f2a4d34dcf8595a9b726e81ca7404e5e9bf26c4e9eb77bfb64795436bb08f4cd605a19909fe3ff
7
+ data.tar.gz: cf0ecb6153746b7e2dc12a962cd669c4533fde762d9786fd34a03b34870c393ca62d31c304c87bc2de3604e2d98634612fa01e4b985c47a78e265a8a09c770b2
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cuterb (0.1.0)
4
+ cuterb (0.2.0)
5
5
  rmagick
6
6
  rqrcode
7
7
 
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
1
  # CuteRB
2
+ [sample/](https://github.com/lrks/cuteRB/tree/master/sample)
3
+
4
+ <img src="https://raw.githubusercontent.com/lrks/cuteRB/master/sample/puhaa_qr.png" width="50%" />
5
+
2
6
  ```
3
7
  $ cuterb
4
8
  cuterb TEXT INPUT_IMAGE_FILE OUTPUT_IMAGE_FILE
@@ -10,3 +14,7 @@ $ cuterb "hogehoge" sample/image.png output/qr.png
10
14
  ```
11
15
  $ gem instlal cuterb
12
16
  ```
17
+
18
+ ## Inspired by
19
+ * [chinuno\-usami/CuteR](https://github.com/chinuno-usami/CuteR)
20
+ * [kciter/qart\.js: Generate artistic QR code\. 🎨](https://github.com/kciter/qart.js)
data/exe/cuterb CHANGED
@@ -1,9 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
  require "cuterb"
3
3
 
4
+ if ['-v', '--version'].include?(ARGV[0])
5
+ puts CuteRB::VERSION
6
+ exit 0
7
+ end
8
+
4
9
  if ARGV.length != 3
5
- puts "#{$0} TEXT INPUT_IMAGE_FILE OUTPUT_IMAGE_FILE"
6
- exit 1
10
+ puts "#{$0} TEXT INPUT_IMAGE_FILE OUTPUT_IMAGE_FILE"
11
+ exit 1
7
12
  end
8
13
 
9
14
  c = CuteRB::CLI.new(ARGV[0], ARGV[1], ARGV[2])
data/lib/cuterb.rb CHANGED
@@ -2,77 +2,97 @@ require 'cuterb/version'
2
2
  require 'rqrcode'
3
3
  require_relative './utils.rb'
4
4
  require 'rmagick'
5
+ require 'tempfile'
5
6
 
6
7
  module CuteRB
7
8
  class Error < StandardError; end
8
9
 
9
10
  class CLI
10
- def initialize(text, image, output)
11
- @qr = RQRCode::QRCode.new(text, :level => :h)
11
+ def initialize(text, image, output)
12
+ @qr = RQRCode::QRCode.new(text, :level => :h)
12
13
 
13
14
  @image = Magick::ImageList.new(image).first
14
- @image = @image.crop(0, 0, [@image.columns, @image.rows].min, [@image.columns, @image.rows].min)
15
- raise "#{image} too small." if @image.columns < @qr.module_count
16
- @image = @image.quantize(256, Magick::GRAYColorspace)
17
- bg = Magick::Image.new(@image.columns, @image.columns) { self.background_color = "white" }
18
- @image = bg.composite(@image, 0, 0, Magick::OverCompositeOp).normalize.contrast(true).contrast(true)
15
+ short = [@image.columns, @image.rows].min
16
+ raise "#{image} too small." if short < @qr.module_count
19
17
 
18
+ # crop image
19
+ if @image.columns != @image.rows
20
+ begin
21
+ tmp = Tempfile.new(['cuterb-canny', '.png'])
22
+ system("convert #{image} -canny 0x1+10%+30% #{tmp.path}")
23
+ raise 'convert error' unless $?.success?
24
+ canny = Magick::ImageList.new(tmp.path).first
25
+ start = Utils.contents_area(canny)
26
+ rescue RuntimeError, Magick::ImageMagickError, Magick::FatalImageMagickError, Magick::DestroyedImageError => e
27
+ start = 0
28
+ end
29
+
30
+ if @image.columns == short
31
+ @image = @image.crop(0, start, short, start+short)
32
+ else
33
+ @image = @image.crop(start, 0, start+short, short)
34
+ end
35
+ end
36
+ @image = @image.quantize(256, Magick::GRAYColorspace)
37
+
38
+ bg = Magick::Image.new(@image.columns, @image.columns) { self.background_color = "white" }
39
+ @image = bg.composite(@image, 0, 0, Magick::OverCompositeOp).normalize.contrast(true).contrast(true)
20
40
  @output = output
21
41
  end
22
42
 
23
43
  def run()
24
- pixel = qrpixel()
25
- overlay(pixel)
26
- @image.write(@output)
44
+ pixel = qrpixel()
45
+ overlay(pixel)
46
+ @image.write(@output)
27
47
  return 0
28
48
  end
29
49
 
30
50
 
31
51
  private
32
- def qrpixel()
52
+ def qrpixel()
33
53
  data = @qr.to_s(:dark => 'b', :light => 'w').delete("\n").chars
34
- Utils.place_position_probe_pattern(data, 0, 0)
35
- Utils.place_position_probe_pattern(data, @qr.module_count - 7, 0)
36
- Utils.place_position_probe_pattern(data, 0, @qr.module_count - 7)
37
- Utils.place_position_adjust_pattern(data, @qr.version)
38
- return data
39
- end
54
+ Utils.place_position_probe_pattern(data, 0, 0)
55
+ Utils.place_position_probe_pattern(data, @qr.module_count - 7, 0)
56
+ Utils.place_position_probe_pattern(data, 0, @qr.module_count - 7)
57
+ Utils.place_position_adjust_pattern(data, @qr.version)
58
+ return data
59
+ end
40
60
 
41
- def overlay(pixel, size=0.2, black_threshold=75, white_threshold=185)
42
- scale = @image.columns / @qr.module_count.to_f
43
- offset = (1 - size) * 0.5 * scale
44
- size = size * scale
61
+ def overlay(pixel, size=0.2, black_threshold=75, white_threshold=185)
62
+ scale = @image.columns / @qr.module_count.to_f
63
+ offset = (1 - size) * 0.5 * scale
64
+ size = size * scale
45
65
 
46
- for row in 0...@qr.module_count
47
- for col in 0...@qr.module_count
48
- cell = pixel[row * @qr.module_count + col]
66
+ for row in 0...@qr.module_count
67
+ for col in 0...@qr.module_count
68
+ cell = pixel[row * @qr.module_count + col]
49
69
 
50
- x = col * scale
51
- y = row * scale
52
- if cell.downcase == cell
53
- x1 = (x + offset).round
54
- y1 = (y + offset).round
55
- xy = size.ceil
70
+ x = col * scale
71
+ y = row * scale
72
+ if cell.downcase == cell
73
+ x1 = (x + offset).round
74
+ y1 = (y + offset).round
75
+ xy = size.ceil
56
76
 
57
- mean = @image.export_pixels(x1, y1, xy, xy, 'R').map{ |px| px/257.0 }.sum(0.0) / (xy * xy)
58
- next if ((cell == 'b' && mean < black_threshold) || (cell == 'w' && mean > white_threshold))
59
- else
60
- x1 = x.round
61
- y1 = y.round
62
- xy = scale.ceil
63
- end
77
+ mean = @image.export_pixels(x1, y1, xy, xy, 'R').map{ |px| px/257.0 }.sum(0.0) / (xy * xy)
78
+ next if ((cell == 'b' && mean < black_threshold) || (cell == 'w' && mean > white_threshold))
79
+ else
80
+ x1 = x.round
81
+ y1 = y.round
82
+ xy = scale.ceil
83
+ end
64
84
 
65
- draw = Magick::Draw.new
66
- draw.fill((cell.downcase == 'w') ? '#ffffff' : '#000000')
67
- draw.rectangle(x1, y1, x1+xy, y1+xy)
68
- draw.draw(@image)
69
- end
70
- end
85
+ draw = Magick::Draw.new
86
+ draw.fill((cell.downcase == 'w') ? '#ffffff' : '#000000')
87
+ draw.rectangle(x1, y1, x1+xy, y1+xy)
88
+ draw.draw(@image)
89
+ end
90
+ end
71
91
 
72
- quiet = 4 * scale
73
- width = (@image.columns + quiet * 2).ceil
74
- bg = Magick::Image.new(width, width) { self.background_color = "white" }
75
- @image = bg.composite(@image, quiet.round, quiet.round, Magick::OverCompositeOp)
76
- end
92
+ quiet = 4 * scale
93
+ width = (@image.columns + quiet * 2).ceil
94
+ bg = Magick::Image.new(width, width) { self.background_color = "white" }
95
+ @image = bg.composite(@image, quiet.round, quiet.round, Magick::OverCompositeOp)
96
+ end
77
97
  end
78
98
  end
@@ -1,3 +1,3 @@
1
1
  module CuteRB
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/utils.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  module CuteRB
2
2
  class Utils
3
- # rqrcode: /lib/rqrcode/qrcode/qr_util.rb L16 -- L58
4
- #--
5
- # Copyright 2004 by Duncan Robertson (duncan@whomwah.com).
6
- # All rights reserved.
3
+ # rqrcode: /lib/rqrcode/qrcode/qr_util.rb L16 -- L58
4
+ #--
5
+ # Copyright 2004 by Duncan Robertson (duncan@whomwah.com).
6
+ # All rights reserved.
7
7
 
8
- # Permission is granted for use, copying, modification, distribution,
9
- # and distribution of modified versions of this work as long as the
10
- # above copyright notice is included.
11
- #++
8
+ # Permission is granted for use, copying, modification, distribution,
9
+ # and distribution of modified versions of this work as long as the
10
+ # above copyright notice is included.
11
+ #++
12
12
  PATTERN_POSITION_TABLE = [
13
13
  [],
14
14
  [6, 18],
@@ -52,17 +52,17 @@ module CuteRB
52
52
  [6, 30, 58, 86, 114, 142, 170]
53
53
  ]
54
54
 
55
- # rqrcode: lib/rqrcode/qrcode/qr_code.rb L346 -- L361
56
- #--
57
- # Copyright 2008 by Duncan Robertson (duncan@whomwah.com).
58
- # All rights reserved.
55
+ # rqrcode: lib/rqrcode/qrcode/qr_code.rb L346 -- L361
56
+ #--
57
+ # Copyright 2008 by Duncan Robertson (duncan@whomwah.com).
58
+ # All rights reserved.
59
59
 
60
- # Permission is granted for use, copying, modification, distribution,
61
- # and distribution of modified versions of this work as long as the
62
- # above copyright notice is included.
63
- #++
60
+ # Permission is granted for use, copying, modification, distribution,
61
+ # and distribution of modified versions of this work as long as the
62
+ # above copyright notice is included.
63
+ #++
64
64
  def self.place_position_probe_pattern(dst, row, col)
65
- length = Math.sqrt(dst.length).to_i
65
+ length = Math.sqrt(dst.length).to_i
66
66
  (-1..7).each do |r|
67
67
  next if !(row + r).between?(0, length - 1)
68
68
 
@@ -72,60 +72,83 @@ module CuteRB
72
72
  is_vert_line = (r.between?(0, 6) && (c == 0 || c == 6))
73
73
  is_horiz_line = (c.between?(0, 6) && (r == 0 || r == 6))
74
74
  is_square = r.between?(2,4) && c.between?(2, 4)
75
- y = row + r
76
- x = col + c
77
- dst[y * length + x] = (is_vert_line || is_horiz_line || is_square) ? 'B' : 'W'
75
+ y = row + r
76
+ x = col + c
77
+ dst[y * length + x] = (is_vert_line || is_horiz_line || is_square) ? 'B' : 'W'
78
78
  end
79
79
  end
80
80
  end
81
81
 
82
- # rqrcode: lib/rqrcode/qrcode/qr_code.rb L388 -- L403
83
- #--
84
- # Copyright 2008 by Duncan Robertson (duncan@whomwah.com).
85
- # All rights reserved.
82
+ # rqrcode: lib/rqrcode/qrcode/qr_code.rb L388 -- L403
83
+ #--
84
+ # Copyright 2008 by Duncan Robertson (duncan@whomwah.com).
85
+ # All rights reserved.
86
86
 
87
- # Permission is granted for use, copying, modification, distribution,
88
- # and distribution of modified versions of this work as long as the
89
- # above copyright notice is included.
90
- #++
87
+ # Permission is granted for use, copying, modification, distribution,
88
+ # and distribution of modified versions of this work as long as the
89
+ # above copyright notice is included.
90
+ #++
91
91
  def self.place_position_adjust_pattern(dst, version)
92
- length = Math.sqrt(dst.length).to_i
92
+ length = Math.sqrt(dst.length).to_i
93
93
  positions = PATTERN_POSITION_TABLE[version - 1]
94
94
 
95
95
  positions.each do |row|
96
96
  positions.each do |col|
97
- next if dst[row * length + col] == dst[row * length + col].upcase
97
+ next if dst[row * length + col] == dst[row * length + col].upcase
98
98
 
99
99
  ( -2..2 ).each do |r|
100
100
  ( -2..2 ).each do |c|
101
- y = row + r
102
- x = col + c
103
- dst[y * length + x] = (r.abs == 2 || c.abs == 2 || (r == 0 && c == 0)) ? 'B' : 'W'
101
+ y = row + r
102
+ x = col + c
103
+ dst[y * length + x] = (r.abs == 2 || c.abs == 2 || (r == 0 && c == 0)) ? 'B' : 'W'
104
104
  end
105
105
  end
106
106
  end
107
107
  end
108
108
  end
109
109
 
110
- def self.dump(data)
111
- length = Math.sqrt(data.length).to_i
112
- for row in 0...length
113
- for col in 0...length
114
- case data[row * length + col]
115
- when 'b'
116
- print "\e[47m \e[m"
117
- when 'w'
118
- print "\e[40m \e[m"
119
- when 'B'
120
- print "\e[30m\e[47m--\e[m\e[m"
121
- when 'W'
122
- print "\e[37m\e[40m--\e[m\e[m"
123
- else
124
- p data[row * length + col]
125
- end
126
- end
127
- puts ""
128
- end
129
- end
110
+ def self.dump(data)
111
+ length = Math.sqrt(data.length).to_i
112
+ for row in 0...length
113
+ for col in 0...length
114
+ case data[row * length + col]
115
+ when 'b'
116
+ print "\e[47m \e[m"
117
+ when 'w'
118
+ print "\e[40m \e[m"
119
+ when 'B'
120
+ print "\e[30m\e[47m--\e[m\e[m"
121
+ when 'W'
122
+ print "\e[37m\e[40m--\e[m\e[m"
123
+ else
124
+ p data[row * length + col]
125
+ end
126
+ end
127
+ puts ""
128
+ end
129
+ end
130
+
131
+ def self.contents_area(canny)
132
+ short = [canny.columns, canny.rows].min
133
+ long = [canny.columns, canny.rows].max
134
+ integral = Array.new(long)
135
+ canny.rotate!(90, '>')
136
+
137
+ for y in 0...canny.rows
138
+ integral[y] = canny.export_pixels(0, y, canny.columns, 1, 'R').map{|px| px == 65535 ? 1 : 0}.sum(integral[y-1] || 0)
139
+ end
140
+
141
+ start = 0
142
+ count = 0
143
+ for offset in 0...(long-short)
144
+ c = integral[short + offset] - (integral[offset - 1] || 0)
145
+ if c > count
146
+ start = offset
147
+ count = c
148
+ end
149
+ end
150
+
151
+ return start
152
+ end
130
153
  end
131
154
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cuterb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - lrks
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-27 00:00:00.000000000 Z
11
+ date: 2019-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler