Refactor code, added colors flag, negative flag, image formats flag
This commit is contained in:
parent
f91513f00b
commit
a7eebb45d0
47
README.md
47
README.md
|
|
@ -3,11 +3,11 @@
|
||||||
ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. It is cross-platform so both Windows and Linux distributions are supported
|
ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. It is cross-platform so both Windows and Linux distributions are supported
|
||||||
|
|
||||||
Image formats currently supported:
|
Image formats currently supported:
|
||||||
* PNG
|
|
||||||
* JPEG/JPG
|
* JPEG/JPG
|
||||||
|
* PNG
|
||||||
* WEBP
|
* WEBP
|
||||||
* BMP
|
* BMP
|
||||||
* TIFF
|
* TIFF/TIF
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ ascii-image-converter [path to image] --complex
|
||||||
|
|
||||||
|
|
||||||
#### --dimensions OR -d
|
#### --dimensions OR -d
|
||||||
Set the width and height of the printed ascii image in character lengths.
|
Set the width and height for ascii art in CHARACTER lengths. (Don't immediately append another flag with -d)
|
||||||
```
|
```
|
||||||
ascii-image-converter [path to image] -d <width>,<height>
|
ascii-image-converter [path to image] -d <width>,<height>
|
||||||
# Or
|
# Or
|
||||||
|
|
@ -90,18 +90,45 @@ Example:
|
||||||
ascii-image-converter [path to image] -d 100,30
|
ascii-image-converter [path to image] -d 100,30
|
||||||
```
|
```
|
||||||
|
|
||||||
#### --save OR -S
|
#### --color OR -C
|
||||||
Save the image ascii art in a file ascii-image.txt in the same directory
|
Display ascii art with the colors from original image. Works with the -n flag as well.
|
||||||
```
|
```
|
||||||
ascii-image-converter [path to image] --save
|
ascii-image-converter [path to image] -C
|
||||||
# Or
|
# Or
|
||||||
ascii-image-converter [path to image] -S
|
ascii-image-converter [path to image] --color
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### --negative OR -n
|
||||||
|
Display ascii art in negative colors. Works with both uncolored and colored text from -C flag.
|
||||||
|
```
|
||||||
|
ascii-image-converter [path to image] -n
|
||||||
|
# Or
|
||||||
|
ascii-image-converter [path to image] --negative
|
||||||
|
```
|
||||||
|
|
||||||
|
#### --save OR -s
|
||||||
|
Save the printed ascii art in a file ascii-image.txt in the directory passed alongside. (Don't immediately append another flag with -s)
|
||||||
|
|
||||||
|
Example for current directory:
|
||||||
|
```
|
||||||
|
ascii-image-converter [path to image] --save ./
|
||||||
|
# Or
|
||||||
|
ascii-image-converter [path to image] -s ./
|
||||||
|
```
|
||||||
|
|
||||||
|
#### --formats OR -f
|
||||||
|
Display supported image formats.
|
||||||
|
```
|
||||||
|
ascii-image-converter [path to image] --formats
|
||||||
|
# Or
|
||||||
|
ascii-image-converter [path to image] -f
|
||||||
|
```
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
You can combine commands as well
|
You can combine flags as well. Following command outputs colored and negative ascii art, with complex characters, fixed 100 by 30 character dimensions and saves the output in current directory as well.
|
||||||
```
|
```
|
||||||
ascii-image-converter [path to image] -Scd 100,30
|
ascii-image-converter [path to image] -Ccnd 100,30 -s ./
|
||||||
```
|
```
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
@ -122,6 +149,8 @@ You can fork the project and implement any changes you want for a pull request.
|
||||||
|
|
||||||
[github.com/nfnt/resize](https://github.com/nfnt/resize)
|
[github.com/nfnt/resize](https://github.com/nfnt/resize)
|
||||||
|
|
||||||
|
[github.com/gookit/color](https://github.com/gookit/color)
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE)
|
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE)
|
||||||
79
cmd/root.go
79
cmd/root.go
|
|
@ -40,29 +40,45 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfgFile string
|
// Flags
|
||||||
compl bool
|
cfgFile string
|
||||||
dimensions []int
|
compl bool
|
||||||
save bool
|
dimensions []int
|
||||||
|
savePath string
|
||||||
|
negative bool
|
||||||
|
formatsTrue bool
|
||||||
|
colored bool
|
||||||
|
|
||||||
|
// Root commands
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "ascii-image-converter [image path]",
|
Use: "ascii-image-converter [image path]",
|
||||||
Short: "Converts images into ascii format",
|
Short: "Converts images into ascii format",
|
||||||
Long: `ascii-image-converter converts images into ascii format and prints them onto the terminal window. Further configuration can be managed with flags`,
|
Long: `This tool converts images into ascii format and prints them onto the terminal window. Further configuration can be managed with flags`,
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
if formatsTrue {
|
||||||
|
fmt.Println("Supported image formats: JPEG/JPG, PNG, WEBP, BMP, TIFF/TIF")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Requires 1 image path, got %v", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
numberOfDimensions := len(dimensions)
|
numberOfDimensions := len(dimensions)
|
||||||
if dimensions != nil && numberOfDimensions != 2 {
|
if dimensions != nil && numberOfDimensions != 2 {
|
||||||
return fmt.Errorf("-d requires two dimensions got %v", numberOfDimensions)
|
return fmt.Errorf("-d requires 2 dimensions, got %v", numberOfDimensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
imagePath := args[0]
|
imagePath := args[0]
|
||||||
return convertPicture(imagePath, compl, dimensions, save)
|
|
||||||
|
return convertImage(imagePath)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func convertPicture(imagePath string, isComplex bool, dimensions []int, save bool) error {
|
func convertImage(imagePath string) error {
|
||||||
|
|
||||||
pic, err := os.Open(imagePath)
|
pic, err := os.Open(imagePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to open file: %w", err)
|
return fmt.Errorf("Unable to open file: %w", err)
|
||||||
|
|
@ -74,32 +90,46 @@ func convertPicture(imagePath string, isComplex bool, dimensions []int, save boo
|
||||||
return fmt.Errorf("Unable to decode file: %w", err)
|
return fmt.Errorf("Unable to decode file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
imgSet := imgMani.ConvertToTerminalSizedSlices(imData, dimensions)
|
imgSet, err := imgMani.ConvertToAsciiPixels(imData, dimensions)
|
||||||
var asciiSet [][]string
|
if err != nil {
|
||||||
|
return err
|
||||||
if isComplex {
|
|
||||||
asciiSet = imgMani.ConvertToAsciiDetailed(imgSet)
|
|
||||||
} else {
|
|
||||||
asciiSet = imgMani.ConvertToAsciiSimple(imgSet)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ascii := flattenAscii(asciiSet)
|
var asciiSet [][]imgMani.AsciiChar
|
||||||
|
asciiSet = imgMani.ConvertToAscii(imgSet, negative, colored, compl)
|
||||||
|
|
||||||
|
var ascii []string
|
||||||
|
ascii = flattenAscii(asciiSet, colored)
|
||||||
|
|
||||||
for _, line := range ascii {
|
for _, line := range ascii {
|
||||||
fmt.Println(line)
|
fmt.Println(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if save {
|
if savePath != "" {
|
||||||
return ioutil.WriteFile("ascii-image.txt", []byte(strings.Join(ascii, "\n")), 0777)
|
// To make sure uncolored ascii art is the one saved
|
||||||
|
saveAscii := flattenAscii(asciiSet, false)
|
||||||
|
if savePath == "." {
|
||||||
|
savePath = "./"
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(savePath+"ascii-image.txt", []byte(strings.Join(saveAscii, "\n")), 0777)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// flattenAscii flattens a two-dimensional grid of ascii characters into a one dimension
|
// flattenAscii flattens a two-dimensional grid of ascii characters into a one dimension
|
||||||
// of lines of ascii
|
// of lines of ascii
|
||||||
func flattenAscii(asciiSet [][]string) []string {
|
func flattenAscii(asciiSet [][]imgMani.AsciiChar, color bool) []string {
|
||||||
var ascii []string
|
var ascii []string
|
||||||
for _, line := range asciiSet {
|
for _, line := range asciiSet {
|
||||||
ascii = append(ascii, strings.Join(line, ""))
|
var tempAscii []string
|
||||||
|
for i := 0; i < len(line); i++ {
|
||||||
|
if color {
|
||||||
|
tempAscii = append(tempAscii, line[i].Colored)
|
||||||
|
} else {
|
||||||
|
tempAscii = append(tempAscii, line[i].Simple)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ascii = append(ascii, strings.Join(tempAscii, ""))
|
||||||
}
|
}
|
||||||
return ascii
|
return ascii
|
||||||
}
|
}
|
||||||
|
|
@ -117,9 +147,12 @@ func init() {
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)")
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ascii-image-converter.yaml)")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&compl, "complex", "c", false, "Prints ascii characters in a larger range, may result in higher quality")
|
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with the colors from original image (Can work with the -n flag)")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&compl, "complex", "c", false, "Display ascii characters in a larger range, may result in higher quality")
|
||||||
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length e.g. 100,30 (defaults to terminal size)")
|
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length e.g. 100,30 (defaults to terminal size)")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&save, "save", "S", false, "Save ascii text in current directory in an ascii-image.txt file")
|
rootCmd.PersistentFlags().BoolVarP(&formatsTrue, "formats", "f", false, "Display supported image formats")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&negative, "negative", "n", false, "Display ascii art in negative colors (Can work with the -C flag)")
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&savePath, "save", "s", "", "Save ascii art in an ascii-image.txt file in a given path (pass ./ for current directory)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// initConfig reads in config file and ENV variables if set.
|
// initConfig reads in config file and ENV variables if set.
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -4,6 +4,7 @@ go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
|
github.com/gookit/color v1.4.2 // indirect
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
|
|
|
||||||
7
go.sum
7
go.sum
|
|
@ -66,6 +66,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
|
||||||
|
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
|
@ -192,10 +194,13 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||||
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
|
@ -264,6 +269,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q=
|
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q=
|
||||||
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|
@ -328,6 +334,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,12 @@ limitations under the License.
|
||||||
|
|
||||||
package image_conversions
|
package image_conversions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gookit/color"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
// Reference taken from http://paulbourke.net/dataformats/asciiart/
|
||||||
var asciiTableSimple = map[int]string{
|
var asciiTableSimple = map[int]string{
|
||||||
0: " ",
|
0: " ",
|
||||||
|
|
@ -106,68 +112,71 @@ var asciiTableDetailed = map[int]string{
|
||||||
// For each individual element of imgSet in ConvertToASCIISlice()
|
// For each individual element of imgSet in ConvertToASCIISlice()
|
||||||
const MAX_VAL float32 = 65535
|
const MAX_VAL float32 = 65535
|
||||||
|
|
||||||
// Converts the 2D uint32 slice of image data (each value representing each pixel of image)
|
type AsciiChar struct {
|
||||||
// to a 2D string slice with each string having an ASCII character corresponding to
|
Colored string
|
||||||
// the original uint32 value.
|
Simple string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts the 2D AsciiPixel slice of image data (each instance representing each pixel of original image)
|
||||||
|
// to a 2D AsciiChar slice with each colored and simple string having an ASCII character corresponding to
|
||||||
|
// the original grayscale and RGB values in AsciiPixel.
|
||||||
//
|
//
|
||||||
// Values are compared to 69 ASCII characters
|
// If complex parameter is true, values are compared to 69 levels of color density in ASCII characters.
|
||||||
func ConvertToAsciiDetailed(imgSet [][]uint32) [][]string {
|
// Otherwise, values are compared to 10 levels of color density in ASCII characters.
|
||||||
|
func ConvertToAscii(imgSet [][]AsciiPixel, negative bool, colored bool, complex bool) [][]AsciiChar {
|
||||||
|
|
||||||
height := len(imgSet)
|
height := len(imgSet)
|
||||||
width := len(imgSet[0])
|
width := len(imgSet[0])
|
||||||
|
|
||||||
result := make([][]string, height)
|
var chosenTable map[int]string
|
||||||
|
if complex {
|
||||||
|
chosenTable = asciiTableDetailed
|
||||||
|
} else {
|
||||||
|
chosenTable = asciiTableSimple
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([][]AsciiChar, height)
|
||||||
for i := range result {
|
for i := range result {
|
||||||
result[i] = make([]string, width)
|
result[i] = make([]AsciiChar, width)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < height; i++ {
|
for i := 0; i < height; i++ {
|
||||||
var tempSlice []string
|
|
||||||
|
var tempSlice []AsciiChar
|
||||||
|
|
||||||
for j := 0; j < width; j++ {
|
for j := 0; j < width; j++ {
|
||||||
|
value := float32(imgSet[i][j].grayscaleValue)
|
||||||
|
|
||||||
value := float32(imgSet[i][j])
|
// Gets appropriate string index from asciiTableSimple by percentage comparisons with its length
|
||||||
|
tempFloat := (value / MAX_VAL) * float32(len(chosenTable))
|
||||||
tempFloat := (value / MAX_VAL) * float32(len(asciiTableDetailed))
|
|
||||||
if value == MAX_VAL {
|
if value == MAX_VAL {
|
||||||
tempFloat = float32(len(asciiTableDetailed) - 1)
|
tempFloat = float32(len(chosenTable) - 1)
|
||||||
}
|
}
|
||||||
tempInt := int(tempFloat)
|
tempInt := int(tempFloat)
|
||||||
|
|
||||||
tempSlice = append(tempSlice, asciiTableDetailed[tempInt])
|
r := int(imgSet[i][j].rgbValue[0])
|
||||||
}
|
g := int(imgSet[i][j].rgbValue[1])
|
||||||
result[i] = tempSlice
|
b := int(imgSet[i][j].rgbValue[2])
|
||||||
}
|
|
||||||
|
if negative {
|
||||||
return result
|
// Select character from opposite side of table as well as turn pixels negative
|
||||||
}
|
r = 255 - r
|
||||||
|
g = 255 - g
|
||||||
// Converts the 2D uint32 slice of image data (each value representing each pixel of image)
|
b = 255 - b
|
||||||
// to a 2D string slice with each string having an ASCII character corresponding to
|
|
||||||
// the original uint32 value.
|
tempInt = (len(chosenTable) - 1) - tempInt
|
||||||
//
|
}
|
||||||
// Values are compared to 10 ASCII characters
|
|
||||||
func ConvertToAsciiSimple(imgSet [][]uint32) [][]string {
|
rStr := strconv.Itoa(r)
|
||||||
|
gStr := strconv.Itoa(g)
|
||||||
height := len(imgSet)
|
bStr := strconv.Itoa(b)
|
||||||
width := len(imgSet[0])
|
|
||||||
|
var char AsciiChar
|
||||||
result := make([][]string, height)
|
|
||||||
for i := range result {
|
char.Colored = color.Sprintf("<fg="+rStr+","+gStr+","+bStr+">%v</>", chosenTable[tempInt])
|
||||||
result[i] = make([]string, width)
|
char.Simple = chosenTable[tempInt]
|
||||||
}
|
|
||||||
|
tempSlice = append(tempSlice, char)
|
||||||
for i := 0; i < height; i++ {
|
|
||||||
var tempSlice []string
|
|
||||||
for j := 0; j < width; j++ {
|
|
||||||
|
|
||||||
value := float32(imgSet[i][j])
|
|
||||||
tempFloat := (value / MAX_VAL) * float32(len(asciiTableSimple))
|
|
||||||
if value == MAX_VAL {
|
|
||||||
tempFloat = float32(len(asciiTableSimple) - 1)
|
|
||||||
}
|
|
||||||
tempInt := int(tempFloat)
|
|
||||||
|
|
||||||
tempSlice = append(tempSlice, asciiTableSimple[tempInt])
|
|
||||||
}
|
}
|
||||||
result[i] = tempSlice
|
result[i] = tempSlice
|
||||||
}
|
}
|
||||||
|
|
@ -20,19 +20,23 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/nathan-fiscaletti/consolesize-go"
|
"github.com/nathan-fiscaletti/consolesize-go"
|
||||||
"github.com/nfnt/resize"
|
"github.com/nfnt/resize"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AsciiPixel struct {
|
||||||
|
grayscaleValue uint32
|
||||||
|
rgbValue []uint32
|
||||||
|
}
|
||||||
|
|
||||||
// This function shrinks the passed image according to passed dimensions or terminal
|
// This function shrinks the passed image according to passed dimensions or terminal
|
||||||
// size if none are passed. It turns each pixel into grayscale to simplify getting numeric
|
// size if none are passed. Stores each pixel's grayscale and RGB values in an AsciiPixel
|
||||||
// data for ASCII character comparison.
|
// instance to simplify getting numeric data for ASCII character comparison.
|
||||||
//
|
//
|
||||||
// The returned 2D uint32 slice contains each corresponding pixel's value ranging from
|
// The returned 2D AsciiPixel slice contains each corresponding pixel's values. Grayscale value
|
||||||
// 0 to 65535.
|
// ranges from 0 to 65535, while RGB values are separate.
|
||||||
func ConvertToTerminalSizedSlices(img image.Image, dimensions []int) [][]uint32 {
|
func ConvertToAsciiPixels(img image.Image, dimensions []int) ([][]AsciiPixel, error) {
|
||||||
|
|
||||||
var terminalWidth, terminalHeight int
|
var terminalWidth, terminalHeight int
|
||||||
|
|
||||||
|
|
@ -46,12 +50,12 @@ func ConvertToTerminalSizedSlices(img image.Image, dimensions []int) [][]uint32
|
||||||
// Sometimes full length outputs print empty lines between ascii art
|
// Sometimes full length outputs print empty lines between ascii art
|
||||||
terminalWidth -= 1
|
terminalWidth -= 1
|
||||||
|
|
||||||
|
// Passing 0 in place of height keeps the original image's aspect ratio
|
||||||
smallImg = resize.Resize(uint(terminalWidth), 0, img, resize.Lanczos3)
|
smallImg = resize.Resize(uint(terminalWidth), 0, img, resize.Lanczos3)
|
||||||
terminalHeight = smallImg.Bounds().Max.Y
|
terminalHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
||||||
|
|
||||||
// To fix height ratio
|
// To fix height ratio in eventual ascii art
|
||||||
terminalHeight -= terminalHeight / 2
|
terminalHeight = int(0.5 * float32(terminalHeight))
|
||||||
terminalHeight -= terminalHeight / 5
|
|
||||||
|
|
||||||
smallImg = resize.Resize(uint(terminalWidth), uint(terminalHeight), img, resize.Lanczos3)
|
smallImg = resize.Resize(uint(terminalWidth), uint(terminalHeight), img, resize.Lanczos3)
|
||||||
|
|
||||||
|
|
@ -61,39 +65,49 @@ func ConvertToTerminalSizedSlices(img image.Image, dimensions []int) [][]uint32
|
||||||
smallImg = resize.Resize(uint(terminalWidth), uint(terminalHeight), img, resize.Lanczos3)
|
smallImg = resize.Resize(uint(terminalWidth), uint(terminalHeight), img, resize.Lanczos3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are passed dimensions, check whether the width exceeds terminal width
|
||||||
if len(dimensions) > 0 {
|
if len(dimensions) > 0 {
|
||||||
defaultTermWidth, _ := consolesize.GetConsoleSize()
|
defaultTermWidth, _ := consolesize.GetConsoleSize()
|
||||||
defaultTermWidth -= 1
|
defaultTermWidth -= 1
|
||||||
if dimensions[0] > defaultTermWidth {
|
if dimensions[0] > defaultTermWidth {
|
||||||
fmt.Println("Error: Set width is larger than terminal width")
|
return nil, fmt.Errorf("Set width is larger than terminal width")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize imgSet 2D slice
|
// Initialize imgSet 2D slice
|
||||||
imgSet := make([][]uint32, terminalHeight)
|
imgSet := make([][]AsciiPixel, terminalHeight)
|
||||||
for i := range imgSet {
|
for i := range imgSet {
|
||||||
imgSet[i] = make([]uint32, terminalWidth)
|
imgSet[i] = make([]AsciiPixel, terminalWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
// smallImg := resize.Resize(uint(terminalWidth), 0, img, resize.Lanczos3)
|
|
||||||
b := smallImg.Bounds()
|
b := smallImg.Bounds()
|
||||||
|
|
||||||
|
// These nested loops iterate through each pixel of resized image and get an AsciiPixel instance
|
||||||
for y := b.Min.Y; y < b.Max.Y; y++ {
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||||
|
|
||||||
var temp []uint32
|
var temp []AsciiPixel
|
||||||
for x := b.Min.X; x < b.Max.X; x++ {
|
for x := b.Min.X; x < b.Max.X; x++ {
|
||||||
|
|
||||||
oldPixel := smallImg.At(x, y)
|
oldPixel := smallImg.At(x, y)
|
||||||
pixel := color.GrayModel.Convert(oldPixel)
|
pixel := color.GrayModel.Convert(oldPixel)
|
||||||
|
|
||||||
// We only need Red from Red, Green, Blue since they have the same value for grayscale images
|
// We only need Red from Red, Green, Blue (RGB) for grayscaleValue in AsciiPixel since they have the same value for grayscale images
|
||||||
r, _, _, _ := pixel.RGBA()
|
r1, _, _, _ := pixel.RGBA()
|
||||||
temp = append(temp, r)
|
|
||||||
|
// Get colored RGB values of original pixel for rgbValue in AsciiPixel
|
||||||
|
r2, g2, b2, _ := oldPixel.RGBA()
|
||||||
|
r2 = uint32(r2 / 257)
|
||||||
|
g2 = uint32(g2 / 257)
|
||||||
|
b2 = uint32(b2 / 257)
|
||||||
|
|
||||||
|
temp = append(temp, AsciiPixel{
|
||||||
|
grayscaleValue: r1,
|
||||||
|
rgbValue: []uint32{r2, g2, b2},
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
imgSet[y] = temp
|
imgSet[y] = temp
|
||||||
}
|
}
|
||||||
|
|
||||||
return imgSet
|
return imgSet, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue