Skip to content

Commit 446a74b

Browse files
committed
feat: image2ascii works
1 parent cd53358 commit 446a74b

File tree

5 files changed

+407
-0
lines changed

5 files changed

+407
-0
lines changed

exe/image2ascii

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env ruby
2+
require 'optparse'
3+
require_relative "../lib/convert2ascii/image2ascii"
4+
5+
6+
options = {}
7+
OptionParser.new do |parser|
8+
parser.banner = "Usage: image2ascii [options]"
9+
10+
parser.on("--version", "verison") do |v|
11+
puts "convert2ascii/image2ascii: v#{::Convert2Ascii::VERSION}"
12+
puts "author: Mark24"
13+
puts "mail: mark.zhangyoung@gmail.com"
14+
puts "project: https://github.com/mark24Code/convert2ascii"
15+
return
16+
end
17+
18+
parser.on("-iURI", "--image=URI", "image uri (required)") do |uri|
19+
options[:uri] = uri
20+
21+
# check options
22+
unless options[:uri]
23+
puts "Error: --image option is required."
24+
exit 1
25+
end
26+
end
27+
28+
parser.on("-wWIDTH", "--width=WIDTH", Integer ,"image width (integer)") do |width|
29+
options[:width] = width
30+
end
31+
32+
parser.on("-sSTYLE", "--style=STYLE", "ascii style: ['color'| 'text']") do |style|
33+
options[:style] = style
34+
35+
styles = ["color", "text"]
36+
# check options
37+
unless styles.include? options[:style]
38+
puts "Error: --style option must be [\"color\" | \"text\"]."
39+
exit 1
40+
end
41+
end
42+
43+
parser.on("-b", "--block", "ascii color style use BLOCK or not [ true | false ] ") do |color_block|
44+
options[:color_block] = color_block || false
45+
end
46+
end.parse!
47+
48+
Convert2Ascii::Image2Ascii.new(**options).generate.tty_print

lib/convert2ascii/check_package.rb

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
require 'rainbow'
2+
3+
module Convert2Ascii
4+
module OS
5+
6+
module OS_ENUM
7+
MacOS = :macos
8+
Linux = :linux
9+
Windows = :ms
10+
Unknow = :unknow
11+
end
12+
13+
def detect_os
14+
if RUBY_PLATFORM =~ /linux/
15+
return OS_ENUM::Linux
16+
elsif RUBY_PLATFORM =~ /darwin/
17+
return OS_ENUM::MacOS
18+
elsif RUBY_PLATFORM =~ /mswin|mingw|cygwin/
19+
return OS_ENUM::Windows
20+
else
21+
return OS_ENUM::Unknow
22+
end
23+
end
24+
end
25+
26+
class CheckPackageError < StandardError
27+
end
28+
29+
class CheckPackage
30+
include OS
31+
32+
def initialize
33+
@os_name = nil
34+
end
35+
36+
def macos_check
37+
end
38+
39+
def ms_check
40+
end
41+
42+
def linux_check
43+
end
44+
45+
def unknow_check
46+
raise CheckPackageError, Rainbow("[Error] #{@os_name} not support!").red
47+
end
48+
49+
50+
def macos_installed?(package_name)
51+
output = `brew list #{package_name} 2>/dev/null`
52+
return output != ""
53+
end
54+
55+
def detect_linux_distribution
56+
57+
if !File.exist?('/etc/os-release')
58+
raise CheckPackageError, Rainbow("[Error] can not detect_os! #{@os_name} ").red
59+
end
60+
61+
os_release = File.read('/etc/os-release')
62+
63+
if os_release.include?('debian')
64+
# debian/ubuntu
65+
return'debian'
66+
end
67+
68+
if os_release.include?('centos')
69+
# centos
70+
return'centos'
71+
end
72+
73+
if os_release.include?('redhat')
74+
# redhat
75+
return'redhat'
76+
end
77+
78+
if os_release.include?('arch')
79+
# arch linux
80+
return'arch'
81+
end
82+
83+
raise CheckPackageError, Rainbow("[Error] #{@os_name} linux platform not support!").red
84+
end
85+
86+
87+
def debian_installed?(package_name)
88+
output = `dpkg -l | grep #{package_name}`
89+
!output.strip.empty?
90+
end
91+
92+
def centos_installed?(package_name)
93+
output = `rpm -qa | grep #{package_name}`
94+
!output.strip.empty?
95+
end
96+
97+
def redhat_installed?(package_name)
98+
output = `yum list installed | grep #{package_name}`
99+
!output.strip.empty?
100+
end
101+
102+
def arch_installed?(package_name)
103+
output = `pacman -Q | grep #{package_name}`
104+
!output.strip.empty?
105+
end
106+
107+
def check
108+
@os_name = detect_os
109+
__send__ "#{@os_name}_check"
110+
end
111+
end
112+
113+
class CheckFFmpeg < CheckPackage
114+
def initialize
115+
super
116+
@name = "ffmpeg"
117+
@need_error = Rainbow("[Error] `#{@name}` is need!").red
118+
@tips = Rainbow("[Tips ] For more details and install: https://www.ffmpeg.org/").green
119+
end
120+
121+
def macos_check
122+
if !macos_installed?(@name)
123+
raise CheckPackageError, "\n#{@need_error}\n#{@tips}\n"
124+
end
125+
end
126+
127+
def linux_check
128+
linux_platform_name = detect_linux_distribution
129+
__send__ "#{linux_platform_name}_installed?", @name
130+
end
131+
132+
end
133+
134+
class CheckImageMagick < CheckPackage
135+
def initialize
136+
super
137+
@name = "imagemagick"
138+
@need_error = Rainbow("[Error] `imagemagick` is need!").red
139+
@tips = Rainbow("[Tips ] For more details and install guide: https://github.com/rmagick/rmagick").green
140+
141+
end
142+
143+
def macos_check
144+
if !macos_package_installed?(@name)
145+
raise CheckPackageError, "\n#{@need_error}\n#{@tips}\n"
146+
end
147+
end
148+
149+
def linux_check
150+
# just Debian/Ubuntu
151+
linux_pkg = {
152+
"debian" => "libmagickwand-dev",
153+
"redhat" => "ImageMagick-devel",
154+
"arch" => "imagemagick",
155+
}
156+
157+
linux_platform_name = detect_linux_distribution
158+
pkg_name = linux_pkg.fetch(linux_platform_name, nil)
159+
160+
if !pkg_name
161+
raise CheckPackageError, "\n#{@need_error}\n#{@tips}\n"
162+
end
163+
164+
__send__ "#{linux_platform_name}_installed?", pkg_name
165+
end
166+
end
167+
end

lib/convert2ascii/image2ascii.rb

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
require "io/console"
2+
require "rmagick"
3+
require "rainbow"
4+
require "open-uri"
5+
require_relative "./check_package"
6+
require_relative './version'
7+
8+
module Convert2Ascii
9+
class Image2AsciiError < StandardError
10+
end
11+
12+
class Image2Ascii
13+
module STYLE_ENUM
14+
Color = "color"
15+
Text = "text"
16+
end
17+
18+
module COLOR_ENUM
19+
Full = "full"
20+
Greyscale = "greyscale"
21+
end
22+
23+
24+
attr_reader :width, :ascii_string
25+
attr_accessor :chars
26+
27+
def initialize(**args)
28+
@uri = args[:uri]
29+
@width = args[:width] || IO.console.winsize[1]
30+
@style = args[:style] || STYLE_ENUM::Color # "color": color ansi , "text": plain text
31+
@color = args[:color] || COLOR_ENUM::Full # full
32+
@color_block = args[:color_block] || false
33+
34+
check_quantum_convert_factor
35+
# divides quantum depth color space into usable rgb values
36+
@quantum_convert_factor = Magick::MAGICKCORE_QUANTUM_DEPTH == 16 ? 257 : 1
37+
38+
@chars = ".'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
39+
@ascii_string = ""
40+
end
41+
42+
def check_quantum_convert_factor
43+
# quantum conversion factor for dealing with quantum depth color values
44+
if Magick::MAGICKCORE_QUANTUM_DEPTH > 16
45+
raise Image2AsciiError, "[Error] ImageMagick quantum depth is set to #{Magick::MAGICKCORE_QUANTUM_DEPTH}. It needs to be 16 or less"
46+
end
47+
end
48+
49+
def generate(**args)
50+
@width = args[:width] || @width
51+
@style = args[:style] || @style # "color": color ansi , "text": plain text
52+
@color = args[:color] || @color # full
53+
@color_block = args[:color_block] || @color_block
54+
55+
generate_string
56+
57+
self
58+
end
59+
60+
def tty_print
61+
print @ascii_string
62+
end
63+
64+
private
65+
66+
def check_packages
67+
CheckImageMagick.new.check
68+
end
69+
70+
def generate_string
71+
resource = URI.open(@uri)
72+
img = Magick::ImageList.new
73+
img.from_blob(resource.read)
74+
75+
img = correct_aspect_ratio(img, @width)
76+
77+
img.each_pixel do |pixel, col, row|
78+
r, g, b, brightness = get_pixel_values(pixel)
79+
char = select_character(brightness)
80+
81+
if @style == STYLE_ENUM::Text
82+
@ascii_string << char
83+
end
84+
85+
if @style == STYLE_ENUM::Color
86+
chosen_color = get_chosen_color(@color, r, g, b)
87+
88+
if @color_block == true
89+
@ascii_string << Rainbow(" ").background(*chosen_color)
90+
else
91+
@ascii_string << Rainbow(char).color(*chosen_color)
92+
end
93+
end
94+
95+
# add line wrap once desired width is reached
96+
if (col % (@width - 1) == 0) and (col != 0)
97+
@ascii_string << "\n"
98+
end
99+
end
100+
end
101+
102+
def correct_aspect_ratio(img, width)
103+
img = img.scale(width / img.columns.to_f)
104+
img = img.scale(img.columns, img.rows / 2)
105+
end
106+
107+
def get_pixel_values(pixel)
108+
r = pixel.red / @quantum_convert_factor
109+
g = pixel.green / @quantum_convert_factor
110+
b = pixel.blue / @quantum_convert_factor
111+
# Brightness ref: https://en.wikipedia.org/wiki/Relative_luminance
112+
brightness = (0.2126 * r + 0.7152 * g + 0.0722 * b)
113+
114+
return [r, g, b, brightness]
115+
end
116+
117+
def select_character(brightness)
118+
char_index = brightness / (255.0 / @chars.length)
119+
@chars[char_index.floor]
120+
end
121+
122+
def get_chosen_color(color, r, g, b)
123+
if color == COLOR_ENUM::Full
124+
[r, g, b]
125+
elsif color == COLOR_ENUM::Greyscale
126+
[r, r, r]
127+
else
128+
color
129+
end
130+
end
131+
end
132+
end

test/assets/ruby.jpg

27.1 KB
Loading

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy