Commit bca1da60 authored by Jürgen Enge's avatar Jürgen Enge
Browse files

initial commit

parent db3d1807
# Histogram
erzeugt mit Hilfe von ImageMagick (convert) ein Histogramm
basierend auf einer Colormap, welche im Konfigurationsfile definiert wird.
Aufruf:
`histogram -cfg histogram.toml -img bildchen.jpg `
\ No newline at end of file
color.gif

458 Bytes

color.png

458 Bytes

package main
import (
"github.com/BurntSushi/toml"
"log"
"time"
)
type duration struct {
Duration time.Duration
}
func (d *duration) UnmarshalText(text []byte) error {
var err error
d.Duration, err = time.ParseDuration(string(text))
return err
}
type ConfigImageMagick struct {
Identify string
Convert string
Wsl bool
Timeout duration
Remap string
Colors int
Resize string
}
type Config struct {
Logfile string
Loglevel string
AccessLog string
CertPEM string
KeyPEM string
Addr string
JwtKey string
JwtAlg []string
ImageMagick ConfigImageMagick
Colormap map[string]string
}
func LoadConfig(filepath string) Config {
var conf Config
_, err := toml.DecodeFile(filepath, &conf)
if err != nil {
log.Fatalln("Error on loading config: ", err)
}
return conf
}
package main
import (
"fmt"
"github.com/op/go-logging"
"image/color"
"os"
)
var _logformat = logging.MustStringFormatter(
`%{time:2006-01-02T15:04:05.000} %{module}::%{shortfunc} [%{shortfile}] > %{level:.5s} - %{message}`,
)
func CreateLogger(module string, logfile string, loglevel string) (log *logging.Logger, lf *os.File) {
log = logging.MustGetLogger(module)
var err error
if logfile != "" {
lf, err = os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Errorf("Cannot open logfile %v: %v", logfile, err)
}
//defer lf.CloseInternal()
} else {
lf = os.Stderr
}
backend := logging.NewLogBackend(lf, "", 0)
backendLeveled := logging.AddModuleLevel(backend)
backendLeveled.SetLevel(logging.GetLevel(loglevel), "")
logging.SetFormatter(_logformat)
logging.SetBackend(backendLeveled)
return
}
func FileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func ParseHexColor(s string) (c color.RGBA, err error) {
c.A = 0xff
switch len(s) {
case 7:
_, err = fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B)
case 4:
_, err = fmt.Sscanf(s, "#%1x%1x%1x", &c.R, &c.G, &c.B)
// Double the hex digits:
c.R *= 17
c.G *= 17
c.B *= 17
default:
err = fmt.Errorf("invalid length, must be 7 or 4")
}
return
}
logfile = "" # log file location
loglevel = "DEBUG" # CRITICAL|ERROR|WARNING|NOTICE|INFO|DEBUG
accesslog = "" # http access log file
#addr = "localhost:81"
#certpem = "" # tls client certificate file in PEM format
#keypem = "" # tls client key file in PEM format
#jwtkey = "swordfish"
#jwtalg = ["HS256", "HS384", "HS512"] # "hs256" "hs384" "hs512" "es256" "es384" "es512" "ps256" "ps384" "ps512"
[ImageMagick]
#identify = "/usr/bin/identify"
convert = "/usr/bin/convert"
wsl = true # true, if executable is within linux subsystem on windows
timeout = "10s"
remap = "color.png" # das Bild mit der Zielfarbpalette. Wird erzeugt, falls nicht vorhanden. (Schreibrechte beachten)
colors = 10 # Anzahl der Farben im Histogramm
resize = "100x100!"
[colormap]
AliceBlue = "#F0F8FE"
AntiqueWhite = "#FAEBD7"
Aqua = "#00FFFF"
Aquamarine = "#70DB93"
Azure = "#F0FFFF"
Beige = "#F5F5DC"
Black = "#000000"
Blue = "#0000FF"
BlueViolet = "#9F5F9F"
Brass = "#B5A642"
BrightGold = "#D9D919"
Bronze = "#8C7853"
Brown = "#A52A2A"
CadetBlue = "#5F9EA0"
Chocolate = "#D2691E"
Copper = "#B87333"
Coral = "#FF7F50"
Crimson = "#DC143C"
Cyan = "#00FFFF"
DarkBlue = "#00008B"
DarkBrown = "#5C4033"
DarkCyan = "#008B8B"
DarkGoldenRod = "#B8860B"
DarkGray = "#A9A9A9"
DarkGreen = "#006400"
DarkKhaki = "#BDB76B"
DarkMagenta = "#8B008B"
DarkOliveGreen = "#4F4F2F"
DarkOrange = "#FF8C00"
DarkOrchid = "#9932CD"
DarkPurple = "#871F78"
DarkSalmon = "#E9967A"
DarkSlateBlue = "#6B238E"
DarkSlateGray = "#2F4F4F"
DarkTan = "#97694F"
DarkTurquoise = "#7093DB"
DarkViolet = "#9400D3"
DarkWood = "#855E42"
DimGray = "#545454"
DustyRose = "#856363"
FeldSpar = "#D19275"
FireBrick = "#B22222"
ForestGreen = "#238E23"
Gold = "#CD7F32"
GoldenRod = "#DBDB70"
Gray = "#C0C0C0"
Green = "#00FF00"
GreenCopper = "#527F76"
GreenYellow = "#93DB70"
HotPink = "#FF69B4"
HunterGreen = "#215E21"
IndianRed = "#CD5C5C"
Indigo = "#4B0082"
Ivory = "#FFFFF0"
Khaki = "#9F9F5F"
Lavender = "#E6E6FA"
LightBlue = "#C0D9D9"
LightCoral = "#F08080"
LightCyan = "#E0FFFF"
LightGray = "#A8A8A8"
LightGreen = "#90EE90"
LightPink = "#FFB6C1"
LightSteelBlue = "#8F8FBD"
LightWood = "#E9C2A6"
Lime = "#00FF00"
LimeGreen = "#32CD32"
Magenta = "#FF00FF"
MandarinOrange = "#E47833"
Maroon = "#8E236B"
MediumAquaMarine = "#32CD99"
MediumBlue = "#3232CD"
MediumForestGreen = "#6B8E23"
MediumGoldenRod = "#EAEAAE"
MediumOrchid = "#9370DB"
MediumSeaGreen = "#426F42"
MediumSlateBlue = "#7F00FF"
MediumSpringGreen = "#7FFF00"
MediumTurquoise = "#70DBDB"
MediumVioletRed = "#DB7093"
MediumWood = "#A68064"
MidNightBlue = "#2F2F4F"
MintCream = "#F5FFFA"
MistyRose = "#FFE4E1"
NavyBlue = "#23238E"
NeonBlue = "#4D4DFF"
NeonPink = "#FF6EC7"
NewMidnightBlue = "#00009C"
NewTan = "#EBC79E"
OldGold = "#CFB53B"
Olive = "#808000"
Orange = "#FF7F00"
OrangeRed = "#FF2400"
Orchid = "#DB70DB"
PaleGoldenRod = "#EEE8AA"
PaleGreen = "#8FBC8F"
PaleTurquoise = "#AFEEEE"
Pink = "#BC8F8F"
Plum = "#EAADEA"
PowderBlue = "#B0E0E6"
Purple = "#800080"
Quartz = "#D9D9F3"
Red = "#FF0000"
RichBlue = "#5959AB"
RoyalBlue = "#4169E1"
SaddleBrown = "#8B4513"
Salmon = "#6F4242"
SandyBrown = "#F4A460"
Scarlet = "#8C1717"
SeaGreen = "#238E68"
Seinna = "#8E6B23"
Silver = "#E6E8FA"
SkyBlue = "#3299CC"
SlateBlue = "#007FFF"
Snow = "#FFFAFA"
SpicyPink = "#FF1CAE"
SpringGreen = "#00FF7F"
SteelBlue = "#236B8E"
SummerSky = "#38B0DE"
Tan = "#DB9370"
Teal = "#008080"
Thistle = "#D8BFD8"
Tomato = "#FF6347"
Turquoise = "#ADEAEA"
VeryDarkBrown = "#5C4033"
VeryDarkGray = "#5C4033"
Violet = "#422F4F"
VioletRed = "#CC3299"
Wheat = "#D8D8BF"
White = "#FFFFFF"
Yellow = "#FFFF00"
YellowGreen = "#99CC32"
package main
import (
"bufio"
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"github.com/goph/emperror"
"image"
"image/png"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
)
func main() {
configFile := flag.String("cfg", "./histogram.toml", "config file location")
imgFile := flag.String("img", "", "config file location")
flag.Parse()
var exPath = ""
// if configfile not found try path of executable as prefix
if !FileExists(*configFile) {
ex, err := os.Executable()
if err != nil {
panic(err)
}
exPath = filepath.Dir(ex)
if FileExists(filepath.Join(exPath, *configFile)) {
*configFile = filepath.Join(exPath, *configFile)
} else {
log.Fatalf("cannot find configuration file: %v", *configFile)
return
}
}
// configfile should exists at this place
var config Config
config = LoadConfig(*configFile)
// create logger instance
log, lf := CreateLogger("indexer", config.Logfile, config.Loglevel)
defer lf.Close()
var accesslog io.Writer
if config.AccessLog == "" {
accesslog = os.Stdout
} else {
f, err := os.OpenFile(config.AccessLog, os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
log.Panicf("cannot open file %s: %v", config.AccessLog, err)
return
}
defer f.Close()
accesslog = f
}
fmt.Sprintf("%v", accesslog)
// if necessary create a colormap image
if !FileExists(config.ImageMagick.Remap) {
width := len(config.Colormap)
height := 1
upLeft := image.Point{0, 0}
lowRight := image.Point{width, height}
img := image.NewRGBA(image.Rectangle{upLeft, lowRight})
x := 0
for name, value := range config.Colormap {
color, err := ParseHexColor(value)
if err != nil {
log.Panicf("cannot parse color %s: %s", name, value)
return
}
img.Set(x, 0, color)
x++
}
f, err := os.Create(config.ImageMagick.Remap)
if err != nil {
log.Panicf("cannot create file %s: %v", config.ImageMagick.Remap, err)
return
}
png.Encode(f, img)
f.Close()
}
histogram, err := getHistogram(
config.ImageMagick.Convert,
config.ImageMagick.Resize,
config.ImageMagick.Remap,
config.ImageMagick.Colors,
*imgFile,
config.ImageMagick.Timeout.Duration,
config.ImageMagick.Wsl)
if err != nil {
log.Panicf("error getting histogram: %v", err)
}
result := make(map[string]int64)
for col, weight := range histogram {
ok := false
for name, hex := range config.Colormap {
if col == hex {
ok = true
result[name] = weight
}
}
if !ok {
log.Errorf("color %s not in colormap", col)
}
}
json, err := json.MarshalIndent(result, "", " ")
if err != nil {
log.Errorf("cannot marshal result: %v", err)
}
fmt.Println(string(json))
}
func getHistogram(convert, resize, remap string, colors int, file string, timeout time.Duration, wsl bool) (map[string]int64, error) {
result := make(map[string]int64)
cmdparam := []string{file, "-resize", resize, "-dither", "Riemersma", "-colors", fmt.Sprintf("%d", colors), "+dither", "-remap", remap, "-format", `%c`, "histogram:info:"}
cmdfile := convert
if wsl {
cmdparam = append([]string{cmdfile}, cmdparam...)
cmdfile = "wsl"
}
var out bytes.Buffer
out.Grow(1024 * 1024) // 1MB size
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := exec.CommandContext(ctx, cmdfile, cmdparam...)
// cmd.Stdin = dataOut
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
switch e := err.(type) {
case *exec.ExitError:
if e.ProcessState.ExitCode() != 1 {
return result, emperror.Wrapf(err, "error executing (%s %s): %v - %v", cmdfile, cmdparam, out.String(), err)
}
}
}
data := out.String()
// 44391: ( 0, 0, 0, 0) #00000000 none
// 182: ( 0, 0, 0, 2) #00000002 srgba(0,0,0,0.00784314)
scanner := bufio.NewScanner(strings.NewReader(data))
r := regexp.MustCompile(` ([0-9]+):.+(#[0-9A-F]{6})[0-9A-F]{2} `)
for scanner.Scan() {
line := scanner.Text()
matches := r.FindStringSubmatch(line)
if matches == nil {
continue
}
col := matches[2]
countstr := matches[1]
if _, ok := result[col]; !ok {
result[col] = 0
}
count, err := strconv.ParseInt(countstr, 10, 64)
if err != nil {
return result, emperror.Wrapf(err, "cannot parse number %s in line %s", countstr, line)
}
result[col] += count
}
return result, nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment