gomosaic

package module
v0.0.0-...-ae9e004 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2019 License: Apache-2.0 Imports: 26 Imported by: 3

README

gomosaic

gomosaic is an mosaic image generator written entirely in Go. In short: It manages an image database and given a query image returns the image composed or "approximated" of images from the database. The methods implemented are mostly based on "On the use of CBIR in image mosaic generation" by Yue Zhang, 2002, University of Alberta. For more information see the Wiki.

Background

The project originate from a Golang introduction course where I was looking for a project for the participants. Over time I became more and more interested and started working on this project.

Examples

Kybfelsen Orignal image (Kybfelsen by Freiburg, photo by me) transformed into a mosaic with 70x50 tiles: mosaic-lch4-euclid For more examples check the Wiki's example page.

Installation

If you are a developer and want to play around / use this library just run go get -u github.com/FabianWe/gomosaic. If you just want to use the software please check the release page for downloads. Currently binaries are uploaded for linux and windows with am64, if you need something else please contact me or compile for yourself. You should rename the file to plan gomosaic or gomosaic.exe (instead of for example mosaic mosaicamd64.exe). For usage instructions see here. Currently it can only run in command line mode, I'm planning to write a GUI though.

Bugs

There are probably bugs in this software, though it worked fine for "useful" inputs. Please open an issue on github or send an email to fabianwen#posteo.eu (replace # with @).

Nice ways to extend the software

Implement color coherence vectors as mentioned in Yue Zhang, 2002. Also the same image gets selected again and again for a query image, especially with big areas of nearly the same color. Implement a mechanism that prevents this.

License

Copyright 2018 Fabian Wenzelmann

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.

Tird Party Licenses

See here.

Documentation

Overview

Package gomosaic provides methods for generating mosaic images given a database (= set) of images. It takes a query image and returns a composition of the query with images from the database.

Different metrics can be used to find matching images, also the size of the tiles in the result is configurable.

It ships with a executable program to generate mosaic images and administrate image databases on the filesystem.

Index

Constants

View Source
const (
	// Debug is true if code should be compiled in debug mode, printing
	// more stuff and performing checks.
	Debug = true

	// Version is the version of gomosaic.
	Version = "1.1"
)
View Source
const MaxInt = int(MaxUint >> 1)
View Source
const MaxUint = ^uint(0)
View Source
const MinInt = -MaxInt - 1
View Source
const MinUint = 0
View Source
const (
	// QuantizeFactor is used during quantiation, it's the number of values in
	// each rgb component.
	QuantizeFactor uint = 256
)

Variables

View Source
var (
	// RunSimple contains script code that when executed loads images from memory,
	// creates all GCHs and then creates the mosaic. No GCHs are stored on the
	// filesystem.
	// It is parameterized by five parameters: First the directory containing the
	// database images, second the name of the input file, third the name of the
	// output file fourth the number of tiles in the mosaic and last the
	// dimensions of the output. Because the last element can actually be omitted
	// we can create a mosaic with just four parameters.
	//
	// It is at the moment the easiest way to create a mosaic.
	// But it can be very slow if the database is quite large.
	//
	// Example usage: RunSimple ~/Pictures/ input.jpg output.png 20x30 x
	//
	// This would create output.png with 20x30 tiles from input.jpg with images
	// from ~/Pictures/. The output image would have the same size as the input
	// image (no dimension given).
	RunSimple = `storage load $1
gch create
mosaic $2 $3 gch-euclid $4 $5`

	// RunMetric is similar to RunSimple but takes an additional argument: The
	// metric name.
	//
	// Example usage: RunMetric ~/Pictures/ input.jpg output.png 20x30 x cosine
	//
	// This would do the same as RunSimple but using cosine-similarity.
	RunMetric = `storage load $1
gch create
mosaic $2 $3 gch-$6 $4 $5`

	// CompareMetrics is similar to RunSimple but generates multiple output
	// images based on different metrics. Thus the third argument is not an path
	// for a file but a directory. In this directory multiple mosaics will be
	// generated.
	//
	// Example usage: CompareMetrics ~/Pictures/ input.jpg ./output/ 20x30 x
	CompareMetrics = `` /* 326-byte string literal not displayed */

)
View Source
var (
	// BufferSize is the (default) size of buffers. Some methods create buffered
	// channels, this parameter controls how big such buffers might be.
	// Usually such buffers store no big data (ints, bools etc.).
	BufferSize = 1000
)
View Source
var (
	// DefaultResizer is the resizer that is used by default, if you're
	// looking for a resizer default argument this seems useful.
	DefaultResizer = NewNfntResizer(resize.MitchellNetravali)
)
View Source
var (
	// ErrCmdSyntaxErr is returned by a CommandFunc if the syntax for the command
	// is invalid.
	ErrCmdSyntaxErr = errors.New("invalid command syntax")
)
View Source
var (
	// ImageCacheSize is the default size of images caches. Some procedures
	// (especially the composition of mosaics) might be much more performant if
	// they're allowed to cache images. This variable controls the size of such
	// caches. It must be a value ≥ 1.
	ImageCacheSize = 15
)

Functions

func CanberraDistance

func CanberraDistance(p, q []float64) float64

CanberraDistance is a weighted version of the manhattan distance, see https://en.wikipedia.org/wiki/Canberra_distance

func CdCommand

func CdCommand(state *ExecutorState, args ...string) error

CdCommand is a command that changes the current directory.

func ChessboardDistance

func ChessboardDistance(p, q []float64) float64

ChessboardDistance is the max over all absolute distances, see https://reference.wolfram.com/language/ref/ChessboardDistance.html

func ComposeMosaic

func ComposeMosaic(storage ImageStorage, symbolicTiles [][]ImageID,
	mosaicDivison TileDivision, resizer ImageResizer, s ResizeStrategy,
	numRoutines, cacheSize int, progress ProgressFunc) (image.Image, error)

ComposeMosaic concurrently composes a mosaic image given the distribution in tiles and the selected images for each tile. Images are loaded by the storage. The resizer and the resize strategy are used to resize database images to fit in tiles. The mosaic division must start from (0, 0) and the rectangles are not allowed to overlap, in short it has be what we intuively would call a distribution into tiles.

Scaled database images are cached to speed up the generation process. The cache size parameter is the size of the cache used. The more elements in the cache the faster the composition process is, but it also increases memory consumption. If cache size is ≤ 0 the DefaultCacheSize is used.

func ComputeHeaps

func ComputeHeaps(storage ImageStorage, metric ImageMetric, query image.Image, dist TileDivision,
	k, numRoutines int, progress ProgressFunc) ([][]*ImageHeap, error)

ComputeHeaps computes the image heap for each tile given k (the number of images to store in each heap).

Metric will not be initialized, that must happen before.

func CosineSimilarity

func CosineSimilarity(p, q []float64) float64

CosineSimilarity returns 1 - cos(∡(p, q)). The result is between 0 and 2, as special case is that the length of p or q is 0, in this case the result is 2.1

func EuclideanDistance

func EuclideanDistance(p, q []float64) float64

EuclideanDistance returns the euclidean distance of two vectors, that is sqrt( (p1 - q1)² + ... + (pn - qn)² ).

func Execute

func Execute(handler CommandHandler, commandMap CommandMap)

Execute implements the high-level execution loop as described in the documentation of CommandHandler. commandMap is used to lookup commands.

func ForceResize

func ForceResize(resizer ImageResizer, tileWidth, tileHeight uint, img image.Image) image.Image

ForceResize is a resize strategy that resizes to the given width and height, ignoring the ration of the original image.

func GCHCommand

func GCHCommand(state *ExecutorState, args ...string) error

GCHCommand can create histograms for all images in storage, save and load files.

func GCHFileName

func GCHFileName(k uint, ext string) string

GCHFileName returns the proposed filename for a file containing global color histograms. When saving HistogramFSController instances (that's the type used for storing GCHs) the file should be saved by this file name. The scheme is "gch-k.(gob|json)". k is the value as defined in histogram and ext is the extension (gob for gob encoded files and json for json encoded files).

For example histograms with 8 sub-divions encoded as json would be stored in a file "gch-8.json".

func GenHeapViews

func GenHeapViews(heaps [][]*ImageHeap) [][][]ImageHeapEntry

GenHeapViews can be used to transform the image heaps into the actual list of images in that heap. It's only a shortcut calling GetView on each heap.

func GetHistogramMetricNames

func GetHistogramMetricNames() []string

GetHistogramMetricNames returns a list of all registered named histogram metrics. See RegisterHistogramMetric for details.

func GetInterP

func GetInterP(quality uint) resize.InterpolationFunction

GetInterP returns an interpolation function given a desired quality. The higher the quality the better the interpolation should be, but execution time is higher. Currently supported are values between 0 and 4, each selecting a different interpolation function. Values greater than 4 are treated as 4.

This method assumes that the interpolation functions provided by nfnt/resize can be sorted according to their quality. This should be a reasonable assumption.

func ImageStorageCommand

func ImageStorageCommand(state *ExecutorState, args ...string) error

ImageStorageCommand is a command that executes tasks with the fs mapper and therefor the image storage (the user doesn't need to know about details as mapper and storage, so it's simply called storage). This command without arguments just prints the number of databases in the storage. With the single argument "list" it prints the path of each image in the storage. With the argument "load" a second argument "DIR" is required, this will load all images from the directory in the storage. If a third argument is provided this must be a bool that is true if the directory should be scanned recursively. The default is not to scan recursively.

Note that jpg and png files are considered valid image types, thus image.jpeg and image.png should be included if you're planning to use this function.

func InitTilesHelper

func InitTilesHelper(storage ImageStorage, query image.Image, dist TileDivision,
	numRoutines int,
	init func(tiles Tiles) error,
	onTile func(i, j int, tileImage image.Image) error) error

InitTilesHelper is a helper function to easily create a concurrent InitTiles function for ImageMetrics.

It will do the following: First it creates the actual tiles of the image and then it will call the init function. In this function you don't have to create metric values or something, just initialize the datastructure that holds information. It then gets concurrently filled by calls of onTile which is concurrently called for each tile of the image.

func IntAbs

func IntAbs(a int) int

IntAbs returns the absolute value of a, that is |a| as an int.

func IntMax

func IntMax(a, b int) int

func IntMin

func IntMin(a, b int) int

func InterPFromString

func InterPFromString(s string) (resize.InterpolationFunction, error)

InterPFromString parses s as an interpreation function, valid values include: "nearest-neighbor", "bilinear", "bicubic", "mitchell-netravali", "lanczos2" and "lanczos3".

func InterPString

func InterPString(interP resize.InterpolationFunction) string

InterPString returns a string representation of the interpolation function since resize doesn't seem to provide a String() function.

func JPGAndPNG

func JPGAndPNG(ext string) bool

JPGAndPNG is an implementation of SupportedImageFunc accepting jpg and png file extensions.

func KeepRatioHeight

func KeepRatioHeight(originalWidth, originalHeight, width int) int

KeepRatioHeight computes the new height given the original width and height s.t. the ration remains unchanged. The original values must be > 0.

func KeepRatioWidth

func KeepRatioWidth(originalWidth, originalHeight, height int) int

KeepRatioWidth computes the new width given the original width and height s.t. the ration remains unchanged. The original values must be > 0.

func LCHCommand

func LCHCommand(state *ExecutorState, args ...string) error

func LCHFileName

func LCHFileName(k, schemeSize uint, ext string) string

LCHFileName returns the proposed filename for a file containing lchs. When saving LCHFSController instances (that's the type used for storing GCHs) the file should be saved by this file name. The scheme is "lch-scheme-k.(gob|json)". k is the value as defined in histogram and ext is the extension (gob for gob encoded files and json for json encoded files). Scheme is the scheme size, currently implemented are two parting techniques. This naming is ambiguous (someone could come up with another technique to build 5 blocks) but that should be well enough.

For example LCHs with 8 sub-divions encoded as json with the 5 parts scheme would be stored in a file "lch-5-8.json".

func Manhattan

func Manhattan(p, q []float64) float64

Manhattan returns the manhattan distance of two vectors, that is |p1 - q1| + ... + |pn - qn|.

func MaxUint32

func MaxUint32(a uint32, elements ...uint32) uint32

func MaxUint8

func MaxUint8(a uint8, elements ...uint8) uint8

func MinDistance

func MinDistance(p, q []float64) float64

MinDistance returns 1 - ( min(p1, q1) + ... + min(pn, qn) ).

func MinUint32

func MinUint32(a uint32, elements ...uint32) uint32

func MinUint8

func MinUint8(a uint8, elements ...uint8) uint8

func MosaicCommand

func MosaicCommand(state *ExecutorState, args ...string) error

MosaicCommand creates a mosaic images. For details see the entry created in the init() method / the description text of the command our the online documentation. Usage example: mosaic in.jpg out.jpg gch-cosine 20x30 1024x768

func Parameterized

func Parameterized(r io.Reader, args ...string) (io.Reader, error)

Parameterized is used to transform parameterized commands into executable commands, that means replacing variables $i with the provided argument. Example: The command "gch load $1" can be called with one argument that will replace the placeholder $1. It should work with an arbitrary number of variables (let's hope so).

The current implementation works by reading the whole original reader and then transforming the elements, given that scripts are not too long the overhead should be manageable.

If no parameters are given it is best practise to avoid calling this method and use the original reader.

func ParameterizedFromStrings

func ParameterizedFromStrings(commands []string, args ...string) io.Reader

ParameterizedFromStrings runs the commands provided in commands (each entry is considered to be a command) and replaces placeholders by args. For placeholder details see Parameterized.

func ParseCommand

func ParseCommand(s string) ([]string, error)

ParseCommand parses a command of the form "COMMAND ARG1 ... ARGN". Examples:

foo bar is the command "foo" with argument "bar". Arguments might also be enclosed in quotes, so foo "bar bar" is parsed as command foo with argument bar bar (a single argument).

func ParseDimensions

func ParseDimensions(s string) (int, int, error)

ParseDimensions parses a string of the form "AxB" where A and B are positive integers.

func ParseDimensionsEmpty

func ParseDimensionsEmpty(s string) (int, int, error)

ParseDimensionsEmpty works as ParseDimensions of the form "AxB" but also also A and / or B to be empty. That is "1024x" would be valid as well as "x768" and "x". Empty values are returned as -1.

func ParsePercent

func ParsePercent(s string) (float64, error)

func ProgressIgnore

func ProgressIgnore(num int)

ProgressIgnore is a ProgressFunc that does nothing.

func PwdCommand

func PwdCommand(state *ExecutorState, args ...string) error

PwdCommand is a command that prints the current working directory.

func QuantizeC

func QuantizeC(val uint8, k uint) uint8

QuantizeC quantizes the color component c (sub-divison in k values). That is it returns c * (k / 256). k must be a number between 1 and 256.

func RGBID

func RGBID(r, g, b, k uint) uint

RGBID assigns each RGB color (k sub-divisions) a unique id. That id is given by r + k * g + k² * b.

RGBs Id method uses this function, it's just more convenient if the id can be computed without creating an RGB object.

func ReaderFromCmdLines

func ReaderFromCmdLines(lines []string) io.Reader

ReaderFromCmdLines returns a reader for a script source that reads the content of the combined lines.

func RegisterHistogramMetric

func RegisterHistogramMetric(name string, metric HistogramMetric) bool

RegisterHistogramMetric is used to register a named histogram metric. It will only add the metric if the name does not exist yet. The result is true if the metric was successfully registered and false otherwise. Some metrics are registered by default. All names must be lowercase strings, the register and get methods will always transform a string to lowercase.

All metrics should be registered by an init method.

func SetVarCommand

func SetVarCommand(state *ExecutorState, args ...string) error

SetVarCommand sets a variable to a new value.

func StatsCommand

func StatsCommand(state *ExecutorState, args ...string) error

StatsCommand is a command that prints variable / value pairs.

func SubImage

func SubImage(img image.Image, r image.Rectangle) (image.Image, error)

SubImage returns a subimage of img given the boundaries r. The rectangle should be a valid area in the image. If the image type does not have a sub image method an error is returned.

Types

type AverageColor

type AverageColor RGB

AverageColor describes the average of several RGB colors.

func ComputeAverageColor

func ComputeAverageColor(img image.Image) AverageColor

ComputeAverageColor computes the average color of an image.

func (AverageColor) Dist

func (c AverageColor) Dist(other AverageColor, metric VectorMetric) float64

Dist returns the distance between the two average color vectors given the metric for the component vectors.

type CmdVarietySelector

type CmdVarietySelector int
const (
	CmdVarietyNone CmdVarietySelector = iota
	CmdVarietyRand
	CmdVarietyMetric
)

func ParseCMDVarietySelector

func ParseCMDVarietySelector(s string) (CmdVarietySelector, error)

func (CmdVarietySelector) DisplayString

func (s CmdVarietySelector) DisplayString() string

type Command

type Command struct {
	Exec        CommandFunc
	Usage       string
	Description string
}

Command a command consists of a function to actually execute the command and some information about the command.

type CommandFunc

type CommandFunc func(state *ExecutorState, args ...string) error

CommandFunc is a function that is applied to the current states and arguments to that command.

type CommandHandler

type CommandHandler interface {
	Init() *ExecutorState
	Start(s *ExecutorState)
	Before(s *ExecutorState)
	After(s *ExecutorState)
	OnParseErr(s *ExecutorState, err error) bool
	OnInvalidCmd(s *ExecutorState, cmd string) bool
	OnSuccess(s *ExecutorState, cmd Command)
	OnError(s *ExecutorState, err error, cmd Command) bool
	OnScanErr(s *ExecutorState, err error)
}

CommandHandler together with Execute implements a high-level command execution loop. CommandFuncs are applied to the current state until there are no more commands to execute (no more input).

We won't go into the details, please read the source for details (yes, that's probably not the best practise but is so much easier in this case).

A command has the form "COMMAND ARG1 ... ARGN" where COMMAND is the command name and ARG1 to ARGN are the arguments for the command.

Here's a rough summary of what Execute will do: First it creates an initial state by calling Init. After that it immediately calls Start to notify the handler that the execution begins.

We use to different methods to separate object creation from execution. Before a command is executed the Before method is called to notify the handler that a command will be executed.

Then a loop will begin that reads all lines from the state's reader. If there is a command line the line will be parsed, if an error during parsing occurred the handler gets notified via OnParseErr. This method should return true if the execution should continue despite the error. Then a lookup in the provided command man happens: If the command was found the corresponding Command object is executed. If it was not found the OnInvalidCmd function is called on the handler. Again it should return true if the exeuction should continue despite the error. If this execution was successful the OnSuccess function is called with the executed command. If the execution was unsuccessful the OnError function will be called. Commands should return ErrCmdSyntaxErr if the syntax of the command is incorrect (for example invalid number of arguments) and OnError can do special handling in this case. Again OnError returns true if execution should continue. OnScanErr is called if there is an error while reading a command line from the state's reader.

Errors while writing to the provided out stream might be reported, but this is not a requirement.

type CommandMap

type CommandMap map[string]Command

CommandMap maps command names to Commands.

var DefaultCommands CommandMap

DefaultCommands contains some commands that are often used.

type DistanceHeapSelector

type DistanceHeapSelector struct {
	Metric      ImageMetric
	K           int
	NumRoutines int
}

func NewDistanceHeapSelector

func NewDistanceHeapSelector(metric ImageMetric, k, numRoutines int) *DistanceHeapSelector

func (*DistanceHeapSelector) Init

func (selector *DistanceHeapSelector) Init(storage ImageStorage) error

func (*DistanceHeapSelector) SelectImages

func (selector *DistanceHeapSelector) SelectImages(storage ImageStorage,
	query image.Image, dist TileDivision, progress ProgressFunc) ([][]ImageID, error)

type DivideMode

type DivideMode int

DivideMode is used to describe in which way to handle remaining pixels in image division. The exact meaning might differ (depending on the arranger) but as an example consider an image with 99 pixels width. If we want to divide the image into tiles with 10 pixels. This leads to a 9 tiles with 10 pixels, but 9 pixels are left. DivideMode now describes what to do with the remaining 9 pixels: Crop would mean to crop the image and discard the remaining pixels. Adjust would mean to adjust the last tile to have a width of 9 and pad would mean to add an additional tile with width 10 (and thus describing a tile that does not intersect with the image everywhere).

const (
	// DivideCrop is the mode in which remaining pixels are discarded.
	DivideCrop DivideMode = iota
	// DivideAdjust is the mode in which a tile is adjusted to the remaining
	// pixels.
	DivideAdjust
	// DividePad is the mode in which a tile of a certain size is created even
	// if not enough pixels are remaining.
	DividePad
)

func (DivideMode) String

func (mode DivideMode) String() string

type ExecutorState

type ExecutorState struct {
	// WorkingDir is the current directory. It must always be an absolute path.
	WorkingDir string

	// Mapper is the current file system mapper.
	Mapper *FSMapper

	// ImgStorage is image database, backed by Mapper.
	ImgStorage *FSImageDB

	// NumRoutines is the number of go routines used for different tasks during
	// mosaic generation.
	NumRoutines int

	// GCHStorage stores the global color histograms. Whenever new images are
	// loaded the old histograms become invalid (set to nil again) and must
	// be reloaded / created.
	GCHStorage *MemoryHistStorage

	// LCHStorage stores the local color histograms. Whenever new images are
	// loaded the old histograms become invalid (set to nil again) and must
	// be reloaded / created.
	LCHStorage *MemoryLCHStorage

	// Verbose is true if detailed output should be generated.
	Verbose bool

	// In is the source to read commands from (line by line).
	In io.Reader

	// Out is used to write state information.
	Out io.Writer

	// CutMosaic describes whether the mosaic should be "cut".
	// Cutting means to cut the resulting image s.t. each tile has the same bounds.
	// Example: Suppose you want to divide an image with width 99 and want ten
	// tiles horizontally. This leads to an image where each tile has
	// a width of 9. Ten tiles yields to a final width of 90. As you see 9 pixels
	// are "left over". The distribution in ten tiles is fixed, so we can't add
	// another tile. But in order to enforce the original proposed width
	// we can enlarge the last tile by 9 pixels. So we would have 9 tiles with
	// width 9 and one tile with width 18.
	//
	// Cut controls what to do with those remaining pixels: If cut is set
	// to true we skip the 9 pixels and return an image of size 90. If set to
	// false we enlarge the last tile and return an image with size 99.
	// Usually the default is false.
	CutMosaic bool

	// JPGQuality is the quality between 1 and 100 used when storing images.
	// The higher the value the better the quality. We use a default quality of
	// 100.
	JPGQuality int

	// InterP is the interpolation functions used when resizing the images.
	InterP resize.InterpolationFunction

	// Cache size is the size of the image cache during mosaic composition.
	// The more elements in the cache the faster the composition process is, but
	// it also increases memory consumption. If cache size is < 0 the
	// DefaultCacheSize is used.
	CacheSize int

	// VarietySelector is the current variety selector, defaults to
	// cmdVarietyNone.
	VarietySelector CmdVarietySelector

	// BestFit is the percent value (between 0 and 1) that describes how much
	// percent of the input images are considered in the variety heaps.
	BestFit float64
}

ExecutorState is the state during a CommandHandler execution, see that type for more details of the workflow.

The variables in the state are shared among the executions of the command functions.

func (*ExecutorState) GetBestFitImages

func (state *ExecutorState) GetBestFitImages(numImages int) int

GetBestFitImages multiplies that best fit factor (BestFit) with num images to get the number of best fit images for the variety selectors. It sets same sane defaults in the case something weird happens.

func (*ExecutorState) GetPath

func (state *ExecutorState) GetPath(path string) (string, error)

GetPath returns the absolute path given some other path. The idea is the following: If the user inputs a path we have two cases: The user used an absolute path, in this case we use this absolute path to perform tasks with. If it is a relative path we join the working directory with this path and thus retrieve the absolute path we work on.

The home directory can be used like on Unix: ~/Pictures is the Pictures directory in the home directory of the user.

type FSImageDB

type FSImageDB struct {
	// contains filtered or unexported fields
}

FSImageDB implements ImageStorage. It uses images stored on the filesystem and opens them on demand. Files are retrieved from a FSMapper.

func NewFSImageDB

func NewFSImageDB(mapper *FSMapper) *FSImageDB

NewFSImageDB returns a new data base given the filesystem mapper.

func (FSImageDB) LoadConfig

func (db FSImageDB) LoadConfig(id ImageID) (image.Config, error)

LoadConfig loads the image configuration for the image with the given id from the filesystem.

func (FSImageDB) LoadImage

func (db FSImageDB) LoadImage(id ImageID) (image.Image, error)

LoadImage loads the image with the given id from the filesystem.

func (*FSImageDB) NumImages

func (db *FSImageDB) NumImages() ImageID

NumImages returns the number of images in the database.

type FSMapper

type FSMapper struct {
	NameMapping map[string]ImageID
	IDMapping   []string
}

FSMapper is a mapping between filesystem images and internal ids. It maps both, ids to images and images to ids, implementing a bijective mapping.

A problem arises when we store for example histograms. Images may be deleted or new images added, thus the histograms stored (e.g. in an array) can't be directly used.

FSMapper provides methods to keep such things synched.

A mapper maps absolute paths to image ids (and vice versa). Meaning that the mapping can't just be transferred to another machine.

func CreateFSMapper

func CreateFSMapper(root string, recursive bool, filter SupportedImageFunc) (*FSMapper, error)

CreateFSMapper creates an FSMapper containing images from the root directory. All files for which filter returns true will be registered to the mapping. If recursive is true also subdirectories of root will be scanned, otherwise only root is scanned.

The filter function can be nil and is then set to JPGAndPNG. Any error while scanning the directory / the directories is returned together with nil.

This is the same as creating a new FSMapper and then calling its load method. The only difference is that on an error nil will be returned (not a mapper containing some images).

func NewFSMapper

func NewFSMapper() *FSMapper

NewFSMapper creates a new mapper without any values (empty mappings). To create a mapper with content (i.e. reading a list of files from the filesystem) use CreateFSMapper.

func (*FSMapper) Clear

func (m *FSMapper) Clear()

Clear removes all registered images from the mappings.

func (*FSMapper) GetID

func (m *FSMapper) GetID(path string) (ImageID, bool)

GetID returns the id of an absolute image path. If the image wasn't registered the id will be invalid and the boolean false.

func (*FSMapper) GetPath

func (m *FSMapper) GetPath(id ImageID) (string, bool)

GetPath returns the absolute path of the image with the given id. If no image with that id exists the returned path is the empty string and the boolean false.

func (*FSMapper) Gone

func (m *FSMapper) Gone(paths []string) []string

Gone returns images that are gone, i.e. images that are not registered in the mapper. This is useful for storages that store for example histograms. These storages can test which images are gone ("missing") from the filesystem.

The result contains all images from paths that are not registered in the mapper.

A storage can implement a "Mising" method by simply iterating over all elements in the mapper and testing if it has an entry for that.

func (*FSMapper) Len

func (m *FSMapper) Len() int

Len returns the number of images stored in the mapper.

func (*FSMapper) Load

func (m *FSMapper) Load(path string, recursive bool, filter SupportedImageFunc) error

Load scans path for images supported by gomosaic.

All files for which filter returns true will be registered to the mapping. If recursive is true also subdirectories of root will be scanned, otherwise only root is scanned.

The filter function can be nil and is then set to JPGAndPNG. Any error while scanning the directory / the directories is returned together with nil.

Note that if an error occurs it is still possible that some images were added to the storage.

func (*FSMapper) NumImages

func (m *FSMapper) NumImages() ImageID

NumImages returns the number of images in the mapper as an ImageID. Values between 0 and NumImages - 1 are considered valid ids.

func (*FSMapper) Register

func (m *FSMapper) Register(path string) (ImageID, bool)

Register registers an image to the mapping and returns the id of the image. If an image with that path is already present in the storage the images will not get added.

path must be an absolute path to an image resource, this is however not checked / enforced in Register. Ensure this before calling the function. The returned value is the newly assigned ImageID; however if the image is already present the second return value is false and the ImageID is not valid. So only if the returned bool is true the ImageID may be used.

Register adjusts both mappings and is not safe for concurrent use.

type FiveLCHScheme

type FiveLCHScheme struct{}

FiveLCHScheme implements the scheme with vie parts: north, west, south, east and center.

It implements LCHScheme, the LCH contains the GCHs for the parts in the order described above.

func NewFiveLCHScheme

func NewFiveLCHScheme() FiveLCHScheme

NewFiveLCHScheme returns a new FourLCHScheme.

func (FiveLCHScheme) GetParts

func (s FiveLCHScheme) GetParts(img image.Image) ([][]image.Image, error)

GetParts returns exactly five histograms (N, W, S, E, C).

type FixedNumDivider

type FixedNumDivider struct {
	NumX, NumY int
	Cut        bool
}

FixedNumDivider is an ImageDivider that divides an image into a given number of tiles. Cut describes whether the image should be "cut". Cutting means to cut the resulting image s.t. each tile has the same bounds. Example: Suppose you want to divide an image with width 99 and want ten tiles horizontally. This leads to an image where each tile has a width of 9. Ten tiles yields to a final width of 90. As you see 9 pixels are "left over". The distribution in ten tiles is fixed, so we can't add another tile. But in order to enforce the original proposed width we can enlarge the last tile by 9 pixels. So we would have 9 tiles with width 9 and one tile with width 18.

Cut controls what to do with those remaining pixels: If cut is set to true we skip the 9 pixels and return an image of size 90. If set to false we enlarge the last tile and return an image with size 99.

func NewFixedNumDivider

func NewFixedNumDivider(numX, numY int, cut bool) *FixedNumDivider

NewFixedNumDivider returns a new FixedNumDivider given the number of tiles in x and y direction.

func (*FixedNumDivider) Divide

func (divider *FixedNumDivider) Divide(bounds image.Rectangle) TileDivision

Divide implements the Divide method of ImageDivider.

type FixedSizeDivider

type FixedSizeDivider struct {
	Width, Height int
	Mode          DivideMode
}

FixedSizeDivider divides an image into tiles where each tile has the given width and height. It implements ImageDivider. The DivideMode describes how to deal with "remaining" pixel.

func NewFixedSizeDivider

func NewFixedSizeDivider(width, height int, mode DivideMode) FixedSizeDivider

NewFixedSizeDivider returns a new FixedSizeDivider.

func (FixedSizeDivider) Divide

func (divider FixedSizeDivider) Divide(bounds image.Rectangle) TileDivision

Divide implements the Divide method of ImageDivider.

type FourLCHScheme

type FourLCHScheme struct{}

FourLCHScheme implements the scheme with four parts: north, west, south and east.

It implements LCHScheme, the LCH contains the GCHs for the parts in the order described above.

func NewFourLCHScheme

func NewFourLCHScheme() FourLCHScheme

NewFourLCHScheme returns a new FourLCHScheme.

func (FourLCHScheme) GetParts

func (s FourLCHScheme) GetParts(img image.Image) ([][]image.Image, error)

GetParts returns exactly four histograms (N, W, S, E).

type HeapImageSelector

type HeapImageSelector struct {
	Metric      ImageMetric
	Selector    HeapSelector
	K           int
	NumRoutines int
}

HeapImageSelector implements ImageSelector. It first computes the image heaps given the metric and then uses the provided HeapSelector to select the actual images from the heaps.

func NewHeapImageSelector

func NewHeapImageSelector(metric ImageMetric, selector HeapSelector, k, numRoutines int) *HeapImageSelector

NewHeapImageSelector returns a new selector. Metric is the image metric that is used for the image heaps, selector is used to select the actual images from the heaps. k is the number of images stored in each image heap (that is the k best images are stored in the heaps). NumRoutines is the number of things that happen concurrently (not exactly, but guidance level),

func RandomHeapImageSelector

func RandomHeapImageSelector(metric ImageMetric, k, numRoutines int) *HeapImageSelector

RandomHeapImageSelector returns a HeapImageSelector using a random selection. Thus it can be used as an ImageSelector.

func (*HeapImageSelector) Init

func (sel *HeapImageSelector) Init(storage ImageStorage) error

Init just calls InitStorage on the provided image metric.

func (*HeapImageSelector) SelectImages

func (sel *HeapImageSelector) SelectImages(storage ImageStorage,
	query image.Image, dist TileDivision, progress ProgressFunc) ([][]ImageID, error)

SelectImages first calls InitTiles on the provided metric, then computes the heaps and applies the selector on the heaps.

type HeapSelector

type HeapSelector interface {
	Select(storage ImageStorage, query image.Image, dist TileDivision, heaps [][]*ImageHeap) ([][]ImageID, error)
}

HeapSelector is used to select the actual images after creating the image heaps.

type Histogram

type Histogram struct {
	// Entries contains for each r, g, b color the frequency. The histogram does
	// not save each possible r, g, b color but the quantizd version.
	// That is it stores frequencies (r, g, b) where r, g, b < k.
	Entries []float64
	// K is the number of sub-divisions used to create the histogram.
	// It must be a number between 1 and 256.
	K uint
}

Histogram describes a color histogram for an image. It counts the number of pixels with a certain color or the relative frequency of the color (normalized histogram).

An entry for color r, g, b quantized to k sub-divisions is stored at position r + k * g + k * k * b.

To compute the id of an r, g, b color use RGBID or ID on RGB objects.

func CreateAllHistograms

func CreateAllHistograms(storage ImageStorage, normalize bool, k uint, numRoutines int, progress ProgressFunc) ([]*Histogram, error)

CreateAllHistograms creates all histograms for images in the storage. It is a shortcut using CreateHistograms, see this documentation for details.

func CreateHistograms

func CreateHistograms(ids []ImageID, storage ImageStorage, normalize bool, k uint, numRoutines int, progress ProgressFunc) ([]*Histogram, error)

CreateHistograms creates histograms for all images in the ids list and loads the images through the given storage. If you want to create all histograms for a given storage you can use CreateAllHistograms as a shortcut. It runs the creation of histograms concurrently (how many go routines run concurrently can be controlled by numRoutines). k is the number of sub-divisons as described in the histogram type, If normalized is true the normalized histograms are computed. progress is a function that is called to inform about the progress, see doucmentation for ProgressFunc.

func CreateHistogramsSequential

func CreateHistogramsSequential(storage ImageStorage, normalize bool, k uint, progress ProgressFunc) ([]*Histogram, error)

CreateHistogramsSequential works as CreateAllHistograms but does not use concurrency.

func GenHistogram

func GenHistogram(img image.Image, k uint, normalize bool) *Histogram

GenHistogram creates a histogram given an image and the number of sub-divions in each direction (k), k must be a number between 1 and 256. The histogram contains the freuqency of each color after quantiation in k sub-divisions.

func GenHistogramFromList

func GenHistogramFromList(k uint, normalize bool, images ...image.Image) *Histogram

GenHistogramFromList generates a histogram containing an entry for each image in the images list. k is the number of sub-divisons. If normalize is true the normalized histogram will be computed instead of the frequency histogram.

func NewHistogram

func NewHistogram(k uint) *Histogram

NewHistogram creates a new histogram given the number of sub-divions in each direction. k must be a number between 1 and 256.

func (*Histogram) Add

func (h *Histogram) Add(img image.Image, k uint)

Add creates the histogram given an image, that is it counts how often a color appears in the image. k is the number of sub-divisions in each direction, it must be a number between 1 and 256. The histogram contains the freuqency of each color after quantiation in k sub-divisions.

This method can be called multiple times to accumulate the histograms of multiple image,s it is however not save to concurrently call this method on the same histogram.

To create a histogram for one image you can also use GenHistogram.

func (*Histogram) EntrySum

func (h *Histogram) EntrySum() float64

EntrySum returns the sum of all entries in the histogram.

func (*Histogram) Equals

func (h *Histogram) Equals(other *Histogram, epsilon float64) bool

Equals checks if two histograms are equal. epsilon is the difference between that is allowed to still consider them equal.

func (*Histogram) Normalize

func (h *Histogram) Normalize(pixels int) *Histogram

Normalize computes the normalized histogram of h if h contains the number of occurrences in the image. pixels is the total number of pixels in the original image the historam was created for. If pixels is a negative number or 0 the number of pixels will be computed as the sum of all entries in the original histogram. If no pixels exist in the image all result entries are set to 0.

func (*Histogram) PrintInfo

func (h *Histogram) PrintInfo(verbose bool)

PrintInfo prints information about the histogram to the standard output. If verbose is true it prints a formatted table of all frequencies, otherwise it prints the shorter tuple representation.

func (*Histogram) String

func (h *Histogram) String() string

String returns a tuple representation of the histogram.

type HistogramFSController

type HistogramFSController struct {
	Entries []HistogramFSEntry
	K       uint
	Version string
}

HistogramFSController is used to store histograms (wrapped by HistogramFSEntry) on the filesystem.

Its intended use is to write an instance to a file (or whatever), making it possible to safe histograms connected to a named image (path).

The idea is: Load histogram data from the filesystem and transform the histograms to HistogramStorage, maybe perform some tests if all images in the database are present in the controller.

It also has a version field that is set to the Version variable when saving. This can be useful if the definition should ever change.

See MissingEntries, AddtionalEntries and MemHistStorageFromFSMapper for some examples.

func CreateHistFSController

func CreateHistFSController(ids []ImageID, mapper *FSMapper, storage HistogramStorage) (*HistogramFSController, error)

CreateHistFSController creates a histogram filesystem controller given some input data. ids is the list of all image ids to be included in the controler, mapper is used to get the absolute path of an image (stored alongside the histogram data) and the storage is used to lookup the histograms.

If you want to create a fs controller with all ids from a storage you can use IDList to create a list of all ids.

func NewHistogramFSController

func NewHistogramFSController(capacity int, k uint) *HistogramFSController

NewHistogramFSController creates an empty file system controller with the given capacity.

To create a new file system controller initialized with some content use CreateHistFSController.

func (*HistogramFSController) AddtionalEntries

func (c *HistogramFSController) AddtionalEntries(m *FSMapper) []string

AddtionalEntries computes all images files that are present in the histogram controller but not in the mapper. Usually that means that the image has been deleted and is no longer required.

func (*HistogramFSController) CheckData

func (c *HistogramFSController) CheckData(k uint, checkK bool, checkNormalized bool) error

CheckData is used to verify (parts) of the controller data. It tests if the controller is defined for the same k as the argument k (tested only if checK is true). If you don't want to check k just set checK to false and k to some arbitrary value. It also checks if each histogram in the controler is defined for the same k (the k defined in the controller). If checkNormalized is set it also checks if each histogram only contains values between 0 and 1.

This method should not be used in production code because it's rather slow, but it's useful for debugging.

If the returned error is nil the check passed, otherwise an error != nil is returned describing all failed tests.

Usually we deal with incorrectly stored files during mosaic generation: If there is an error with one of the histogram ojbects (wrong k) the metrics return an error. If somehow not-normalized histograms are stored the error is not detected, it should just lead to weird results.

func (*HistogramFSController) Map

func (c *HistogramFSController) Map() map[string]*Histogram

Map computes the mapping filename ↦ histogram. That is useful sometimes, especially when computing the diff between this and an FSMapper.

func (*HistogramFSController) MissingEntries

func (c *HistogramFSController) MissingEntries(m *FSMapper, histMap map[string]*Histogram) []string

MissingEntries computes the set of all images that are present in the mapping m but have no matching entry in the histogram.

That is: For these images new histograms must be computed. HistMap is the map as computed by the Map() function. It is an argument to avoid multiple compoutations of it if used more often. Just set it to nil and it will be computed with the map function.

func (*HistogramFSController) ReadFile

func (c *HistogramFSController) ReadFile(path string) error

ReadFile reads the content of the controller from the specified file. The read method depends on the file extension which must be either .json or .gob.

func (*HistogramFSController) ReadGobFile

func (c *HistogramFSController) ReadGobFile(path string) error

ReadGobFile reads the content of the controller from the specified file. The file must be encoded in gob.

func (*HistogramFSController) ReadJSONFile

func (c *HistogramFSController) ReadJSONFile(path string) error

ReadJSONFile reads the content of the controller from the specified file. The file must be encoded in json.

func (*HistogramFSController) Remove

func (c *HistogramFSController) Remove(paths []string)

Remove removes all entries from the controller whose path is in the paths element. Example usage: Use AddtionalEntries to compute histograms that are probably not required any more and then Remove them.

func (*HistogramFSController) WriteFile

func (c *HistogramFSController) WriteFile(path string) error

WriteFile writes the content of the controller to a file depending on the file extension hich must be either .json or .gob.

func (*HistogramFSController) WriteGobFile

func (c *HistogramFSController) WriteGobFile(path string) error

WriteGobFile writes the histograms to a file encoded gob format.

func (*HistogramFSController) WriteJSON

func (c *HistogramFSController) WriteJSON(path string) error

WriteJSON writes the histograms to a file encoded in json format.

type HistogramFSEntry

type HistogramFSEntry struct {
	Path      string
	Histogram *Histogram
	Checksum  string
}

HistogramFSEntry is used to store a histogram on the filesystem. It contains the path of the image the histogram was created for as well as the histogram data.

It also has a field checksum that is not used yet. Later it can be adjusted s.t. an histogram is stored together with the checksum (e.g. just plain md5 encoded with e.g. base64) of the image the histogram was created for. This way we can test if the content of an image has changed, and thus the histogram became invalid. At the moment we don't recognize if an image has changed.

This is however not supported at the moment. An empty string signals that no checksum was computed.

func NewHistogramFSEntry

func NewHistogramFSEntry(path string, histogram *Histogram, checksum string) HistogramFSEntry

NewHistogramFSEntry returns a new entry with the given content.

type HistogramImageMetric

type HistogramImageMetric struct {
	HistStorage HistogramStorage
	Metric      HistogramMetric
	TileData    [][]*Histogram
	K           uint
	NumRoutines int
}

HistogramImageMetric implements ImageMetric by keeping a histogram storage and computing histograms for a query image.

func NewHistogramImageMetric

func NewHistogramImageMetric(storage HistogramStorage, metric HistogramMetric, numRoutines int) *HistogramImageMetric

NewHistogramImageMetric returns a new histogram image metric given a metric function between to histograms and the histogram storage to back the image metric. NumRoutines is the number of things that run concurrently when initializing the tile histograms.

func (*HistogramImageMetric) Compare

func (m *HistogramImageMetric) Compare(storage ImageStorage, image ImageID, tileY, tileX int) (float64, error)

Compare compares a database image and a query image based on the histogram metric function.

func (*HistogramImageMetric) InitStorage

func (m *HistogramImageMetric) InitStorage(storage ImageStorage) error

InitStorage does at the moment nothing.

func (*HistogramImageMetric) InitTiles

func (m *HistogramImageMetric) InitTiles(storage ImageStorage, query image.Image, dist TileDivision) error

InitTiles concurrently computes the histograms of the tiles of the query image.

type HistogramMetric

type HistogramMetric func(hA, hB *Histogram) float64

HistogramMetric is a function that compares images based on histograms. It has no other input than the histograms, especially it has no access to the image. More complicated ImageMetrics based on histogram should be defined by another type. A HistogramMetric can assume that both histograms are defined for the same k. The smaller the metric value is the more equal the images are considered.

Usually histogram metrics operate on normalized histograms. The metric value should be ≥ 0.

func GetHistogramMetric

func GetHistogramMetric(name string) (HistogramMetric, bool)

GetHistogramMetric returns a registered histogram metric. Returns the metric and true on success and nil and false otherwise. See RegisterHistogramMetric for details.

func HistogramVectorMetric

func HistogramVectorMetric(vm VectorMetric) HistogramMetric

HistogramVectorMetric converts a vector metric to a histogram metric.

type HistogramStorage

type HistogramStorage interface {
	// GetHistogram returns the histogram for a previously registered ImageID.
	GetHistogram(id ImageID) (*Histogram, error)

	// Divisions returns the number of sub-divisons in each direction (called k
	// in the histogram documentation). All histograms from this storage should
	// have this number of sub-divisions.
	Divisions() uint
}

HistogramStorage maps image ids to histograms. By default the histograms should be normalized.

Implementations must be safe for concurrent use.

type ImageCache

type ImageCache struct {
	// contains filtered or unexported fields
}

ImageCache is used to cache resized versions of images during mosaic generation. The same image with the same size might appear often in a mosaic (or the same area). This and the fact that resizing an image is not very fast makes it useful to cache the images.

Caches are safe for concurrent use.

func NewImageCache

func NewImageCache(size int) *ImageCache

NewImageCache returns an empty image cache. size is the number of images that will be cached. size must be ≥ 1.

func (*ImageCache) Get

func (cache *ImageCache) Get(id ImageID, width, height int) image.Image

Get returns the image from the cache. If the return value is nil the image was not found in the cache and should be added to the cache by Put.

func (*ImageCache) Put

func (cache *ImageCache) Put(id ImageID, width, height int, img image.Image)

Put adds an image to the cache. Usually Put is called after Get: If the image was not found in the cache it is scaled and then added to the cache via Put.

type ImageDivider

type ImageDivider interface {
	Divide(image.Rectangle) TileDivision
}

ImageDivider is a type to divide an image into tiles. That is it creates the areas which should be replaced by images from the database.

The returned distribution has to meet the following requirements:

(1) It returns a matrix of rectangles. That is the results contains rows and each row has the same length. So the element at (0, 0) describes the first rectangle in the image (top left corner).

(2) Rectangles might be of different size.

(3) The rectangle is not required to be a part of the image. In fact it must not even overlap with the image at some point, but usually it should.

(4) The result may be empty (or nil); rows may be empty.

(5) Images are stored in coordinates [y][x], that means each entry in the tile division describes a column.

type ImageHeap

type ImageHeap struct {
	// contains filtered or unexported fields
}

ImageHeap is a container that stores images sorted according to a float value.

func NewImageHeap

func NewImageHeap(bound int) *ImageHeap

NewImageHeap returns a new image heap with a given bound. If bound ≥ 0 it is used as the upper limit of entries stored in the heap. That is only the bound smallest images are stored.

func (*ImageHeap) Add

func (h *ImageHeap) Add(image ImageID, metricValue float64)

Add is a shortcut for AddEntry.

func (*ImageHeap) AddEntry

func (h *ImageHeap) AddEntry(entry ImageHeapEntry)

AddEntry adds a new entry to the heap, truncating the heap if bounds is ≥ 0.

func (*ImageHeap) GetView

func (h *ImageHeap) GetView() []ImageHeapEntry

GetView returns the sorted collection of entries in the heap, that is images with smallest values first. The length of the result slice is between 0 and bounds. The complexity is O(n * log(n)) where n is the size of the heap.

type ImageHeapEntry

type ImageHeapEntry struct {
	Image ImageID
	Value float64
}

ImageHeapEntry is an entry stored in an image heap. It consists of an image (by id) and the value of that image.

func NewImageHeapEntry

func NewImageHeapEntry(image ImageID, value float64) ImageHeapEntry

NewImageHeapEntry returns a new heap entry.

type ImageID

type ImageID int

ImageID is used to unambiguously identify an image.

const (
	// NoImageID is used to signal errors etc. on images.
	// It is usually never used and you don't have to care about ImageID < 0.
	// Certain functions however use this value in a specific way.
	NoImageID ImageID = -1
)

func IDList

func IDList(storage ImageStorage) []ImageID

IDList returns the list [0, 1, ..., storage.NumImages - 1].

type ImageMetric

type ImageMetric interface {
	InitStorage(storage ImageStorage) error
	InitTiles(storage ImageStorage, query image.Image, dist TileDivision) error
	Compare(storage ImageStorage, image ImageID, tileY, tileX int) (float64, error)
}

ImageMetric is used to compare a database image (image identified by an id) and a tile (previously registered) and return a metric value between the database image and the tile.

It is used in ImageMetricMinimizer which contains more information.

You might want to use InitTilesHelper to easily crate a concurrent InitTiles function.

An example implementation is given in HistogramImageMetric.

type ImageMetricMinimizer

type ImageMetricMinimizer struct {
	Metric      ImageMetric
	NumRoutines int
}

ImageMetricMinimizer implements ImageSelector and selects the image with the smallest distance to the tile.

It relies on a ImageMetric. The Init method simply calls the InitStorage method of the provided metric.

Each time images should be selected it calls InitTiles on the metric and selects the best images.

Thus the workflow is as follows: First the InitStorage method of the metric is called. Again, it is not the job of the metric to keep in sync with a filesystem / web resource whatever. Then for a query once InitTiles is called on the metric. In this step information about the query image are computed, for example computing GCHs of the query tiles. Then multiple calls to compare are made. To get the actual tiles from an image and a distribution use DivideImage.

A note for more sophisticated storage approaches: At the moment all metric storages (for example histogram storage) have all the data fastly available in memory. This makes it easy to access an histogram. Here we iterate for each tile and then over each database image. This might be bad if the histograms for the database images are not loaded in memory and need to be opened from a file or database. Caches won't work fine because we iterate all database images, process to the next tile and repeat that. Thus an alternative version should be implemented iterating over the database images and then over the tiles, making it easier to cache things. However this requires more communication that is not necessary at the moment and so this implementation works fine as long as all information is in memory.

The minimizer ignores metric errors in the way that whenever Compare returns an error != nil the candidate will be omitted. However a message will be logged in this case.

func GCHSelector

func GCHSelector(histStorage HistogramStorage, delta HistogramMetric, numRoutines int) *ImageMetricMinimizer

GCHSelector is an image selector that selects images that minimize the histogram metric function Δ. Formally it is an ImageMetricMinimizer and thus implements ImageSelector.

func LCHSelector

func LCHSelector(storage LCHStorage, scheme LCHScheme, metric HistogramMetric, numRoutines int) *ImageMetricMinimizer

LCHSelector is an image selector that selects images that minimize the LCH distance |Δ(h1[1], h2[1])| + ... + |Δ(h1[n], h2[n])| where Δ is a histogram metric. Formally it is an ImageMetricMinimizer and thus implements ImageSelector.

func NewImageMetricMinimizer

func NewImageMetricMinimizer(metric ImageMetric, numRoutines int) *ImageMetricMinimizer

NewImageMetricMinimizer returns a new metric minimizer given the metric to use and the number of go routines to run when selecting images.

func (*ImageMetricMinimizer) Init

func (min *ImageMetricMinimizer) Init(storage ImageStorage) error

Init just calls InitStorage of the metric.

func (*ImageMetricMinimizer) SelectImages

func (min *ImageMetricMinimizer) SelectImages(storage ImageStorage,
	query image.Image, dist TileDivision, progress ProgressFunc) ([][]ImageID, error)

SelectImages selects the image that minimizes the metric for each tile. It computes the most fitting image for NumRoutines tiles concurrently.

type ImageResizer

type ImageResizer interface {
	Resize(width, height uint, img image.Image) image.Image
}

ImageResizer resizes an image to the given width and height.

type ImageSelector

type ImageSelector interface {
	// Init is called each time the storage changes and at creation.
	// Note that in general a selector is not responsible for syncing histograms
	// with filesystem files etc. This should happen outside the storage.
	Init(storage ImageStorage) error

	// SelectImage is called after Init and returns the most fitting images for
	// the query. The returned images matrix must be of the same size as the
	// dist matrix.
	//
	// This step usually involves iterating over the precomputed data (for example
	// histograms for a database of images) and selecting the most fitting one.
	//
	// ImageMetricMinimizer is an example implementation.
	//
	// If no image can be selected (for example empty database) the id in the
	// result should be set to NoImageID.
	SelectImages(storage ImageStorage, query image.Image, dist TileDivision,
		progress ProgressFunc) ([][]ImageID, error)
}

ImageSelector is used to select images for all tiles. The workflow is as follows: The selector gets initialized by calling Init, then images are selected with SelectImages.

This is because we might want to use an selector multiple times, though no such approach is implemented at the moment.

type ImageStorage

type ImageStorage interface {
	// NumImages returns the number of images in the storage as an ImageID.
	// All ids < than NumImages are considered valid and can be retrieved via
	// LoadImage.
	NumImages() ImageID

	// LoadImage loads an image into memory.
	LoadImage(id ImageID) (image.Image, error)

	// LoadConfig loads the config of the image with the given id.
	LoadConfig(id ImageID) (image.Config, error)
}

ImageStorage is used to administrate a collection or database of images. Images are not stored in memory but are identified by an id and can be loaded into memory when required. A storage has a maximal id and can be used to access images with ids smaller than the the number of images. The access methods should return an error if the image id is not associated with any image data or if there is an error reading the image (e.g. from the filesystem).

Implementations must be safe for concurrent use.

type LCH

type LCH struct {
	Histograms []*Histogram
}

LCH is a sorted collection of global color histograms. Different schemes yield to a different number of LCHs, but for each image the same number of GCHs is computed.

All histograms should be generated with the same k (sub-divisons).

func CreateAllLCHs

func CreateAllLCHs(scheme LCHScheme, storage ImageStorage, normalize bool,
	k uint, numRoutines int, progress ProgressFunc) ([]*LCH, error)

CreateAllLCHs creates all lchs for images in the storage. It is a shortcut using CreateLCHs, see this documentation for details.

func CreateLCHs

func CreateLCHs(scheme LCHScheme, ids []ImageID, storage ImageStorage, normalize bool,
	k uint, numRoutines int, progress ProgressFunc) ([]*LCH, error)

CreateLCHs creates histograms for all images in the ids list and loads the images through the given storage. If you want to create all histograms for a given storage you can use CreateAllLCHs as a shortcut. It runs the creation of LCHs concurrently (how many go routines run concurrently can be controlled by numRoutines). k is the number of sub-divisons as described in the histogram type, If normalized is true the normalized histograms are computed. progress is a function that is called to inform about the progress, see doucmentation for ProgressFunc.

func GenLCH

func GenLCH(scheme LCHScheme, img image.Image, k uint, normalize bool) (*LCH, error)

GenLCH computes the LCHs an image. It uses the scheme to compute the image parts and then concurrently creates the GCHs for each list. k and normalize are defined as for the GCH method: k is the number of histogram sub-divisions and if normalize is true the GCHs are normalized.

func NewLCH

func NewLCH(histograms []*Histogram) *LCH

NewLCH creates a new LCH givent the histograms.

func (*LCH) Dist

func (lch *LCH) Dist(other *LCH, delta HistogramMetric) (float64, error)

Dist returns the distance between two LCHs parameterized by a HistogramMetric two compare the histograms. It returns |Δ(h1[1], h2[1])| + ... + |Δ(h1[n], h2[n])| if n is the number of GCHs of the LCH.

If the LCHs are of different dimensions or the GCHs inside the LCHs are of different dimensions an error != nil is returned.

type LCHFSController

type LCHFSController struct {
	Entries []LCHFSEntry
	K       uint
	Size    uint
	Version string
}

LCHFSController is used to store LCHs (wrapped by LCHFSEntry) on the filesystem.

It's the same idea as with HistogramFSController, see details there. Some of the functions implemented for HistogramFSController are not implemented here because they're not needed at the moment. But they could be implemented similar to those in HistogramFSController.

func CreateLCHFSController

func CreateLCHFSController(ids []ImageID, mapper *FSMapper, storage LCHStorage) (*LCHFSController, error)

CreateLCHFSController creates a histogram filesystem controller given some input data. ids is the list of all image ids to be included in the controler, mapper is used to get the absolute path of an image (stored alongside the LCH data) and the storage is used to lookup the LCHs.

If you want to create a fs controller with all ids from a storage you can use IDList to create a list of all ids.

func NewLCHFSController

func NewLCHFSController(k, schemeSize uint, capacity int) *LCHFSController

NewLCHFSController returns an empty file system controller with the given capacity. Too create a new file system controller initialized with some content use CreateLCHFSController.

func (*LCHFSController) Map

func (c *LCHFSController) Map() map[string]*LCH

Map computes the mapping filename ↦ lch. That is useful sometimes, especially when computing the diff between this and an FSMapper.

func (*LCHFSController) ReadFile

func (c *LCHFSController) ReadFile(path string) error

ReadFile reads the content of the controller from the specified file. The read method depends on the file extension which must be either .json or .gob.

func (*LCHFSController) ReadGobFile

func (c *LCHFSController) ReadGobFile(path string) error

ReadGobFile reads the content of the controller from the specified file. The file must be encoded in gob.

func (*LCHFSController) ReadJSONFile

func (c *LCHFSController) ReadJSONFile(path string) error

ReadJSONFile reads the content of the controller from the specified file. The file must be encoded in json.

func (*LCHFSController) WriteFile

func (c *LCHFSController) WriteFile(path string) error

WriteFile writes the content of the controller to a file depending on the file extension hich must be either .json or .gob.

func (*LCHFSController) WriteGobFile

func (c *LCHFSController) WriteGobFile(path string) error

WriteGobFile writes the LCH to a file encoded gob format.

func (*LCHFSController) WriteJSON

func (c *LCHFSController) WriteJSON(path string) error

WriteJSON writes the LCHs to a file encoded in json format.

type LCHFSEntry

type LCHFSEntry struct {
	Path     string
	LCH      *LCH
	Checksum string
}

LCHFSEntry is used to store LCHs on the filesystem. It contains the path of the image the LCH was created for as well as the LCH data.

It also has a field checksum that is not used yet. Later it can be adjusted s.t. an histgram is stored together with the checksum (e.g. just plain md5 encoded with e.g. base64) of the image the histogram was created for. This way we can test if the content of an image has changed, and thus the histogram became invalid. At the moment we don't recognize if an image has changed.

This is however not supported at the moment. An empty string signals that no checksum was computed.

func NewLCHFSEntry

func NewLCHFSEntry(path string, lch *LCH, checksum string) LCHFSEntry

NewLCHFSEntry returns a new entry with the given content.

type LCHImageMetric

type LCHImageMetric struct {
	LCHStorage LCHStorage
	Scheme     LCHScheme
	Metric     HistogramMetric
	TileData   [][]*LCH
	// we don't really have to save it, but it won't hurt
	// better than calling storage.Divisions again and again
	K           uint
	NumRoutines int
}

LCHImageMetric implements ImageMetric by building the LCH sum, that is |Δ(h1[1], h2[1])| + ... + |Δ(h1[n], h2[n])| where Δ is a histogram metric.

func NewLCHImageMetric

func NewLCHImageMetric(storage LCHStorage, scheme LCHScheme, metric HistogramMetric, numRoutines int) *LCHImageMetric

NewLCHImageMetric returns a new LCH based metric.

func (*LCHImageMetric) Compare

func (m *LCHImageMetric) Compare(storage ImageStorage, image ImageID, tileY, tileX int) (float64, error)

Compare compares a database image and a query image based on the histogram metric function.

func (LCHImageMetric) InitStorage

func (m LCHImageMetric) InitStorage(storage ImageStorage) error

InitStorage does nothing.

func (*LCHImageMetric) InitTiles

func (m *LCHImageMetric) InitTiles(storage ImageStorage, query image.Image, dist TileDivision) error

InitTiles concurrently computes the LCHs of the tiles of the query image.

type LCHScheme

type LCHScheme interface {
	GetParts(img image.Image) ([][]image.Image, error)
}

LCHScheme returns the distribution of an image into sub images. Note that a sub image can be contained in multiple lists and not all lists must be of the same length. For example the four parts scheme: The first list could contain both top sub images. The western list would contain the bot left sub images. They both contain the to-left image.

Schemes always return a fixed number of image lists.

type LCHStorage

type LCHStorage interface {
	// GetLCH returns the LCH for a previously registered ImageID.
	GetLCH(id ImageID) (*LCH, error)

	// Divisions returns the number of sub-divisions used in the gchs of an LCH.
	Divisions() uint

	// SchemeSize returns the number of gchs stored for each lch.
	SchemeSize() uint
}

LCHStorage maps image ids to LCHs. By default the histograms of the LCHs should be normalized.

Implementations must be safe for concurrent use.

type MemoryHistStorage

type MemoryHistStorage struct {
	Histograms []*Histogram
	K          uint
}

MemoryHistStorage implements HistogramStorage by keeping a list of histograms in memory.

func MemHistStorageFromFSMapper

func MemHistStorageFromFSMapper(mapper *FSMapper, fileContent *HistogramFSController,
	histMap map[string]*Histogram) (*MemoryHistStorage, error)

MemHistStorageFromFSMapper creates a new memory histogram storage that contains an entry for each image described by the filesystem mapper. If no histogram for an image is found an error is returned. An error is also returned if there is an invalid histogram (wrong k, wrong size of entries).

HistMap is the map as computed by the Map() function of the histogram controller. It is an argument to avoid multiple compoutations of it if used more often. Just set it to nil and it will be computed with the map function.

func NewMemoryHistStorage

func NewMemoryHistStorage(k uint, capacity int) *MemoryHistStorage

NewMemoryHistStorage returns a new memory histogram storage storing histograms with k sub-divisons. Capacity is the capacity of the underlying histogram array, negative values yield to a default capacity.

func (*MemoryHistStorage) Divisions

func (s *MemoryHistStorage) Divisions() uint

Divisions returns the number of sub-divisions k.

func (*MemoryHistStorage) GetHistogram

func (s *MemoryHistStorage) GetHistogram(id ImageID) (*Histogram, error)

GetHistogram implements the HistogramStorage interface function by returning the histogram on position id in the list. If id is not a valid position inside the the list an error is returned.

type MemoryLCHStorage

type MemoryLCHStorage struct {
	LCHs []*LCH
	K    uint
	Size uint
}

MemoryLCHStorage implements LCHStorage by keeping a list of LCHs in memory.

func MemLCHStorageFromFSMapper

func MemLCHStorageFromFSMapper(mapper *FSMapper, fileContent *LCHFSController,
	lchMap map[string]*LCH) (*MemoryLCHStorage, error)

MemLCHStorageFromFSMapper creates a new memory LCH storage that contains an entry MemLCHStorageFromFSMapper each image described by the filesystem mapper. If no lch for an image is found an error is returned.

HistMap is the map as computed by the Map() function of the LCH controller. It is an argument to avoid multiple compoutations of it if used more often. Just set it to nil and it will be computed with the map function.

func NewMemoryLCHStorage

func NewMemoryLCHStorage(k, schemeSize uint, capacity int) *MemoryLCHStorage

NewMemoryLCHStorage returns a new memory LCH storage storing LCHs of size schemeSize with k sub-divisions. Capacity is the capacity of the underlying histogram array, negative values yield to a default capacity.

func (*MemoryLCHStorage) Divisions

func (s *MemoryLCHStorage) Divisions() uint

Divisions returns the number of sub-divisions k.

func (*MemoryLCHStorage) GetLCH

func (s *MemoryLCHStorage) GetLCH(id ImageID) (*LCH, error)

GetLCH implements the LCHStorage interface function by returning the LCH on position id in the list. If id is not a valid position inside the the list an error is returned.

func (*MemoryLCHStorage) SchemeSize

func (s *MemoryLCHStorage) SchemeSize() uint

SchemeSize returns the number of GCHs stored for each LCH in the storage.

type NfntResizer

type NfntResizer struct {
	// InterP is the interpolation function to use.
	InterP resize.InterpolationFunction
}

NfntResizer uses the nfnt/resize package to resize an image.

func NewNfntResizer

func NewNfntResizer(interP resize.InterpolationFunction) NfntResizer

NewNfntResizer returns a new resizer given the interpolation function.

func (NfntResizer) Resize

func (resizer NfntResizer) Resize(width, height uint, img image.Image) image.Image

Resize calls nfnt/resize methods.

type ProgressFunc

type ProgressFunc func(num int)

ProgressFunc is a function that is used to inform a caller about the progress of a called function. For example if we process thousands of images we might wish to know how far the call is and give feedback to the user. The called method calls the process function after each iteration.

func LoggerProgressFunc

func LoggerProgressFunc(prefix string, max, step int) ProgressFunc

LoggerProgressFunc is a parameterized ProgressFunc that logs to log. The output describes the progress (how many of how many objects processed). Log messages may have an addition prefix. max is the total number of elements to process and step describes how often to print to the log (for example step = 100 every 100 items).

func StdProgressFunc

func StdProgressFunc(w io.Writer, prefix string, max, step int) ProgressFunc

StdProgressFunc is a parameterized ProgressFunc that logs to the specified writer. The output describes the progress (how many of how many objects processed). Log messages may have an addition prefix. max is the total number of elements to process and step describes how often to print to the log (for example step = 100 every 100 items).

type RGB

type RGB struct {
	R, G, B uint8
}

RGB is a color containing r, g and b components.

func ConvertRGB

func ConvertRGB(c color.Color) RGB

ConvertRGB converts a generic color into the internal RGB representation.

func NewRGB

func NewRGB(r, g, b uint8) RGB

NewRGB returns a new RGB color.

func (RGB) ID

func (c RGB) ID(k uint) uint

ID assigns each RGB color (k sub-divisions) a unique id. That id is given by r + k * g + k² * b.

func (RGB) Quantize

func (c RGB) Quantize(k uint) RGB

Quantize quantizes the RGB color (k sub-divisions in each direction). k must be a number between 1 and 256.

type RandomHeapSelector

type RandomHeapSelector struct {
	// contains filtered or unexported fields
}

RandomHeapSelector implements HeapSelector by using just a random element from each heap.

Note that instances of this selector are not safe for concurrent use.

func NewRandomHeapSelector

func NewRandomHeapSelector(randGen *rand.Rand) *RandomHeapSelector

NewRandomHeapSelector returns a new random selector. The provided random generator is used to generate random numbers. You can use nil and a random generator will be created.

Note that rand.Rand instances are not safe for concurrent use. Thus using the same generator on two instances that run concurrently is not allowed.

func (*RandomHeapSelector) Select

func (sel *RandomHeapSelector) Select(storage ImageStorage, query image.Image, dist TileDivision, heaps [][]*ImageHeap) ([][]ImageID, error)

Select implements the HeapSelector interface, it selects the random images.

type ReplHandler

type ReplHandler struct{}

ReplHandler implements CommandHandler by reading commands from stdin and writing output to stdout.

func (ReplHandler) After

func (h ReplHandler) After(s *ExecutorState)

func (ReplHandler) Before

func (h ReplHandler) Before(s *ExecutorState)

func (ReplHandler) Init

func (h ReplHandler) Init() *ExecutorState

Init creates an initial ExecutorState. It creates a new mapper and image database and sets the working directory to the current directory. This method might panic if something with filepath is wrong, this should however usually not be the case.

func (ReplHandler) OnError

func (h ReplHandler) OnError(s *ExecutorState, err error, cmd Command) bool

func (ReplHandler) OnInvalidCmd

func (h ReplHandler) OnInvalidCmd(s *ExecutorState, cmd string) bool

func (ReplHandler) OnParseErr

func (h ReplHandler) OnParseErr(s *ExecutorState, err error) bool

func (ReplHandler) OnScanErr

func (h ReplHandler) OnScanErr(s *ExecutorState, err error)

func (ReplHandler) OnSuccess

func (h ReplHandler) OnSuccess(s *ExecutorState, cmd Command)

func (ReplHandler) Start

func (h ReplHandler) Start(s *ExecutorState)

type ResizeStrategy

type ResizeStrategy func(resizer ImageResizer, tileWidth, tileHeight uint, img image.Image) image.Image

ResizeStrategy is a function that scales an image (img) to an image of exyctly the size defined by tileWidth and tileHeight. This is used to compose the mosaic when the selected database images must be resized to fit in the tiles.

The difference between ResizeStrategy and ImageResizer is that we think of an ImageResizer as an "engine", for example a libarary, that performs the of scaling an image exactly to a specific width and height. A ResizeStrategy might first resize an image to some other size and then return a subimage. That is we think of a resizer as something that does the work and a ResizeStrategy as something that decides how to nicely scale an image s.t. it fits nicely.

type ScriptHandler

type ScriptHandler struct {
	Source io.Reader
}

ScriptHandler implements CommandHandler. It writes the output to stdout and reads from a specified reader. It stops whenever an error is enountered.

func NewScriptHandler

func NewScriptHandler(source io.Reader) ScriptHandler

NewScriptHandler returns a new script handler that reads input from the given source.

func ScriptHandlerFromCmds

func ScriptHandlerFromCmds(lines []string) ScriptHandler

ScriptHandlerFromCmds is a function to create a script handler from a predefined set of lines. This allows us for easy execution of predefined scripts.

func (ScriptHandler) After

func (h ScriptHandler) After(s *ExecutorState)

func (ScriptHandler) Before

func (h ScriptHandler) Before(s *ExecutorState)

func (ScriptHandler) Init

func (h ScriptHandler) Init() *ExecutorState

Init creates an initial ExecutorState. It creates a new mapper and image database and sets the working directory to the current directory. This method might panic if something with filepath is wrong, this should however usually not be the case.

func (ScriptHandler) OnError

func (h ScriptHandler) OnError(s *ExecutorState, err error, cmd Command) bool

func (ScriptHandler) OnInvalidCmd

func (h ScriptHandler) OnInvalidCmd(s *ExecutorState, cmd string) bool

func (ScriptHandler) OnParseErr

func (h ScriptHandler) OnParseErr(s *ExecutorState, err error) bool

func (ScriptHandler) OnScanErr

func (h ScriptHandler) OnScanErr(s *ExecutorState, err error)

func (ScriptHandler) OnSuccess

func (h ScriptHandler) OnSuccess(s *ExecutorState, cmd Command)

func (ScriptHandler) Start

func (h ScriptHandler) Start(s *ExecutorState)

type SubImager

type SubImager interface {
	SubImage(r image.Rectangle) image.Image
}

SubImager is a type that can produce a sub image from an original image.

type SupportedImageFunc

type SupportedImageFunc func(ext string) bool

SupportedImageFunc is a function that takes a file extension and decides if this file extension is supported. Usually our library should support jpg and png files, but this may change depending on what image protocols are loaded.

The extension passed to this function could be for example ".txt" or ".jpg". JPGAndPNG is an implementation accepting jpg and png files.

type TileDivision

type TileDivision [][]image.Rectangle

TileDivision represents the divison of an image into rectangles. See ImageDivider for details about divisions.

Divisons are stored column wise, that is each entry in division is a column. division[0] is the first column etc.

func RepairDistribution

func RepairDistribution(distribution TileDivision, numX, numY int) TileDivision

RepairDistribution is used to ensure that distribution contains a matrix of numY rows and in each row numX columns. Usually this method does not do anything (and hopefully never will). But just to be sure we add it here. It will never decrease the number of rectangles, only increase if required.

This function is usally only triggered in debug mode.

func (TileDivision) Size

func (div TileDivision) Size() int

Size returns the total number of rectangles.

type Tiles

type Tiles [][]image.Image

Tiles are the tiles of an image. They're genrated from a TileDivision and the image matrix is of the same size as the TileDivision.

Tiles are stored column wise, that is each entry in tiles is a column. tiles[0] is the first column etc.

func DivideImage

func DivideImage(img image.Image, distribution TileDivision, numRoutines int) (Tiles, error)

DivideImage computes the actual tiles from an image and the distribution into tile rectangles. The returned images should all be part of the image, thus must not have the same size as suggested by the distribution.

func (Tiles) Size

func (tiles Tiles) Size() int

Size returns the total number of tiles.

type VectorMetric

type VectorMetric func(p, q []float64) float64

VectorMetric is a function that takes two vectors of the same length and returns a metric value ("distance") of the two.

Vector metrics therefor can be used for comparing histograms.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL