From 596b61e18082520ed1a9c092fbec29819dcb2f88 Mon Sep 17 00:00:00 2001 From: qeesung <1245712564@qq.com> Date: Sat, 3 Nov 2018 23:41:25 +0800 Subject: [PATCH] Add method to support convert image to pixelASCII matrix --- ascii/ascii.go | 31 ++++++++++++++++++++++++++++--- convert/convert.go | 34 ++++++++++++++++++++++++++++++++++ convert/convert_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/ascii/ascii.go b/ascii/ascii.go index 6bd142b..ca460f9 100644 --- a/ascii/ascii.go +++ b/ascii/ascii.go @@ -10,6 +10,14 @@ import ( "reflect" ) +type PixelASCII struct { + Char byte + R uint8 + G uint8 + B uint8 + A uint8 +} + // Options convert pixel to raw char type Options struct { Pixels []byte @@ -46,14 +54,15 @@ func NewPixelConverter() PixelConverter { // PixelConverter define the convert pixel operation type PixelConverter interface { ConvertPixelToASCII(pixel color.Color, options *Options) string + ConvertPixelToPixelASCII(pixel color.Color, options *Options) PixelASCII } // PixelASCIIConverter responsible for pixel ascii conversion type PixelASCIIConverter struct { } -// ConvertPixelToASCII converts a pixel to a ASCII char string -func (converter PixelASCIIConverter) ConvertPixelToASCII(pixel color.Color, options *Options) string { +// ConvertPixelToPixelASCII convert a image pixel to PixelASCII +func (converter PixelASCIIConverter) ConvertPixelToPixelASCII(pixel color.Color, options *Options) PixelASCII { convertOptions := NewOptions() convertOptions.mergeOptions(options) @@ -70,6 +79,22 @@ func (converter PixelASCIIConverter) ConvertPixelToASCII(pixel color.Color, opti // Choose the char precision := float64(255 * 3 / (len(convertOptions.Pixels) - 1)) rawChar := convertOptions.Pixels[converter.roundValue(float64(value)/precision)] + return PixelASCII{ + Char: rawChar, + R: uint8(r), + G: uint8(g), + B: uint8(b), + A: uint8(a), + } +} + +// ConvertPixelToASCII converts a pixel to a ASCII char string +func (converter PixelASCIIConverter) ConvertPixelToASCII(pixel color.Color, options *Options) string { + convertOptions := NewOptions() + convertOptions.mergeOptions(options) + + pixelASCII := converter.ConvertPixelToPixelASCII(pixel, options) + rawChar, r, g, b := pixelASCII.Char, pixelASCII.R, pixelASCII.G, pixelASCII.B if convertOptions.Colored { return converter.decorateWithColor(r, g, b, rawChar) } @@ -93,7 +118,7 @@ func (converter PixelASCIIConverter) intensity(r, g, b, a uint64) uint64 { } // decorateWithColor decorate the raw char with the color base on r,g,b value -func (converter PixelASCIIConverter) decorateWithColor(r, g, b uint64, rawChar byte) string { +func (converter PixelASCIIConverter) decorateWithColor(r, g, b uint8, rawChar byte) string { coloredChar := rgbterm.FgString(string([]byte{rawChar}), uint8(r), uint8(g), uint8(b)) return coloredChar } diff --git a/convert/convert.go b/convert/convert.go index 2299c77..1a7c905 100644 --- a/convert/convert.go +++ b/convert/convert.go @@ -50,6 +50,8 @@ type Converter interface { Image2ASCIIString(image image.Image, options *Options) string ImageFile2ASCIIMatrix(imageFilename string, option *Options) []string ImageFile2ASCIIString(imageFilename string, option *Options) string + Image2PixelASCIIMatrix(image image.Image, imageConvertOptions *Options) [][]ascii.PixelASCII + ImageFile2PixelASCIIMatrix(image image.Image, imageConvertOptions *Options) [][]ascii.PixelASCII } // ImageConverter implement the Convert interface, and responsible @@ -59,6 +61,38 @@ type ImageConverter struct { pixelConverter ascii.PixelConverter } +// Image2PixelASCIIMatrix convert a image to a pixel ascii matrix +func (converter *ImageConverter) Image2PixelASCIIMatrix(image image.Image, imageConvertOptions *Options) [][]ascii.PixelASCII { + newImage := converter.resizeHandler.ScaleImage(image, imageConvertOptions) + sz := newImage.Bounds() + newWidth := sz.Max.X + newHeight := sz.Max.Y + pixelASCIIs := make([][]ascii.PixelASCII, 0, newHeight) + for i := 0; i < int(newHeight); i++ { + line := make([]ascii.PixelASCII, 0, newWidth) + for j := 0; j < int(newWidth); j++ { + pixel := color.NRGBAModel.Convert(newImage.At(j, i)) + // Convert the pixel to ascii char + pixelConvertOptions := ascii.NewOptions() + pixelConvertOptions.Colored = imageConvertOptions.Colored + pixelConvertOptions.Reversed = imageConvertOptions.Reversed + pixelASCII := converter.pixelConverter.ConvertPixelToPixelASCII(pixel, &pixelConvertOptions) + line = append(line, pixelASCII) + } + pixelASCIIs = append(pixelASCIIs, line) + } + return pixelASCIIs +} + +// Image2PixelASCIIMatrix convert a image to a pixel ascii matrix +func (converter *ImageConverter) ImageFile2PixelASCIIMatrix(imageFilename string, imageConvertOptions *Options) [][]ascii.PixelASCII { + img, err := OpenImageFile(imageFilename) + if err != nil { + log.Fatal("open image failed : " + err.Error()) + } + return converter.Image2PixelASCIIMatrix(img, imageConvertOptions) +} + // Image2ASCIIMatrix converts a image to ASCII matrix func (converter *ImageConverter) Image2ASCIIMatrix(image image.Image, imageConvertOptions *Options) []string { // Resize the convert first diff --git a/convert/convert_test.go b/convert/convert_test.go index 7dd7f01..b855caa 100644 --- a/convert/convert_test.go +++ b/convert/convert_test.go @@ -96,6 +96,33 @@ func TestImageFile2ASCIIString(t *testing.T) { } } +func TestImageFile2PixelASCIIMatrix(t *testing.T) { + converter := NewImageConverter() + imageTests := []struct { + imageFilename string + width int + height int + }{ + {"testdata/3x3_black.png", 3, 3}, + {"testdata/3x3_white.png", 3,3}, + {"testdata/8x3_multi_colors.png", 8, 3}, + } + + for _, tt := range imageTests { + t.Run(tt.imageFilename, func(t *testing.T) { + convertOptions := DefaultOptions + convertOptions.FitScreen = false + convertOptions.Colored = false + + matrix := converter.ImageFile2PixelASCIIMatrix(tt.imageFilename, &convertOptions) + if len(matrix) != tt.height|| len(matrix[0]) != tt.width{ + t.Errorf("image %s convert expected to %+v, %+v, but get %+v", + tt.imageFilename, tt.width, tt.height, matrix) + } + }) + } +} + func TestImage2ReversedASCIIString(t *testing.T) { converter := NewImageConverter() imageTests := []struct { -- 2.45.2