aboutsummaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'main.go')
-rw-r--r--main.go412
1 files changed, 206 insertions, 206 deletions
diff --git a/main.go b/main.go
index 29b08d3..53c7e22 100644
--- a/main.go
+++ b/main.go
@@ -453,212 +453,6 @@ func handleAPITags(w http.ResponseWriter, r *http.Request) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-func getImageDimensions(sha1 string) (w int64, h int64, err error) {
- err = db.QueryRow(`SELECT width, height FROM image WHERE sha1 = ?`,
- sha1).Scan(&w, &h)
- return
-}
-
-func getImagePaths(sha1 string) (paths []string, err error) {
- rows, err := db.Query(`WITH RECURSIVE paths(parent, path) AS (
- SELECT parent, name AS path FROM node WHERE sha1 = ?
- UNION ALL
- SELECT n.parent, n.name || '/' || p.path
- FROM node AS n JOIN paths AS p ON n.id = p.parent
- ) SELECT path FROM paths WHERE parent IS NULL`, sha1)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- paths = []string{}
- for rows.Next() {
- var path string
- if err := rows.Scan(&path); err != nil {
- return nil, err
- }
- paths = append(paths, path)
- }
- return paths, rows.Err()
-}
-
-func getImageTags(sha1 string) (map[string]map[string]float32, error) {
- rows, err := db.Query(`
- SELECT ts.name, t.name, ta.weight FROM tag_assignment AS ta
- JOIN tag AS t ON t.id = ta.tag
- JOIN tag_space AS ts ON ts.id = t.space
- WHERE ta.sha1 = ?`, sha1)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- result := make(map[string]map[string]float32)
- for rows.Next() {
- var (
- space, tag string
- weight float32
- )
- if err := rows.Scan(&space, &tag, &weight); err != nil {
- return nil, err
- }
-
- tags := result[space]
- if tags == nil {
- tags = make(map[string]float32)
- result[space] = tags
- }
- tags[tag] = weight
- }
- return result, rows.Err()
-}
-
-func handleAPIInfo(w http.ResponseWriter, r *http.Request) {
- var params struct {
- SHA1 string
- }
- if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
-
- var result struct {
- Width int64 `json:"width"`
- Height int64 `json:"height"`
- Paths []string `json:"paths"`
- Tags map[string]map[string]float32 `json:"tags"`
- }
-
- var err error
- result.Width, result.Height, err = getImageDimensions(params.SHA1)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- result.Paths, err = getImagePaths(params.SHA1)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- result.Tags, err = getImageTags(params.SHA1)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if err := json.NewEncoder(w).Encode(result); err != nil {
- log.Println(err)
- }
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-type webSimilarImage struct {
- SHA1 string `json:"sha1"`
- PixelsRatio float32 `json:"pixelsRatio"`
- ThumbW int64 `json:"thumbW"`
- ThumbH int64 `json:"thumbH"`
- Paths []string `json:"paths"`
-}
-
-func getSimilar(sha1 string, dhash int64, pixels int64, distance int) (
- result []webSimilarImage, err error) {
- // For distance ∈ {0, 1}, this query is quite inefficient.
- // In exchange, it's generic.
- //
- // If there's a dhash, there should also be thumbnail dimensions,
- // so not bothering with IFNULL on them.
- rows, err := db.Query(`
- SELECT sha1, width * height, IFNULL(thumbw, 0), IFNULL(thumbh, 0)
- FROM image WHERE sha1 <> ? AND dhash IS NOT NULL
- AND hamming(dhash, ?) = ?`, sha1, dhash, distance)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- result = []webSimilarImage{}
- for rows.Next() {
- var (
- match webSimilarImage
- matchPixels int64
- )
- if err = rows.Scan(&match.SHA1,
- &matchPixels, &match.ThumbW, &match.ThumbH); err != nil {
- return nil, err
- }
- if match.Paths, err = getImagePaths(match.SHA1); err != nil {
- return nil, err
- }
- match.PixelsRatio = float32(matchPixels) / float32(pixels)
- result = append(result, match)
- }
- return result, rows.Err()
-}
-
-func getSimilarGroups(sha1 string, dhash int64, pixels int64,
- output map[string][]webSimilarImage) error {
- var err error
- for distance := 0; distance <= 1; distance++ {
- output[fmt.Sprintf("Perceptual distance %d", distance)], err =
- getSimilar(sha1, dhash, pixels, distance)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func handleAPISimilar(w http.ResponseWriter, r *http.Request) {
- var params struct {
- SHA1 string
- }
- if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
- http.Error(w, err.Error(), http.StatusBadRequest)
- return
- }
-
- var result struct {
- Info webSimilarImage `json:"info"`
- Groups map[string][]webSimilarImage `json:"groups"`
- }
-
- result.Info = webSimilarImage{SHA1: params.SHA1, PixelsRatio: 1}
- if paths, err := getImagePaths(params.SHA1); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- } else {
- result.Info.Paths = paths
- }
-
- var (
- width, height int64
- dhash sql.NullInt64
- )
- err := db.QueryRow(`
- SELECT width, height, dhash, IFNULL(thumbw, 0), IFNULL(thumbh, 0)
- FROM image WHERE sha1 = ?`, params.SHA1).Scan(&width, &height, &dhash,
- &result.Info.ThumbW, &result.Info.ThumbH)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- result.Groups = make(map[string][]webSimilarImage)
- if dhash.Valid {
- if err := getSimilarGroups(
- params.SHA1, dhash.Int64, width*height, result.Groups); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- }
-
- if err := json.NewEncoder(w).Encode(result); err != nil {
- log.Println(err)
- }
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
type webDuplicateImage struct {
SHA1 string `json:"sha1"`
ThumbW int64 `json:"thumbW"`
@@ -899,6 +693,212 @@ func handleAPIOrphans(w http.ResponseWriter, r *http.Request) {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+func getImageDimensions(sha1 string) (w int64, h int64, err error) {
+ err = db.QueryRow(`SELECT width, height FROM image WHERE sha1 = ?`,
+ sha1).Scan(&w, &h)
+ return
+}
+
+func getImagePaths(sha1 string) (paths []string, err error) {
+ rows, err := db.Query(`WITH RECURSIVE paths(parent, path) AS (
+ SELECT parent, name AS path FROM node WHERE sha1 = ?
+ UNION ALL
+ SELECT n.parent, n.name || '/' || p.path
+ FROM node AS n JOIN paths AS p ON n.id = p.parent
+ ) SELECT path FROM paths WHERE parent IS NULL`, sha1)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ paths = []string{}
+ for rows.Next() {
+ var path string
+ if err := rows.Scan(&path); err != nil {
+ return nil, err
+ }
+ paths = append(paths, path)
+ }
+ return paths, rows.Err()
+}
+
+func getImageTags(sha1 string) (map[string]map[string]float32, error) {
+ rows, err := db.Query(`
+ SELECT ts.name, t.name, ta.weight FROM tag_assignment AS ta
+ JOIN tag AS t ON t.id = ta.tag
+ JOIN tag_space AS ts ON ts.id = t.space
+ WHERE ta.sha1 = ?`, sha1)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ result := make(map[string]map[string]float32)
+ for rows.Next() {
+ var (
+ space, tag string
+ weight float32
+ )
+ if err := rows.Scan(&space, &tag, &weight); err != nil {
+ return nil, err
+ }
+
+ tags := result[space]
+ if tags == nil {
+ tags = make(map[string]float32)
+ result[space] = tags
+ }
+ tags[tag] = weight
+ }
+ return result, rows.Err()
+}
+
+func handleAPIInfo(w http.ResponseWriter, r *http.Request) {
+ var params struct {
+ SHA1 string
+ }
+ if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ var result struct {
+ Width int64 `json:"width"`
+ Height int64 `json:"height"`
+ Paths []string `json:"paths"`
+ Tags map[string]map[string]float32 `json:"tags"`
+ }
+
+ var err error
+ result.Width, result.Height, err = getImageDimensions(params.SHA1)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ result.Paths, err = getImagePaths(params.SHA1)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ result.Tags, err = getImageTags(params.SHA1)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if err := json.NewEncoder(w).Encode(result); err != nil {
+ log.Println(err)
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+type webSimilarImage struct {
+ SHA1 string `json:"sha1"`
+ PixelsRatio float32 `json:"pixelsRatio"`
+ ThumbW int64 `json:"thumbW"`
+ ThumbH int64 `json:"thumbH"`
+ Paths []string `json:"paths"`
+}
+
+func getSimilar(sha1 string, dhash int64, pixels int64, distance int) (
+ result []webSimilarImage, err error) {
+ // For distance ∈ {0, 1}, this query is quite inefficient.
+ // In exchange, it's generic.
+ //
+ // If there's a dhash, there should also be thumbnail dimensions,
+ // so not bothering with IFNULL on them.
+ rows, err := db.Query(`
+ SELECT sha1, width * height, IFNULL(thumbw, 0), IFNULL(thumbh, 0)
+ FROM image WHERE sha1 <> ? AND dhash IS NOT NULL
+ AND hamming(dhash, ?) = ?`, sha1, dhash, distance)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ result = []webSimilarImage{}
+ for rows.Next() {
+ var (
+ match webSimilarImage
+ matchPixels int64
+ )
+ if err = rows.Scan(&match.SHA1,
+ &matchPixels, &match.ThumbW, &match.ThumbH); err != nil {
+ return nil, err
+ }
+ if match.Paths, err = getImagePaths(match.SHA1); err != nil {
+ return nil, err
+ }
+ match.PixelsRatio = float32(matchPixels) / float32(pixels)
+ result = append(result, match)
+ }
+ return result, rows.Err()
+}
+
+func getSimilarGroups(sha1 string, dhash int64, pixels int64,
+ output map[string][]webSimilarImage) error {
+ var err error
+ for distance := 0; distance <= 1; distance++ {
+ output[fmt.Sprintf("Perceptual distance %d", distance)], err =
+ getSimilar(sha1, dhash, pixels, distance)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func handleAPISimilar(w http.ResponseWriter, r *http.Request) {
+ var params struct {
+ SHA1 string
+ }
+ if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ var result struct {
+ Info webSimilarImage `json:"info"`
+ Groups map[string][]webSimilarImage `json:"groups"`
+ }
+
+ result.Info = webSimilarImage{SHA1: params.SHA1, PixelsRatio: 1}
+ if paths, err := getImagePaths(params.SHA1); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ } else {
+ result.Info.Paths = paths
+ }
+
+ var (
+ width, height int64
+ dhash sql.NullInt64
+ )
+ err := db.QueryRow(`
+ SELECT width, height, dhash, IFNULL(thumbw, 0), IFNULL(thumbh, 0)
+ FROM image WHERE sha1 = ?`, params.SHA1).Scan(&width, &height, &dhash,
+ &result.Info.ThumbW, &result.Info.ThumbH)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ result.Groups = make(map[string][]webSimilarImage)
+ if dhash.Valid {
+ if err := getSimilarGroups(
+ params.SHA1, dhash.Int64, width*height, result.Groups); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ if err := json.NewEncoder(w).Encode(result); err != nil {
+ log.Println(err)
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
// cmdWeb runs a web UI against GD on ADDRESS.
func cmdWeb(args []string) error {
if len(args) != 2 {