Added dithering and improved performance
This commit is contained in:
parent
8be1d4c9b5
commit
2db0093e58
28
README.md
28
README.md
|
|
@ -1,10 +1,10 @@
|
||||||
# ascii-image-converter
|
# ascii-image-converter
|
||||||
|
|
||||||
[](https://github.com/TheZoraiz/ascii-image-converter/releases/latest)
|
[](https://github.com/TheZoraiz/ascii-image-converter/releases/latest)
|
||||||
[](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
[](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
||||||
[](https://golang.org/)
|
[](https://golang.org/)
|
||||||

|

|
||||||
[](https://snapcraft.io/ascii-image-converter)
|
[](https://snapcraft.io/ascii-image-converter)
|
||||||
|
|
||||||
ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. Available on Windows, Linux and macOS.
|
ascii-image-converter is a command-line tool that converts images into ascii art and prints them out onto the console. Available on Windows, Linux and macOS.
|
||||||
|
|
||||||
|
|
@ -135,8 +135,7 @@ Now you can use ascii-image-converter in the terminal. Execute `ascii-image-conv
|
||||||
You will need to set an Environment Variable to the folder the ascii-image-converter.exe executable is placed in to be able to use it in the command prompt. Follow the instructions in case of confusion:
|
You will need to set an Environment Variable to the folder the ascii-image-converter.exe executable is placed in to be able to use it in the command prompt. Follow the instructions in case of confusion:
|
||||||
|
|
||||||
Download the archive for your Windows architecture [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest), extract it, and open the extracted folder. Now, copy the folder path from the top of the file explorer and follow these instructions:
|
Download the archive for your Windows architecture [here](https://github.com/TheZoraiz/ascii-image-converter/releases/latest), extract it, and open the extracted folder. Now, copy the folder path from the top of the file explorer and follow these instructions:
|
||||||
* In Search, search for and then select: System (Control Panel)
|
* In Search, search for and then select: Advanced System Settings
|
||||||
* Click the Advanced System settings link.
|
|
||||||
* Click Environment Variables. In the section User Variables find the Path environment variable and select it. Click "Edit".
|
* Click Environment Variables. In the section User Variables find the Path environment variable and select it. Click "Edit".
|
||||||
* In the Edit Environment Variable window, click "New" and then paste the path of the folder that you copied initially.
|
* In the Edit Environment Variable window, click "New" and then paste the path of the folder that you copied initially.
|
||||||
* Click "Ok" on all open windows.
|
* Click "Ok" on all open windows.
|
||||||
|
|
@ -199,6 +198,19 @@ Example:
|
||||||
ascii-image-converter [image paths/urls] -b --threshold 170
|
ascii-image-converter [image paths/urls] -b --threshold 170
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### --dither
|
||||||
|
|
||||||
|
Apply dithering on image to make braille art more visible. Since braille dots can only be on or off, dithering images makes them more visible in braille art.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
ascii-image-converter [image paths/urls] -b --dither
|
||||||
|
```
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://raw.githubusercontent.com/TheZoraiz/ascii-image-converter/master/example_gifs/dither.gif">
|
||||||
|
</p>
|
||||||
|
|
||||||
#### --color-bg
|
#### --color-bg
|
||||||
|
|
||||||
If any of the coloring flags is passed, this flag will transfer its color to each character's background. instead of foreground. However, this option isn't available for `--save-img` and `--save-gif`
|
If any of the coloring flags is passed, this flag will transfer its color to each character's background. instead of foreground. However, this option isn't available for `--save-img` and `--save-gif`
|
||||||
|
|
@ -492,6 +504,8 @@ You can fork the project and implement any changes you want for a pull request.
|
||||||
|
|
||||||
[github.com/asaskevich/govalidator](https://github.com/asaskevich/govalidator)
|
[github.com/asaskevich/govalidator](https://github.com/asaskevich/govalidator)
|
||||||
|
|
||||||
|
[github.com/makeworld-the-better-one/dither/v2](https://github.com/makeworld-the-better-one/dither/v2)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
[Apache-2.0](https://github.com/TheZoraiz/ascii-image-converter/blob/master/LICENSE.txt)
|
||||||
|
|
@ -97,7 +97,7 @@ func pathIsGif(gifPath, urlImgName string, pathIsURl bool, urlImgBytes []byte, l
|
||||||
|
|
||||||
var imgSet [][]imgManip.AsciiPixel
|
var imgSet [][]imgManip.AsciiPixel
|
||||||
|
|
||||||
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full, braille)
|
imgSet, err = imgManip.ConvertToAsciiPixels(frameImage, dimensions, width, height, flipX, flipY, full, braille, dither)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error:", err)
|
fmt.Println("Error:", err)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func pathIsImage(imagePath, urlImgName string, pathIsURl bool, urlImgBytes []byt
|
||||||
return "", fmt.Errorf("can't decode %v: %v", imagePath, err)
|
return "", fmt.Errorf("can't decode %v: %v", imagePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full, braille)
|
imgSet, err := imgManip.ConvertToAsciiPixels(imData, dimensions, width, height, flipX, flipY, full, braille, dither)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ func DefaultFlags() Flags {
|
||||||
SaveBackgroundColor: [3]int{0, 0, 0},
|
SaveBackgroundColor: [3]int{0, 0, 0},
|
||||||
Braille: false,
|
Braille: false,
|
||||||
Threshold: 128,
|
Threshold: 128,
|
||||||
|
Dither: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +95,7 @@ func Convert(filePath string, flags Flags) (string, error) {
|
||||||
saveBgColor = flags.SaveBackgroundColor
|
saveBgColor = flags.SaveBackgroundColor
|
||||||
braille = flags.Braille
|
braille = flags.Braille
|
||||||
threshold = flags.Threshold
|
threshold = flags.Threshold
|
||||||
|
dither = flags.Dither
|
||||||
|
|
||||||
// Declared at the start since some variables are initially used in conditional blocks
|
// Declared at the start since some variables are initially used in conditional blocks
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,10 @@ type Flags struct {
|
||||||
// be between 0 and 255. Ideal value is 128.
|
// be between 0 and 255. Ideal value is 128.
|
||||||
// This will be ignored if Flags.Braille is not set
|
// This will be ignored if Flags.Braille is not set
|
||||||
Threshold int
|
Threshold int
|
||||||
|
|
||||||
|
// Apply FloydSteinberg dithering on an image before ascii conversion. This option
|
||||||
|
// is meant for braille art. Therefore, it will be ignored if Flags.Braille is false
|
||||||
|
Dither bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -117,4 +121,5 @@ var (
|
||||||
saveBgColor [3]int
|
saveBgColor [3]int
|
||||||
braille bool
|
braille bool
|
||||||
threshold int
|
threshold int
|
||||||
|
dither bool
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,13 @@ var (
|
||||||
saveBgColor []int
|
saveBgColor []int
|
||||||
braille bool
|
braille bool
|
||||||
threshold int
|
threshold int
|
||||||
|
dither bool
|
||||||
|
|
||||||
// Root commands
|
// Root commands
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "ascii-image-converter [image paths/urls]",
|
Use: "ascii-image-converter [image paths/urls]",
|
||||||
Short: "Converts images and gifs into ascii art",
|
Short: "Converts images and gifs into ascii art",
|
||||||
Version: "1.8.0",
|
Version: "1.9.0",
|
||||||
Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.",
|
Long: "This tool converts images into ascii art and prints them on the terminal.\nFurther configuration can be managed with flags.",
|
||||||
|
|
||||||
// Not RunE since help text is getting larger and seeing it for every error impacts user experience
|
// Not RunE since help text is getting larger and seeing it for every error impacts user experience
|
||||||
|
|
@ -87,6 +88,7 @@ var (
|
||||||
SaveBackgroundColor: [3]int{saveBgColor[0], saveBgColor[1], saveBgColor[2]},
|
SaveBackgroundColor: [3]int{saveBgColor[0], saveBgColor[1], saveBgColor[2]},
|
||||||
Braille: braille,
|
Braille: braille,
|
||||||
Threshold: threshold,
|
Threshold: threshold,
|
||||||
|
Dither: dither,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, imagePath := range args {
|
for _, imagePath := range args {
|
||||||
|
|
@ -126,13 +128,14 @@ func init() {
|
||||||
|
|
||||||
// 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(&colored, "color", "C", false, "Display ascii art with original colors\n(Inverts with --negative flag)\n(Overrides --grayscale and --font-color flags)\n")
|
rootCmd.PersistentFlags().BoolVarP(&colored, "color", "C", false, "Display ascii art with original colors\n(Inverts with --negative flag)\n(Overrides --grayscale and --font-color flags)\n")
|
||||||
rootCmd.PersistentFlags().BoolVar(&colorBg, "color-bg", false, "If some color flag is passed, use that color\non character background instead of foreground\n(Inverts with --negative flag)\n(Doesn't work for --save-img or --save-gif)\n")
|
rootCmd.PersistentFlags().BoolVar(&colorBg, "color-bg", false, "If some color flag is passed, use that color\non character background instead of foreground\n(Inverts with --negative flag)\n(Only applicable for terminal display)\n")
|
||||||
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 60,30 (defaults to terminal height)\n(Overrides --width and --height flags)\n")
|
rootCmd.PersistentFlags().IntSliceVarP(&dimensions, "dimensions", "d", nil, "Set width and height for ascii art in CHARACTER length\ne.g. -d 60,30 (defaults to terminal height)\n(Overrides --width and --height flags)\n")
|
||||||
rootCmd.PersistentFlags().IntVarP(&width, "width", "W", 0, "Set width for ascii art in CHARACTER length\nHeight is kept to aspect ratio\ne.g. -W 60\n")
|
rootCmd.PersistentFlags().IntVarP(&width, "width", "W", 0, "Set width for ascii art in CHARACTER length\nHeight is kept to aspect ratio\ne.g. -W 60\n")
|
||||||
rootCmd.PersistentFlags().IntVarP(&height, "height", "H", 0, "Set height for ascii art in CHARACTER length\nWidth is kept to aspect ratio\ne.g. -H 60\n")
|
rootCmd.PersistentFlags().IntVarP(&height, "height", "H", 0, "Set height for ascii art in CHARACTER length\nWidth is kept to aspect ratio\ne.g. -H 60\n")
|
||||||
rootCmd.PersistentFlags().StringVarP(&customMap, "map", "m", "", "Give custom ascii characters to map against\nOrdered from darkest to lightest\ne.g. -m \" .-+#@\" (Quotation marks excluded from map)\n(Overrides --complex flag)\n")
|
rootCmd.PersistentFlags().StringVarP(&customMap, "map", "m", "", "Give custom ascii characters to map against\nOrdered from darkest to lightest\ne.g. -m \" .-+#@\" (Quotation marks excluded from map)\n(Overrides --complex flag)\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&braille, "braille", "b", false, "Use braille characters instead of ascii\nTerminal must support braille patterns properly\n(Overrides --complex and --map flags)\n")
|
rootCmd.PersistentFlags().BoolVarP(&braille, "braille", "b", false, "Use braille characters instead of ascii\nTerminal must support braille patterns properly\n(Overrides --complex and --map flags)\n")
|
||||||
rootCmd.PersistentFlags().IntVar(&threshold, "threshold", 0, "Threshold for braille art\nValue between 0-255 is accepted\ne.g. --threshold 170\n(Defaults to 128)\n")
|
rootCmd.PersistentFlags().IntVar(&threshold, "threshold", 0, "Threshold for braille art\nValue between 0-255 is accepted\ne.g. --threshold 170\n(Defaults to 128)\n")
|
||||||
|
rootCmd.PersistentFlags().BoolVar(&dither, "dither", false, "Apply dithering on image for braille\nart conversion\n(Only applicable with --braille flag)\n(Negates --threshold flag)\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&grayscale, "grayscale", "g", false, "Display grayscale ascii art\n(Inverts with --negative flag)\n(Overrides --font-color flag)\n")
|
rootCmd.PersistentFlags().BoolVarP(&grayscale, "grayscale", "g", false, "Display grayscale ascii art\n(Inverts with --negative flag)\n(Overrides --font-color flag)\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&complex, "complex", "c", false, "Display ascii characters in a larger range\nMay result in higher quality\n")
|
rootCmd.PersistentFlags().BoolVarP(&complex, "complex", "c", false, "Display ascii characters in a larger range\nMay result in higher quality\n")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&full, "full", "f", false, "Use largest dimensions for ascii art\nthat fill the terminal width\n(Overrides --dimensions, --width and --height flags)\n")
|
rootCmd.PersistentFlags().BoolVarP(&full, "full", "f", false, "Use largest dimensions for ascii art\nthat fill the terminal width\n(Overrides --dimensions, --width and --height flags)\n")
|
||||||
|
|
|
||||||
|
|
@ -180,5 +180,10 @@ func checkInputAndFlags(args []string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dither && !braille {
|
||||||
|
fmt.Printf("Error: image dithering is only reserved for --braille flag\n\n")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 216 KiB |
1
go.mod
1
go.mod
|
|
@ -10,6 +10,7 @@ require (
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
github.com/gookit/color v1.4.2
|
github.com/gookit/color v1.4.2
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
|
github.com/makeworld-the-better-one/dither/v2 v2.2.0
|
||||||
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
|
||||||
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d
|
github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -124,6 +124,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||||
|
github.com/makeworld-the-better-one/dither/v2 v2.2.0 h1:VTMAiyyO1YIO07fZwuLNZZasJgKUmvsIA48ze3ALHPQ=
|
||||||
|
github.com/makeworld-the-better-one/dither/v2 v2.2.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,8 @@ limitations under the License.
|
||||||
package image_conversions
|
package image_conversions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"github.com/TheZoraiz/ascii-image-converter/aic_package/winsize"
|
|
||||||
"github.com/disintegration/imaging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AsciiPixel struct {
|
type AsciiPixel struct {
|
||||||
|
|
@ -31,131 +27,26 @@ type AsciiPixel struct {
|
||||||
rgbValue [3]uint32
|
rgbValue [3]uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func resizeForBraille(asciiWidth, asciiHeight int) (int, int) {
|
|
||||||
return asciiWidth * 2, asciiHeight * 4
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function shrinks the passed image according to passed dimensions or terminal
|
This function shrinks the passed image according to specified or default dimensions.
|
||||||
size if none are passed. Stores each pixel's grayscale and RGB values in an AsciiPixel
|
Stores each pixel's grayscale and RGB values in an AsciiPixel instance to simplify
|
||||||
instance to simplify getting numeric data for ASCII character comparison.
|
getting numeric data for ASCII character comparison.
|
||||||
|
|
||||||
The returned 2D AsciiPixel slice contains each corresponding pixel's values. Grayscale value
|
The returned 2D AsciiPixel slice contains each corresponding pixel's values
|
||||||
ranges from 0 to 65535, while RGB values are separate.
|
|
||||||
*/
|
*/
|
||||||
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille bool) ([][]AsciiPixel, error) {
|
func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int, flipX, flipY, full, isBraille, dither bool) ([][]AsciiPixel, error) {
|
||||||
|
|
||||||
var asciiWidth, asciiHeight int
|
smallImg, err := resizeImage(img, full, isBraille, dimensions, width, height)
|
||||||
var smallImg image.Image
|
|
||||||
|
|
||||||
terminalWidth, terminalHeight, err := winsize.GetTerminalSize()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if full {
|
// We mainatin a dithered image literal along with original image
|
||||||
asciiWidth = terminalWidth - 1
|
// The colors are kept from original image
|
||||||
|
var ditheredImage image.Image
|
||||||
|
|
||||||
// Passing 0 in place of width keeps the original image's aspect ratio
|
if isBraille && dither {
|
||||||
smallImg = imaging.Resize(img, asciiWidth, 0, imaging.Lanczos)
|
ditheredImage = ditherImage(smallImg)
|
||||||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
|
||||||
|
|
||||||
// To fix aspect ratio in eventual ascii art
|
|
||||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
|
||||||
|
|
||||||
if isBraille {
|
|
||||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
|
||||||
}
|
|
||||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
|
||||||
|
|
||||||
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
|
|
||||||
// If either width or height is set and dimensions aren't given
|
|
||||||
|
|
||||||
if width > terminalWidth-1 {
|
|
||||||
return nil, fmt.Errorf("set width must be lower than terminal width")
|
|
||||||
}
|
|
||||||
|
|
||||||
if width != 0 && height == 0 {
|
|
||||||
// If width is set and height is not set, use width to calculate aspect ratio
|
|
||||||
|
|
||||||
asciiWidth = width
|
|
||||||
|
|
||||||
smallImg = imaging.Resize(img, asciiWidth, 0, imaging.Lanczos)
|
|
||||||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
|
||||||
|
|
||||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
|
||||||
if asciiHeight == 0 {
|
|
||||||
asciiHeight = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if height != 0 && width == 0 {
|
|
||||||
// If height is set and width is not set, use height to calculate aspect ratio
|
|
||||||
|
|
||||||
asciiHeight = height
|
|
||||||
|
|
||||||
smallImg = imaging.Resize(img, 0, asciiHeight, imaging.Lanczos)
|
|
||||||
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
|
|
||||||
|
|
||||||
asciiWidth = int(2 * float64(asciiWidth))
|
|
||||||
|
|
||||||
if asciiWidth > terminalWidth-1 {
|
|
||||||
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("both width and height can't be set. Use dimensions instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
if isBraille {
|
|
||||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
|
||||||
}
|
|
||||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
|
||||||
|
|
||||||
} else if len(dimensions) == 0 {
|
|
||||||
// This condition calculates aspect ratio according to terminal height
|
|
||||||
|
|
||||||
asciiHeight = terminalHeight - 1
|
|
||||||
|
|
||||||
smallImg = imaging.Resize(img, 0, asciiHeight, imaging.Lanczos)
|
|
||||||
asciiWidth = smallImg.Bounds().Max.X - smallImg.Bounds().Min.X
|
|
||||||
|
|
||||||
// To fix aspect ratio in eventual ascii art
|
|
||||||
asciiWidth = int(2 * float64(asciiWidth))
|
|
||||||
|
|
||||||
// If ascii width exceeds terminal width, change ratio with respect to terminal width
|
|
||||||
if asciiWidth >= terminalWidth {
|
|
||||||
asciiWidth = terminalWidth - 1
|
|
||||||
|
|
||||||
smallImg = imaging.Resize(img, asciiWidth, 0, imaging.Lanczos)
|
|
||||||
|
|
||||||
asciiHeight = smallImg.Bounds().Max.Y - smallImg.Bounds().Min.Y
|
|
||||||
|
|
||||||
// To fix aspect ratio in eventual ascii art
|
|
||||||
asciiHeight = int(0.5 * float64(asciiHeight))
|
|
||||||
}
|
|
||||||
|
|
||||||
if isBraille {
|
|
||||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
|
||||||
}
|
|
||||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
asciiWidth = dimensions[0]
|
|
||||||
asciiHeight = dimensions[1]
|
|
||||||
|
|
||||||
if isBraille {
|
|
||||||
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
|
||||||
}
|
|
||||||
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repeated despite being in cmd/root.go to maintain support for library
|
|
||||||
//
|
|
||||||
// If there are passed dimensions, check whether the width exceeds terminal width
|
|
||||||
if len(dimensions) > 0 && !full {
|
|
||||||
if dimensions[0] > terminalWidth-1 {
|
|
||||||
return nil, fmt.Errorf("set width must be lower than terminal width")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var imgSet [][]AsciiPixel
|
var imgSet [][]AsciiPixel
|
||||||
|
|
@ -177,6 +68,19 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
||||||
g1 = uint32(g1 / 257)
|
g1 = uint32(g1 / 257)
|
||||||
b1 = uint32(b1 / 257)
|
b1 = uint32(b1 / 257)
|
||||||
|
|
||||||
|
if isBraille && dither {
|
||||||
|
|
||||||
|
// Change charDepth if image dithering is applied
|
||||||
|
// Note that neither grayscale nor original color values are changed.
|
||||||
|
// Only charDepth is kept from dithered image. This is because a
|
||||||
|
// dithered image loses its colors so it's only used to check braille
|
||||||
|
// dots' visibility
|
||||||
|
|
||||||
|
ditheredGrayPixel := color.GrayModel.Convert(ditheredImage.At(x, y))
|
||||||
|
charDepth, _, _, _ = ditheredGrayPixel.RGBA()
|
||||||
|
charDepth = charDepth / 257
|
||||||
|
}
|
||||||
|
|
||||||
// Get co1ored RGB values of original pixel for rgbValue in AsciiPixel
|
// Get co1ored RGB values of original pixel for rgbValue in AsciiPixel
|
||||||
r2, g2, b2, _ := oldPixel.RGBA()
|
r2, g2, b2, _ := oldPixel.RGBA()
|
||||||
r2 = uint32(r2 / 257)
|
r2 = uint32(r2 / 257)
|
||||||
|
|
@ -200,22 +104,3 @@ func ConvertToAsciiPixels(img image.Image, dimensions []int, width, height int,
|
||||||
|
|
||||||
return imgSet, nil
|
return imgSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func reverse(imgSet [][]AsciiPixel, flipX, flipY bool) [][]AsciiPixel {
|
|
||||||
|
|
||||||
if flipX {
|
|
||||||
for _, row := range imgSet {
|
|
||||||
for i, j := 0, len(row)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
row[i], row[j] = row[j], row[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if flipY {
|
|
||||||
for i, j := 0, len(imgSet)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
imgSet[i], imgSet[j] = imgSet[j], imgSet[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return imgSet
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2021 Zoraiz Hassan <hzoraiz8@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package image_conversions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/TheZoraiz/ascii-image-converter/aic_package/winsize"
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
|
"github.com/makeworld-the-better-one/dither/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resizeForBraille(asciiWidth, asciiHeight int) (int, int) {
|
||||||
|
return asciiWidth * 2, asciiHeight * 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func ditherImage(img image.Image) image.Image {
|
||||||
|
|
||||||
|
palette := []color.Color{
|
||||||
|
color.Black,
|
||||||
|
color.White,
|
||||||
|
}
|
||||||
|
|
||||||
|
d := dither.NewDitherer(palette)
|
||||||
|
d.Matrix = dither.FloydSteinberg
|
||||||
|
|
||||||
|
return d.DitherCopy(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeImage(img image.Image, full, isBraille bool, dimensions []int, width, height int) (image.Image, error) {
|
||||||
|
|
||||||
|
var asciiWidth, asciiHeight int
|
||||||
|
var smallImg image.Image
|
||||||
|
|
||||||
|
terminalWidth, terminalHeight, err := winsize.GetTerminalSize()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
imgWidth := float64(img.Bounds().Dx())
|
||||||
|
imgHeight := float64(img.Bounds().Dy())
|
||||||
|
aspectRatio := imgWidth / imgHeight
|
||||||
|
|
||||||
|
if full {
|
||||||
|
asciiWidth = terminalWidth - 1
|
||||||
|
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
||||||
|
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||||
|
|
||||||
|
} else if (width != 0 || height != 0) && len(dimensions) == 0 {
|
||||||
|
// If either width or height is set and dimensions aren't given
|
||||||
|
|
||||||
|
if width > terminalWidth-1 {
|
||||||
|
return nil, fmt.Errorf("set width must be lower than terminal width")
|
||||||
|
}
|
||||||
|
|
||||||
|
if width != 0 && height == 0 {
|
||||||
|
// If width is set and height is not set, use width to calculate aspect ratio
|
||||||
|
|
||||||
|
asciiWidth = width
|
||||||
|
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
||||||
|
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||||
|
|
||||||
|
if asciiHeight == 0 {
|
||||||
|
asciiHeight = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if height != 0 && width == 0 {
|
||||||
|
// If height is set and width is not set, use height to calculate aspect ratio
|
||||||
|
|
||||||
|
asciiHeight = height
|
||||||
|
asciiWidth = int(float64(asciiHeight) * aspectRatio)
|
||||||
|
asciiWidth = int(2 * float64(asciiWidth))
|
||||||
|
|
||||||
|
if asciiWidth > terminalWidth-1 {
|
||||||
|
return nil, fmt.Errorf("width calculated with aspect ratio exceeds terminal width")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("both width and height can't be set. Use dimensions instead")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if len(dimensions) == 0 {
|
||||||
|
// This condition calculates aspect ratio according to terminal height
|
||||||
|
|
||||||
|
asciiHeight = terminalHeight - 1
|
||||||
|
asciiWidth = int(float64(asciiHeight) * aspectRatio)
|
||||||
|
asciiWidth = int(2 * float64(asciiWidth))
|
||||||
|
|
||||||
|
// If ascii width exceeds terminal width, change ratio with respect to terminal width
|
||||||
|
if asciiWidth >= terminalWidth {
|
||||||
|
asciiWidth = terminalWidth - 1
|
||||||
|
asciiHeight = int(float64(asciiWidth) / aspectRatio)
|
||||||
|
asciiHeight = int(0.5 * float64(asciiHeight))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
asciiWidth = dimensions[0]
|
||||||
|
asciiHeight = dimensions[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeated despite being in cmd/root.go to maintain support for library
|
||||||
|
//
|
||||||
|
// If there are passed dimensions, check whether the width exceeds terminal width
|
||||||
|
if len(dimensions) > 0 && !full {
|
||||||
|
if dimensions[0] > terminalWidth-1 {
|
||||||
|
return nil, fmt.Errorf("set width must be lower than terminal width")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isBraille {
|
||||||
|
asciiWidth, asciiHeight = resizeForBraille(asciiWidth, asciiHeight)
|
||||||
|
}
|
||||||
|
smallImg = imaging.Resize(img, asciiWidth, asciiHeight, imaging.Lanczos)
|
||||||
|
|
||||||
|
return smallImg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverse(imgSet [][]AsciiPixel, flipX, flipY bool) [][]AsciiPixel {
|
||||||
|
|
||||||
|
if flipX {
|
||||||
|
for _, row := range imgSet {
|
||||||
|
for i, j := 0, len(row)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
row[i], row[j] = row[j], row[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flipY {
|
||||||
|
for i, j := 0, len(imgSet)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
imgSet[i], imgSet[j] = imgSet[j], imgSet[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imgSet
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: ascii-image-converter
|
name: ascii-image-converter
|
||||||
base: core18
|
base: core18
|
||||||
version: "1.8.0"
|
version: "1.9.0"
|
||||||
summary: Convert images and gifs into ascii art
|
summary: Convert images and gifs into ascii art
|
||||||
description: |
|
description: |
|
||||||
ascii-image-converter is a command-line tool that converts images into ascii art and prints
|
ascii-image-converter is a command-line tool that converts images into ascii art and prints
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue