#!/usr/bin/env ruby require 'rmagick' class GoBoardPixel GO_BOARD_CHANNEL_TO_RMAGICK_CHANNEL = 65_536 / 8 attr_reader :red, :green, :blue def self.from_magick(red:, green:, blue:) new( red: red / GO_BOARD_CHANNEL_TO_RMAGICK_CHANNEL, green: green / GO_BOARD_CHANNEL_TO_RMAGICK_CHANNEL, blue: blue / GO_BOARD_CHANNEL_TO_RMAGICK_CHANNEL ) end def initialize(red:, green:, blue:) @red = red @green = green @blue = blue end def distance_from_real_pixel(red:, green:, blue:) (red - @red * GO_BOARD_CHANNEL_TO_RMAGICK_CHANNEL)**2 + (green - @green * GO_BOARD_CHANNEL_TO_RMAGICK_CHANNEL)**2 + (blue - @blue * GO_BOARD_CHANNEL_TO_RMAGICK_CHANNEL)**2 end def distance_from_go_pixel(red:, green:, blue:) (red - @red)**2 + (green - @green)**2 + (blue - @blue)**2 end def eql?(other) red == other.red && green == other.green && blue == other.blue end def hash [red, green, blue].hash end end ImagePixel = Data.define(:red, :green, :blue) image = Magick::Image.read(ARGV[0]).first image.change_geometry!('80x60^') do |cols, rows, img| img.resize!(cols, rows) end image.crop!(Magick::CenterGravity, 80, 60, true) possible_colors = {} image.each_pixel do |px| go_px = GoBoardPixel.from_magick(red: px.red, green: px.green, blue: px.blue) possible_colors[go_px] ||= 0 possible_colors[go_px] += 1 end pixels_by_usage = possible_colors.sort_by(&:last).reverse go_board_palette = pixels_by_usage[0..31].map(&:first) color_mapping = go_board_palette.each_with_index.each_with_object({}) do |(go_px, index), obj| obj[go_px] = index end pixels_by_usage[32..].map(&:first).each do |go_px| best_match = go_board_palette.each_with_index.map do |target_go_px, index| [ target_go_px.distance_from_go_pixel( red: go_px.red, green: go_px.green, blue: go_px.blue, ), index ] end.sort_by(&:first).first color_mapping[go_px] = best_match.last end File.open('image_data.txt', 'w') do |fh| image.each_pixel do |px| magick_px = GoBoardPixel.from_magick( red: px.red, green: px.green, blue: px.blue ) fh.puts format('%05b', color_mapping[magick_px]) end end File.open('palette_data.txt', 'w') do |fh| go_board_palette.each do |color| fh.puts format('%09b', ((color.red << 6) + (color.green << 3) + color.blue)) end end # [ ] load image # [ ] downsample to 40x30 # [ ] change to indexed image with go board palette # [ ] save out body data # [ ] save out palette data