Görüntüleri Tweet'lere Kodlayın (Extreme Image Compression Edition) [kapalı]


59

Stack Overflow'taki çok başarılı Twitter görüntü kodlama mücadelesine dayanarak .

Bir resim 1000 kelimeye bedelse, 114,97 bayta ne kadar resim sığdırabilirim?

Görüntüleri yalnızca yazdırılabilir ASCII metni içeren standart bir Twitter yorumuna sıkıştırmak için genel amaçlı bir yöntem bulmanız konusunda size meydan okuyorum .

Kurallar:

  1. Görüntü alabilen ve kodlanmış metni çıkaran bir program yazmalısınız.
  2. Program tarafından oluşturulan metin en çok 140 karakter uzunluğunda olmalı ve yalnızca kod noktaları 32-126 aralığında olan karakterleri içermelidir.
  3. Kodlanmış metni alan ve fotoğrafın kodu çözülmüş bir sürümünü çıkaran bir program (muhtemelen aynı program) yazmalısınız.
  4. Programınız harici kütüphaneleri ve dosyaları kullanabilir, ancak internet bağlantısı veya diğer bilgisayarlara bağlantı gerektiremez.
  5. Kod çözme işlemi orijinal görüntülere hiçbir şekilde erişemez veya içeremez.
  6. Programınız aşağıdaki biçimlerden en az birinde görüntüleri kabul etmelidir (zorunlu olarak daha fazla olmamak kaydıyla): Bitmap, JPEG, GIF, TIFF, PNG. Örnek görüntülerin bazıları veya tümü doğru biçimde değilse, programınız tarafından sıkıştırılmadan önce bunları kendiniz dönüştürebilirsiniz.

bakılırsa:

Bu biraz öznel bir sorundur, bu yüzden kazanan (sonunda) benim tarafımdan değerlendirilecektir. Kararımı, azalan önemde aşağıda listelenen birkaç önemli faktöre odaklayacağım:

  1. Örnek bir resim olarak listelenmeyenler de dahil olmak üzere çok çeşitli görüntüleri sıkıştırarak makul bir iş yapabilme
  2. Görüntüdeki ana unsurların ana hatlarını koruyabilme
  3. Görüntüdeki ana öğelerin renklerini sıkıştırabilme
  4. Görüntüdeki küçük ayrıntıların ana hatlarını ve renklerini koruma yeteneği
  5. Sıkıştırma zamanı Bir görüntünün ne kadar iyi sıkıştırıldığı kadar önemli olmasa da, daha hızlı programlar aynı şeyi yapan yavaş programlardan daha iyidir.

Gönderiminiz, oluşturulan Twitter yorumu ile birlikte, sıkıştırmadan sonra ortaya çıkan görüntüleri içermelidir. Mümkünse, kaynak koduna bir link de verebilirsiniz.

Örnek görüntüler:

Hindenburg , Dağlık Manzara , Mona Lisa , 2D Şekiller


U + 007F (127) ve U + 0080 (128) kontrol karakterleridir. Bunları da yasaklamayı öneririm.
Lütfen

İyi gözlem. Bunu düzelteceğim.
PhiNotPi

Twitter bir dereceye kadar Unicode'a izin vermiyor mu?
marinus

4
Bunun için bir çözüm patenti almak isteyeceğimi hissediyorum.
Shmiddty

2
"Dağlık Manzaralar" 1024x768 - Gitmeden önce alın! -> i.imgur.com/VaCzpRL.jpg <-
jdstankosky

Yanıtlar:


58

Gerçek sıkıştırma ekleyerek yöntemimi geliştirdim. Şimdi tekrarlayarak aşağıdakileri yaparak çalışır:

  1. Resmi YUV'ye dönüştür
  2. En boy oranını koruyarak görüntüyü küçültün (görüntü renkliyse, renk parlaklığının 1 / 3'ü kadar renklenir)

  3. Bit derinliğini örnek başına 4 bit'e düşürün

  4. Görüntü dağılımına medyan tahmin uygulayarak, örnek dağılımını daha düzgün hale getirin

  5. Görüntüye adaptif aralık sıkıştırma uygulayın.

  6. Sıkıştırılmış görüntünün boyutunun <= 112 olup olmadığını görün

112 bayta uyan en büyük görüntü, son görüntü olarak kullanılır, kalan iki bayt, sıkıştırılmış görüntünün genişliğini ve yüksekliğini ve ayrıca görüntünün renkli olup olmadığını belirten bir bayrağı saklamak için kullanılır. Kod çözme için işlem tersine çevrilir ve görüntü daha küçük boyut 128 olacak şekilde ölçeklenir.

İyileştirme için bir oda var, yani tüm kullanılabilir baytlar tipik olarak kullanılmıyor, ancak aşağı örnekleme + kayıpsız sıkıştırma için önemli ölçüde azalan dönüşler noktasında olduğumu düşünüyorum.

Hızlı ve kirli C ++ kaynağı

Windows exe

Mona Lisa (13x20 parlaklık, 4x6 kroma)

&Jhmi8(,x6})Y"f!JC1jTzRh}$A7ca%/B~jZ?[_I17+91j;0q';|58yvX}YN426@"97W8qob?VB'_Ps`x%VR=H&3h8K=],4Bp=$K=#"v{thTV8^~lm vMVnTYT3rw N%I           

Mona Lisa Mona Lisa Twitter şifreli

Hindenburg (21x13 parlaklık)

GmL<B&ep^m40dPs%V[4&"~F[Yt-sNceB6L>Cs#/bv`\4{TB_P Rr7Pjdk7}<*<{2=gssBkR$>!['ROG6Xs{AEtnP=OWDP6&h{^l+LbLr4%R{15Zc<D?J6<'#E.(W*?"d9wdJ'       

Hindenburg Hindenburg twitter kodlu

Dağlar (19x14 parlaklık, 6x4 kroma)

Y\Twg]~KC((s_P>,*cePOTM_X7ZNMHhI,WeN(m>"dVT{+cXc?8n,&m$TUT&g9%fXjy"A-fvc 3Y#Yl-P![lk~;.uX?a,pcU(7j?=HW2%i6fo@Po DtT't'(a@b;sC7"/J           

Dağ Dağ twitter kodlu

2D Şekiller (21x15 parlaklık, 7x5 kroma)

n@|~c[#w<Fv8mD}2LL!g_(~CO&MG+u><-jT#{KXJy/``#S@m26CQ=[zejo,gFk0}A%i4kE]N ?R~^8!Ki*KM52u,M(his+BxqDCgU>ul*N9tNb\lfg}}n@HhX77S@TZf{k<CO69!    

2D Şekiller 2D Şekiller twitter kodlanmış


7
Bu beni katarakt veya başka bir şey geliştiriyormuş gibi hissettiriyor. Haha, iyi iş!
jdstankosky

Güzel iyileştirmeler!
jdstankosky

37

Git

Görüntüyü tekrarlı olarak bölgelere bölerek çalışır. Yüksek bilgi içeriğine sahip bölgeleri bölmeye çalışıyorum ve iki bölge arasındaki renk farkını maksimize etmek için bölme çizgisini seçiyorum.

Her bölüm, ayırma çizgisini kodlamak için birkaç bit kullanılarak kodlanır. Her yaprak bölgesi tek bir renk olarak kodlanır.

görüntü tanımını buraya girin

4vN!IF$+fP0~\}:0d4a's%-~@[Q(qSd<<BDb}_s|qb&8Ys$U]t0mc]|! -FZO=PU=ln}TYLgh;{/"A6BIER|{lH1?ZW1VNwNL 6bOBFOm~P_pvhV)]&[p%GjJ ,+&!p"H4`Yae@:P

görüntü tanımını buraya girin

<uc}+jrsxi!_:GXM!'w5J)6]N)y5jy'9xBm8.A9LD/^]+t5#L-6?9 a=/f+-S*SZ^Ch07~s)P("(DAc+$[m-:^B{rQTa:/3`5Jy}AvH2p!4gYR>^sz*'U9(p.%Id9wf2Lc+u\&\5M>

görüntü tanımını buraya girin

lO6>v7z87n;XsmOW^3I-0'.M@J@CLL[4z-Xr:! VBjAT,##6[iSE.7+as8C.,7uleb=|y<t7sm$2z)k&dADF#uHXaZCLnhvLb.%+b(OyO$-2GuG~,y4NTWa=/LI3Q4w7%+Bm:!kpe&

görüntü tanımını buraya girin

ZoIMHa;v!]&j}wr@MGlX~F=(I[cs[N^M`=G=Avr*Z&Aq4V!c6>!m@~lJU:;cr"Xw!$OlzXD$Xi>_|*3t@qV?VR*It4gB;%>,e9W\1MeXy"wsA-V|rs$G4hY!G:%v?$uh-y~'Ltd.,(

Hindenburg resmi oldukça berbat görünüyor, ancak diğerleri hoşuma gidiyor.

package main

import (
    "os"
    "image"
    "image/color"
    "image/png"
    _ "image/jpeg"
    "math"
    "math/big"
)

// we have 919 bits to play with: floor(log_2(95^140))

// encode_region(r):
//   0
//      color of region (12 bits, 4 bits each color)
// or
//   1
//      dividing line through region
//        2 bits - one of 4 anchor points
//        4 bits - one of 16 angles
//      encode_region(r1)
//      encode_region(r2)
//
// start with single region
// pick leaf region with most contrast, split it

type Region struct {
    points []image.Point
    anchor int  // 0-3
    angle int // 0-15
    children [2]*Region
}

// mean color of region
func (region *Region) meanColor(img image.Image) (float64, float64, float64) {
    red := 0.0
    green := 0.0
    blue := 0.0
    num := 0
    for _, p := range region.points {
        r, g, b, _ := img.At(p.X, p.Y).RGBA()
        red += float64(r)
        green += float64(g)
        blue += float64(b)
        num++
    }
    return red/float64(num), green/float64(num), blue/float64(num)
}

// total non-uniformity in region's color
func (region *Region) deviation(img image.Image) float64 {
    mr, mg, mb := region.meanColor(img)
    d := 0.0
    for _, p := range region.points {
        r, g, b, _ := img.At(p.X, p.Y).RGBA()
        fr, fg, fb := float64(r), float64(g), float64(b)
        d += (fr - mr) * (fr - mr) + (fg - mg) * (fg - mg) + (fb - mb) * (fb - mb)
    }
    return d
}

// centroid of region
func (region *Region) centroid() (float64, float64) {
    cx := 0
    cy := 0
    num := 0
    for _, p := range region.points {
        cx += p.X
        cy += p.Y
        num++
    }
    return float64(cx)/float64(num), float64(cy)/float64(num)
}

// a few points in (or near) the region.
func (region *Region) anchors() [4][2]float64 {
    cx, cy := region.centroid()

    xweight := [4]int{1,1,3,3}
    yweight := [4]int{1,3,1,3}
    var result [4][2]float64
    for i := 0; i < 4; i++ {
        dx := 0
        dy := 0
        numx := 0
        numy := 0
        for _, p := range region.points {
            if float64(p.X) > cx {
                dx += xweight[i] * p.X
                numx += xweight[i]
            } else {
                dx += (4 - xweight[i]) * p.X
                numx += 4 - xweight[i]
            }
            if float64(p.Y) > cy {
                dy += yweight[i] * p.Y
                numy += yweight[i]
            } else {
                dy += (4 - yweight[i]) * p.Y
                numy += 4 - yweight[i]
            }
        }
        result[i][0] = float64(dx) / float64(numx)
        result[i][1] = float64(dy) / float64(numy)
    }
    return result
}

func (region *Region) split(img image.Image) (*Region, *Region) {
    anchors := region.anchors()
    // maximize the difference between the average color on the two sides
    maxdiff := 0.0
    var maxa *Region = nil
    var maxb *Region = nil
    maxanchor := 0
    maxangle := 0
    for anchor := 0; anchor < 4; anchor++ {
        for angle := 0; angle < 16; angle++ {
            sin, cos := math.Sincos(float64(angle) * math.Pi / 16.0)
            a := new(Region)
            b := new(Region)
            for _, p := range region.points {
                dx := float64(p.X) - anchors[anchor][0]
                dy := float64(p.Y) - anchors[anchor][1]
                if dx * sin + dy * cos >= 0 {
                    a.points = append(a.points, p)
                } else {
                    b.points = append(b.points, p)
                }
            }
            if len(a.points) == 0 || len(b.points) == 0 {
                continue
            }
            a_red, a_green, a_blue := a.meanColor(img)
            b_red, b_green, b_blue := b.meanColor(img)
            diff := math.Abs(a_red - b_red) + math.Abs(a_green - b_green) + math.Abs(a_blue - b_blue)
            if diff >= maxdiff {
                maxdiff = diff
                maxa = a
                maxb = b
                maxanchor = anchor
                maxangle = angle
            }
        }
    }
    region.anchor = maxanchor
    region.angle = maxangle
    region.children[0] = maxa
    region.children[1] = maxb
    return maxa, maxb
}

// split regions take 7 bits plus their descendents
// unsplit regions take 13 bits
// so each split saves 13-7=6 bits on the parent region
// and costs 2*13 = 26 bits on the children, for a net of 20 bits/split
func (region *Region) encode(img image.Image) []int {
    bits := make([]int, 0)
    if region.children[0] != nil {
        bits = append(bits, 1)
        d := region.anchor
        a := region.angle
        bits = append(bits, d&1, d>>1&1)
        bits = append(bits, a&1, a>>1&1, a>>2&1, a>>3&1)
        bits = append(bits, region.children[0].encode(img)...)
        bits = append(bits, region.children[1].encode(img)...)
    } else {
        bits = append(bits, 0)
        r, g, b := region.meanColor(img)
        kr := int(r/256./16.)
        kg := int(g/256./16.)
        kb := int(b/256./16.)
        bits = append(bits, kr&1, kr>>1&1, kr>>2&1, kr>>3)
        bits = append(bits, kg&1, kg>>1&1, kg>>2&1, kg>>3)
        bits = append(bits, kb&1, kb>>1&1, kb>>2&1, kb>>3)
    }
    return bits
}

func encode(name string) []byte {
    file, _ := os.Open(name)
    img, _, _ := image.Decode(file)

    // encoding bit stream
    bits := make([]int, 0)

    // start by encoding the bounds
    bounds := img.Bounds()
    w := bounds.Max.X - bounds.Min.X
    for ; w > 3; w >>= 1 {
        bits = append(bits, 1, w & 1)
    }
    bits = append(bits, 0, w & 1)
    h := bounds.Max.Y - bounds.Min.Y
    for ; h > 3; h >>= 1 {
        bits = append(bits, 1, h & 1)
    }
    bits = append(bits, 0, h & 1)

    // make new region containing whole image
    region := new(Region)
    region.children[0] = nil
    region.children[1] = nil
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            region.points = append(region.points, image.Point{x, y})
        }
    }

    // split the region with the most contrast until we're out of bits.
    regions := make([]*Region, 1)
    regions[0] = region
    for bitcnt := len(bits) + 13; bitcnt <= 919-20; bitcnt += 20 {
        var best_reg *Region
        best_dev := -1.0
        for _, reg := range regions {
            if reg.children[0] != nil {
                continue
            }
            dev := reg.deviation(img)
            if dev > best_dev {
                best_reg = reg
                best_dev = dev
            }
        }
        a, b := best_reg.split(img)
        regions = append(regions, a, b)
    }

    // encode regions
    bits = append(bits, region.encode(img)...)

    // convert to tweet
    n := big.NewInt(0)
    for i := 0; i < len(bits); i++ {
        n.SetBit(n, i, uint(bits[i]))
    }
    s := make([]byte,0)
    r := new(big.Int)
    for i := 0; i < 140; i++ {
        n.DivMod(n, big.NewInt(95), r)
        s = append(s, byte(r.Int64() + 32))
    }
    return s
}

// decodes and fills in region.  returns number of bits used.
func (region *Region) decode(bits []int, img *image.RGBA) int {
    if bits[0] == 1 {
        anchors := region.anchors()
        anchor := bits[1] + bits[2]*2
        angle := bits[3] + bits[4]*2 + bits[5]*4 + bits[6]*8
        sin, cos := math.Sincos(float64(angle) * math.Pi / 16.)
        a := new(Region)
        b := new(Region)
        for _, p := range region.points {
            dx := float64(p.X) - anchors[anchor][0]
            dy := float64(p.Y) - anchors[anchor][1]
            if dx * sin + dy * cos >= 0 {
                a.points = append(a.points, p)
            } else {
                b.points = append(b.points, p)
            }
        }
        x := a.decode(bits[7:], img)
        y := b.decode(bits[7+x:], img)
        return 7 + x + y
    }
    r := bits[1] + bits[2]*2 + bits[3]*4 + bits[4]*8
    g := bits[5] + bits[6]*2 + bits[7]*4 + bits[8]*8
    b := bits[9] + bits[10]*2 + bits[11]*4 + bits[12]*8
    c := color.RGBA{uint8(r*16+8), uint8(g*16+8), uint8(b*16+8), 255}
    for _, p := range region.points {
        img.Set(p.X, p.Y, c)
    }
    return 13
}

func decode(name string) image.Image {
    file, _ := os.Open(name)
    length, _ := file.Seek(0, 2)
    file.Seek(0, 0)
    tweet := make([]byte, length)
    file.Read(tweet)

    // convert to bit string
    n := big.NewInt(0)
    m := big.NewInt(1)
    for _, c := range tweet {
        v := big.NewInt(int64(c - 32))
        v.Mul(v, m)
        n.Add(n, v)
        m.Mul(m, big.NewInt(95))
    }
    bits := make([]int, 0)
    for ; n.Sign() != 0; {
        bits = append(bits, int(n.Int64() & 1))
        n.Rsh(n, 1)
    }
    for ; len(bits) < 919; {
        bits = append(bits, 0)
    }

    // extract width and height
    w := 0
    k := 1
    for ; bits[0] == 1; {
        w += k * bits[1]
        k <<= 1
        bits = bits[2:]
    }
    w += k * (2 + bits[1])
    bits = bits[2:]
    h := 0
    k = 1
    for ; bits[0] == 1; {
        h += k * bits[1]
        k <<= 1
        bits = bits[2:]
    }
    h += k * (2 + bits[1])
    bits = bits[2:]

    // make new region containing whole image
    region := new(Region)
    region.children[0] = nil
    region.children[1] = nil
    for y := 0; y < h; y++ {
        for x := 0; x < w; x++ {
            region.points = append(region.points, image.Point{x, y})
        }
    }

    // new image
    img := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{w, h}})

    // decode regions
    region.decode(bits, img)

    return img
}

func main() {
    if os.Args[1] == "encode" {
        s := encode(os.Args[2])
        file, _ := os.Create(os.Args[3])
        file.Write(s)
        file.Close()
    }
    if os.Args[1] == "decode" {
        img := decode(os.Args[2])
        file, _ := os.Create(os.Args[3])
        png.Encode(file, img)
        file.Close()
    }
}

3
Ahbap, bunlar harika görünüyor.
MrZander

2
Aman Tanrım, bu harika.
jdstankosky

4
Bekle, iplerin nerede?
jdstankosky

1
Bu şimdiye kadar en sevdiğim.
primo

4
Kübist görünüm için +1 .
Ilmari Karonen,

36

piton

Kodlama gerektirir numpy , scipy ve scikit-image .
Kod çözme yalnızca PIL gerektirir .

Bu, süper piksel enterpolasyonuna dayanan bir yöntemdir. Başlamak için, her görüntü benzer renkte benzer boyutta 70 bölgeye bölünmüştür . Örneğin, manzara resmi aşağıdaki şekilde bölünmüştür:

görüntü tanımını buraya girin

Her bir bölgenin centroid'i (en fazla 402 nokta içermeyen bir ızgara üzerinde en yakın raster noktasına) ve ortalama renginin (216 renk paletinden) olduğu ve bu bölgelerin her birinin 0 ile kodlandığı için 86832 , yetenekli saklanmasını 2.5 (aslında yazdırılabilir aSCII karakterleri 2.497 greyscale biraz kodlamak için yeterli oda bırakarak).

Dikkatliyseniz, 140 / 2.5 = 56 bölgeyi fark etmiş olabilirsiniz ve daha önce de söylediğim gibi 70 değil . Bununla birlikte, bu bölgelerin her birinin, herhangi bir sırayla listelenebilecek benzersiz ve karşılaştırılabilir bir nesne olduğuna dikkat edin. Bu nedenle , diğer 14'ü kodlamak için ilk 56 bölgenin permütasyonunu kullanabiliriz , aynı zamanda en boy oranını saklamak için birkaç bit bıraktık.

Daha spesifik olarak, ilave 14 bölgenin her biri bir sayıya dönüştürülür ve daha sonra bu sayıların her biri bir araya getirilir (geçerli değer 86832 ile çarpılır ve bir sonraki eklenir). Bu (devasa) sayı daha sonra 56 nesne üzerindeki bir permütasyona dönüştürülür .

Örneğin:

from my_geom import *

# this can be any value from 0 to 56!, and it will map unambiguously to a permutation
num = 595132299344106583056657556772129922314933943196204990085065194829854239
perm = num2perm(num, 56)
print perm
print perm2num(perm)

çıkacak:

[0, 3, 33, 13, 26, 22, 54, 12, 53, 47, 8, 39, 19, 51, 18, 27, 1, 41, 50, 20, 5, 29, 46, 9, 42, 23, 4, 37, 21, 49, 2, 6, 55, 52, 36, 7, 43, 11, 30, 10, 34, 44, 24, 45, 32, 28, 17, 35, 15, 25, 48, 40, 38, 31, 16, 14]
595132299344106583056657556772129922314933943196204990085065194829854239

Elde edilen permütasyon daha sonra orijinal 56 bölgeye uygulanır . Orijinal sayı (ve dolayısıyla ek 14 bölge) aynı şekilde 56 kodlanmış bölgenin izinlerinin sayısal temsiline dönüştürülmesiyle elde edilebilir .

Ne zaman --greyscalebir seçenek kodlayıcı ile kullanılan, 94 bölgeleri yerine kullanılır (ayrıldı 70 , 24 ), ayrıca 558 tarama noktaları ve 16 gri tonları.

Kod çözülürken, bu bölgelerin her biri, yukarıdan görüldüğü gibi, bölgenin merkezinde tepe noktası olan sonsuzluğa uzanan bir 3D koni olarak işlem görür (aka bir Voronoi Şeması). Sınırlar daha sonra nihai ürünü oluşturmak için bir araya getirilir.

Gelecekteki İyileştirmeler

  1. Mona Lisa'nın boyutları, en boy oranını saklama yöntemim yüzünden biraz kapalı. Farklı bir sistem kullanmam gerekecek. Orijinal en boy oranının 1: 21 ile 21: 1 arasında olduğunu varsayarak, bunun makul bir varsayım olduğunu düşünüyorum.
  2. Hindenburg çok geliştirilebilirdi. Kullandığım renk paletinde sadece 6 gri ton var. Yalnızca gri tonlama modunu tanıtırsam, renk derinliğini, bölge sayısını, tarama noktalarının sayısını veya üçünün herhangi bir birleşimini artırmak için ek bilgileri kullanabilirim. Üçünü de --greyscaleyapan kodlayıcıya bir seçenek ekledim .
  3. 2d Şekiller, muhtemelen karıştırma kapalıyken daha iyi görünür. Bunun için bir bayrak ekleyeceğim. Segmentasyon oranını kontrol etmek için bir kodlayıcı seçeneği ve harmanlamayı kapatmak için bir kod çözücü seçeneği eklendi.
  4. Kombinatorik ile daha eğlenceli. 56! aslında 15 ek bölge ve 15 depolayacak kadar büyük ! toplamda 73 kişi için 2 tane daha depolayacak kadar büyük . Ama bekleyin, dahası var! Bu 73 nesnenin bölümlenmesi daha fazla bilgi depolamak için de kullanılabilir. Örneğin, 73 ilk 56 bölgeyi seçmek için 56 yol seçiyor , ardından 17 sonraki 15'i seçmek için 15 yol seçiyor . Toplam 2403922132944423072 bölümleme, toplam 76 bölge için 3 bölge daha depolayacak kadar büyük. 73'ün tüm bölümlerini benzersiz bir şekilde 56 , 15 , 2 ... ve daha sonra gruplara ayırmak için akıllıca bir yol bulmam gerekiyordu . Belki pratik değil, ama düşünülmesi gereken ilginç bir problem.

0VW*`Gnyq;c1JBY}tj#rOcKm)v_Ac\S.r[>,Xd_(qT6 >]!xOfU9~0jmIMG{hcg-'*a.s<X]6*%U5>/FOze?cPv@hI)PjpK9\iA7P ]a-7eC&ttS[]K>NwN-^$T1E.1OH^c0^"J 4V9X

görüntü tanımını buraya girin görüntü tanımını buraya girin


0Jc?NsbD#1WDuqT]AJFELu<!iE3d!BB>jOA'L|<j!lCWXkr:gCXuD=D\BL{gA\ 8#*RKQ*tv\\3V0j;_4|o7>{Xage-N85):Q/Hl4.t&'0pp)d|Ry+?|xrA6u&2E!Ls]i]T<~)58%RiA

ve

4PV 9G7X|}>pC[Czd!5&rA5 Eo1Q\+m5t:r#;H65NIggfkw'h4*gs.:~<bt'VuVL7V8Ed5{`ft7e>HMHrVVUXc.{#7A|#PBm,i>1B781.K8>s(yUV?a<*!mC@9p+Rgd<twZ.wuFnN dp

görüntü tanımını buraya girin görüntü tanımını buraya girin görüntü tanımını buraya girin

İkincisi, --greyscaleseçenekle kodlanmış .


3dVY3TY?9g+b7!5n`)l"Fg H$ 8n?[Q-4HE3.c:[pBBaH`5'MotAj%a4rIodYO.lp$h a94$n!M+Y?(eAR,@Y*LiKnz%s0rFpgnWy%!zV)?SuATmc~-ZQardp=?D5FWx;v=VA+]EJ(:%

görüntü tanımını buraya girin görüntü tanımını buraya girin

--greyscaleSeçeneği ile kodlanmış .


.9l% Ge<'_)3(`DTsH^eLn|l3.D_na,,sfcpnp{"|lSv<>}3b})%m2M)Ld{YUmf<Uill,*:QNGk,'f2; !2i88T:Yjqa8\Ktz4i@h2kHeC|9,P` v7Xzd Yp&z:'iLra&X&-b(g6vMq

görüntü tanımını buraya girin görüntü tanımını buraya girin

Seçeneklerle kodlanmış --ratio 60ve kodlanmış --no-blending.


encoder.py

from __future__ import division
import argparse, numpy
from skimage.io import imread
from skimage.transform import resize
from skimage.segmentation import slic
from skimage.measure import regionprops
from my_geom import *

def encode(filename, seg_ratio, greyscale):
  img = imread(filename)

  height = len(img)
  width = len(img[0])
  ratio = width/height

  if greyscale:
    raster_size = 558
    raster_ratio = 11
    num_segs = 94
    set1_len = 70
    max_num = 8928  # 558 * 16
  else:
    raster_size = 402
    raster_ratio = 13
    num_segs = 70
    set1_len = 56
    max_num = 86832 # 402 * 216

  raster_width = (raster_size*ratio)**0.5
  raster_height = int(raster_width/ratio)
  raster_width = int(raster_width)

  resize_height = raster_height * raster_ratio
  resize_width = raster_width * raster_ratio

  img = resize(img, (resize_height, resize_width))

  segs = slic(img, n_segments=num_segs-4, ratio=seg_ratio).astype('int16')

  max_label = segs.max()
  numpy.place(segs, segs==0, [max_label+1])
  regions = [None]*(max_label+2)

  for props in regionprops(segs):
    label = props['Label']
    props['Greyscale'] = greyscale
    regions[label] = Region(props)

  for i, a in enumerate(regions):
    for j, b in enumerate(regions):
      if a==None or b==None or a==b: continue
      if a.centroid == b.centroid:
        numpy.place(segs, segs==j, [i])
        regions[j] = None

  for y in range(resize_height):
    for x in range(resize_width):
      label = segs[y][x]
      regions[label].add_point(img[y][x])

  regions = [r for r in regions if r != None]

  if len(regions)>num_segs:
    regions = sorted(regions, key=lambda r: r.area)[-num_segs:]

  regions = sorted(regions, key=lambda r: r.to_num(raster_width))

  set1, set2 = regions[-set1_len:], regions[:-set1_len]

  set2_num = 0
  for s in set2:
    set2_num *= max_num
    set2_num += s.to_num(raster_width)

  set2_num = ((set2_num*85 + raster_width)*85 + raster_height)*25 + len(set2)
  perm = num2perm(set2_num, set1_len)
  set1 = permute(set1, perm)

  outnum = 0
  for r in set1:
    outnum *= max_num
    outnum += r.to_num(raster_width)

  outnum *= 2
  outnum += greyscale

  outstr = ''
  for i in range(140):
    outstr = chr(32 + outnum%95) + outstr
    outnum //= 95

  print outstr

parser = argparse.ArgumentParser(description='Encodes an image into a tweetable format.')
parser.add_argument('filename', type=str,
  help='The filename of the image to encode.')
parser.add_argument('--ratio', dest='seg_ratio', type=float, default=30,
  help='The segmentation ratio. Higher values (50+) will result in more regular shapes, lower values in more regular region color.')
parser.add_argument('--greyscale', dest='greyscale', action='store_true',
  help='Encode the image as greyscale.')
args = parser.parse_args()

encode(args.filename, args.seg_ratio, args.greyscale)

decoder.py

from __future__ import division
import argparse
from PIL import Image, ImageDraw, ImageChops, ImageFilter
from my_geom import *

def decode(instr, no_blending=False):
  innum = 0
  for c in instr:
    innum *= 95
    innum += ord(c) - 32

  greyscale = innum%2
  innum //= 2

  if greyscale:
    max_num = 8928
    set1_len = 70
    image_mode = 'L'
    default_color = 0
    raster_ratio = 11
  else:
    max_num = 86832
    set1_len = 56
    image_mode = 'RGB'
    default_color = (0, 0, 0)
    raster_ratio = 13

  nums = []
  for i in range(set1_len):
    nums = [innum%max_num] + nums
    innum //= max_num

  set2_num = perm2num(nums)

  set2_len = set2_num%25
  set2_num //= 25

  raster_height = set2_num%85
  set2_num //= 85
  raster_width = set2_num%85
  set2_num //= 85

  resize_width = raster_width*raster_ratio
  resize_height = raster_height*raster_ratio

  for i in range(set2_len):
    nums += set2_num%max_num,
    set2_num //= max_num

  regions = []
  for num in nums:
    r = Region()
    r.from_num(num, raster_width, greyscale)
    regions += r,

  masks = []

  outimage = Image.new(image_mode, (resize_width, resize_height), default_color)

  for a in regions:
    mask = Image.new('L', (resize_width, resize_height), 255)
    for b in regions:
      if a==b: continue
      submask = Image.new('L', (resize_width, resize_height), 0)
      poly = a.centroid.bisected_poly(b.centroid, resize_width, resize_height)
      ImageDraw.Draw(submask).polygon(poly, fill=255, outline=255)
      mask = ImageChops.multiply(mask, submask)
    outimage.paste(a.avg_color, mask=mask)

  if not no_blending:
    outimage = outimage.resize((raster_width, raster_height), Image.ANTIALIAS)
    outimage = outimage.resize((resize_width, resize_height), Image.BICUBIC)
    smooth = ImageFilter.Kernel((3,3),(1,2,1,2,4,2,1,2,1))
    for i in range(20):outimage = outimage.filter(smooth)
  outimage.show()

parser = argparse.ArgumentParser(description='Decodes a tweet into and image.')
parser.add_argument('--no-blending', dest='no_blending', action='store_true',
    help="Do not blend the borders in the final image.")
args = parser.parse_args()

instr = raw_input()
decode(instr, args.no_blending)

my_geom.py

from __future__ import division

class Point:
  def __init__(self, x, y):
    self.x = x
    self.y = y
    self.xy = (x, y)

  def __eq__(self, other):
    return self.x == other.x and self.y == other.y

  def __lt__(self, other):
    return self.y < other.y or (self.y == other.y and self.x < other.x)

  def inv_slope(self, other):
    return (other.x - self.x)/(self.y - other.y)

  def midpoint(self, other):
    return Point((self.x + other.x)/2, (self.y + other.y)/2)

  def dist2(self, other):
    dx = self.x - other.x
    dy = self.y - other.y
    return dx*dx + dy*dy

  def bisected_poly(self, other, resize_width, resize_height):
    midpoint = self.midpoint(other)
    points = []
    if self.y == other.y:
      points += (midpoint.x, 0), (midpoint.x, resize_height)
      if self.x < midpoint.x:
        points += (0, resize_height), (0, 0)
      else:
        points += (resize_width, resize_height), (resize_width, 0)
      return points
    elif self.x == other.x:
      points += (0, midpoint.y), (resize_width, midpoint.y)
      if self.y < midpoint.y:
        points += (resize_width, 0), (0, 0)
      else:
        points += (resize_width, resize_height), (0, resize_height)
      return points
    slope = self.inv_slope(other)
    y_intercept = midpoint.y - slope*midpoint.x
    if self.y > midpoint.y:
      points += ((resize_height - y_intercept)/slope, resize_height),
      if slope < 0:
        points += (resize_width, slope*resize_width + y_intercept), (resize_width, resize_height)
      else:
        points += (0, y_intercept), (0, resize_height)
    else:
      points += (-y_intercept/slope, 0),
      if slope < 0:
        points += (0, y_intercept), (0, 0)
      else:
        points += (resize_width, slope*resize_width + y_intercept), (resize_width, 0)
    return points

class Region:
  def __init__(self, props={}):
    if props:
      self.greyscale = props['Greyscale']
      self.area = props['Area']
      cy, cx = props['Centroid']
      if self.greyscale:
        self.centroid = Point(int(cx/11)*11+5, int(cy/11)*11+5)
      else:
        self.centroid = Point(int(cx/13)*13+6, int(cy/13)*13+6)
    self.num_pixels = 0
    self.r_total = 0
    self.g_total = 0
    self.b_total = 0

  def __lt__(self, other):
    return self.centroid < other.centroid

  def add_point(self, rgb):
    r, g, b = rgb
    self.r_total += r
    self.g_total += g
    self.b_total += b
    self.num_pixels += 1
    if self.greyscale:
      self.avg_color = int((3.2*self.r_total + 10.7*self.g_total + 1.1*self.b_total)/self.num_pixels + 0.5)*17
    else:
      self.avg_color = (
        int(5*self.r_total/self.num_pixels + 0.5)*51,
        int(5*self.g_total/self.num_pixels + 0.5)*51,
        int(5*self.b_total/self.num_pixels + 0.5)*51)

  def to_num(self, raster_width):
    if self.greyscale:
      raster_x = int((self.centroid.x - 5)/11)
      raster_y = int((self.centroid.y - 5)/11)
      return (raster_y*raster_width + raster_x)*16 + self.avg_color//17
    else:
      r, g, b = self.avg_color
      r //= 51
      g //= 51
      b //= 51
      raster_x = int((self.centroid.x - 6)/13)
      raster_y = int((self.centroid.y - 6)/13)
      return (raster_y*raster_width + raster_x)*216 + r*36 + g*6 + b

  def from_num(self, num, raster_width, greyscale):
    self.greyscale = greyscale
    if greyscale:
      self.avg_color = num%16*17
      num //= 16
      raster_x, raster_y = num%raster_width, num//raster_width
      self.centroid = Point(raster_x*11 + 5, raster_y*11+5)
    else:
      rgb = num%216
      r, g, b = rgb//36, rgb//6%6, rgb%6
      self.avg_color = (r*51, g*51, b*51)
      num //= 216
      raster_x, raster_y = num%raster_width, num//raster_width
      self.centroid = Point(raster_x*13 + 6, raster_y*13 + 6)

def perm2num(perm):
  num = 0
  size = len(perm)
  for i in range(size):
    num *= size-i
    for j in range(i, size): num += perm[j]<perm[i]
  return num

def num2perm(num, size):
  perm = [0]*size
  for i in range(size-1, -1, -1):
    perm[i] = int(num%(size-i))
    num //= size-i
    for j in range(i+1, size): perm[j] += perm[j] >= perm[i]
  return perm

def permute(arr, perm):
  size = len(arr)
  out = [0] * size
  for i in range(size):
    val = perm[i]
    out[i] = arr[val]
  return out

1
Bu şaşırtıcı bir şey değil kısa
lochok

Mona Lisa'nın renkli versiyonu patlamış göğüslerinden birine benziyor. Bir kenara atmak, bu inanılmaz.
jdstankosky

4
Ek verileri kodlamak için permütasyonları kullanmak oldukça zekice.
Sir_Lagsalot

Gerçekten gerçekten harika. Bu 3 dosya ile ilgili bir fikir verebilir misiniz? gist.github.com
rubik

2
@ rubik, bu zorluğun çözümlerinin tümü gibi inanılmaz derecede kayıplıdır;)
primo

17

PHP

Tamam, biraz zaman aldı, ama işte burada. Tüm görüntüler gri renkte. Renkler, yöntemim için kodlamak için çok fazla bit aldı: P


Mona Lisa
47 Renkler Monokrom
101 bayt dize.

dt99vvv9t8G22+2eZbbf55v3+fAH9X+AD/0BAF6gIOX5QRy7xX8em9/UBAEVXKiiqKqqqiqqqqNqqqivtXqqMAFVUBVVVVVVVVVVU

mona lisa


2B Şekiller
36 Renk Tek Renkli
105 bayt dize.

oAAAAAAABMIDUAAEBAyoAAAAAgAwAAAAADYBtsAAAJIDbYAAAAA22AGwAAAAAGwAAAAAAAAAAKgAAAAAqgAAAACoAAAAAAAAAAAAAAAAA

2d 2dc


Hindenburg
62 Renkler Siyah Beyaz
112 karakter.

t///tCSuvv/99tmwBI3/21U5gCW/+2bdDMxLf+r6VsaHb/tt7TAodv+NhtbFVX/bGD1IVq/4MAHbKq/4AABbVX/AQAFN1f8BCBFntb/6ttYdWnfg

burada resimler görüntü tanımını buraya girin


Dağlar
63 Renk Tek renkli
122 karakter.

qAE3VTkaIAKgqSFigAKoABgQEqAABuAgUQAGenRIBoUh2eqhABCee/2qSSAQntt/s2kJCQbf/bbaJgbWebzqsPZ7bZttwABTc3VAUFDbKqqpzY5uqpudnp5vZg

picshere görüntü tanımını buraya girin


Benim yöntem

Bit akışımı bir tür base64 kodlamasıyla kodlarım. Okunabilir metne kodlanmadan önce, işte olan şey.

Kaynak görüntüyü yükler ve maksimum yüksekliğe veya genişliğe (oryantasyona, dikey / yatay olarak) 20 piksel boyutuna getiririm.

Sonra, yeni görüntünün her pikselini 6 renk gri tonlamalı bir palette en yakınına yeniden renklendiririm.

Bunu yaptıktan sonra, [AF] harflerinin temsil ettiği her piksel rengiyle bir dize oluşturdum.

Daha sonra dizgideki 6 farklı harfin dağılımını hesaplıyorum ve kodlama için harf frekanslarına göre en optimize edilmiş ikili ağacı seçiyorum. 15 olası ikili ağaç vardır.

[1|0]Görüntünün uzun veya geniş olmasına bağlı olarak bit akışımı tek bir bit ile başlatırım . Sonra, kod çözücüye görüntüyü çözmek için hangi ikili ağacın kullanılması gerektiğini bildirmek için akıştaki sonraki 4 bit kullanın.

Ardından görüntüyü temsil eden bit akışı var. Her piksel ve rengi, 2 veya 3 bit ile gösterilir. Bu, yazdırılan her ascii karakteri için en az 2 ve en fazla 3 piksel değerinde bilgi saklamama izin verir. 1110Mona Lisa tarafından kullanılan bir ikili ağaç örneği :

    TREE
   /    \
  #      #
 / \    / \
E   #  F   #
   / \    / \
  A   B  C   D

E 00ve F harfleri 10Mona Lisa'daki en yaygın renklerdir. A 010, B 011, C 110ve D 111en az rastlananlardır.

İkili ağaçlar şu şekilde çalışır: Bir parçadan diğerine 0gitmek, sola 1gitmek, sağa gitmek demektir. Ağacın bir yaprağına veya çıkmaza kadar isabet edene kadar devam et. Sonunda bıraktığın yaprak, istediğin karakter.

Neyse, ikili sokmayı base64 karakterlerine kodladım. İpin kodunu çözerken, işlem tüm pikselleri uygun renge atayarak tersten yapılır ve görüntü, kodlanmış boyutun iki katı kadar ölçeklendirilir (X veya Y maksimum 40 piksel, hangisi daha büyükse) ve sonra bir evrişim matrisi renkleri yumuşatmak için her şeye uygulanır.

Her neyse, işte güncel kod: " pastebin bağlantısı "

Çirkin, ama iyileştirmeler için herhangi bir yer görürseniz, bana bildirin. İstediğim gibi birlikte hackledim. Ben ÖĞRENDİM ÇOK BU CHALLENGE DAN. Gönderdiğiniz için teşekkür ederiz OP!


2
Bunlar ne kadar unutularak depolama alanınız olduğunu dikkate alarak inanılmaz derecede iyi görünüyor (Mona Lisa 920'den sadece 606 bit kullanıyor!).
primo

Teşekkürler Primo, Buna gerçekten minnettarım. İşine her zaman hayranım, bu yüzden söylediğini duymak oldukça gurur verici!
jdstankosky

13

İlk denemem. Bu iyileştirme için oda var. Bence formatın kendisi gerçekten işe yarıyor, sorun kodlayıcıda. Bu, ve çıktılarımdan ayrı ayrı bitlerimi özlüyorum ... benim (burada biraz daha yüksek kalitede) dosyam, kalması gereken zaman, 144 karakterde sona erdi. (ve gerçekten keşke orada olsaydım - bunlar ile aralarındaki farklar göze çarpıyor). Yine de öğrendim, 140 karakterin ne kadar büyük olduğunu asla abartma.

Bunu RISC-OS paletinin değiştirilmiş bir sürümüne indirdim - temel olarak, çünkü 32 renkli bir palete ihtiyacım vardı ve başlamak için yeterince iyi bir yer gibi görünüyordu. Bu da bence biraz değişebilir. palet

Aşağıdaki şekillere ayırırım: Şekiller görüntüyü ön ve arka rengin palet bloklarına (bu durumda, 2x2 piksel) bölün.

Sonuçlar:

Aşağıda tweetler, orijinaller ve tweetin nasıl çözüldüğü gösterilmektedir.

*=If`$aX:=|"&brQ(EPZwxu4H";|-^;lhJCfQ(W!TqWTai),Qbd7CCtmoc(-hXt]/l87HQyaYTEZp{eI`/CtkHjkFh,HJWw%5[d}VhHAWR(@;M's$VDz]17E@6

Hindeberg Benim hindenberg

"&7tpnqK%D5kr^u9B]^3?`%;@siWp-L@1g3p^*kQ=5a0tBsA':C0"*QHVDc=Z='Gc[gOpVcOj;_%>.aeg+JL4j-u[a$WWD^)\tEQUhR]HVD5_-e`TobI@T0dv_el\H1<1xw[|D

Dağ Benim dağ

)ey`ymlgre[rzzfi"K>#^=z_Wi|@FWbo#V5|@F)uiH?plkRS#-5:Yi-9)S3:#3 Pa4*lf TBd@zxa0g;li<O1XJ)YTT77T1Dg1?[w;X"U}YnQE(NAMQa2QhTMYh..>90DpnYd]?

Şekiller Şekillerim

%\MaaX/VJNZX=Tq,M>2"AwQVR{(Xe L!zb6(EnPuEzB}Nk:U+LAB_-K6pYlue"5*q>yDFw)gSC*&,dA98`]$2{&;)[ 4pkX |M _B4t`pFQT8P&{InEh>JHYn*+._[b^s754K_

Mona Lisa Mona Lisa Madeni

Renklerin yanlış olduğunu biliyorum, ama Monalisa'yı gerçekten seviyorum. Bulanıklığı kaldırırsam (ki bu çok zor olmaz), makul bir kübist izlenim: p

Üzerinde çalışmam lazım

  • Şekil algılama ekleme
  • Daha iyi bir renk "fark" algoritması
  • Kayıp bitlerimin nereye gittiğini bulmak

Bunları düzeltmek için daha sonra biraz daha çalışacağım ve kodlayıcıyı geliştireceğim. Bu fazladan 20 ya da öylesine karakterler büyük miktarda fark yaratır. Onları geri istiyorum.

C # kaynağı ve renk paleti https://dl.dropboxusercontent.com/u/46145976/Base96.zip adresinde yer almaktadır - gerçekte, rüzgârda, ayrı ayrı çalıştırıldığında mükemmel çalışmayabilir (programların argümanlarındaki boşluklar böyle gitmediği için) iyi).

Kodlayıcı oldukça ortalama bir makinede birkaç saniyeden az zaman alıyor.


11
Kanka. Bunlar galeride gördüğüm çağdaş sanatlardan daha iyi görünüyorlar ... Büyük baskılar yapmalı ve satmalısınız!
jdstankosky

1
Kartuşu Atari'mden çıkarıp tekrar takmam gerekiyor gibi görünüyor.
undergroundmonorail

13

Rengi tutmaya çalışmaktan vazgeçtim ve siyah beyaza döndüm, çünkü renkle denediğim her şey tanınmıyordu.

Temel olarak, yaptığı tek şey pikselleri yaklaşık olarak 3 eşit parçaya bölmek: siyah, gri ve beyaz. Aynı zamanda boyutunu koruyamaz.

Hindenburg

~62RW.\7`?a9}A.jvCedPW0t)]g/e4 |+D%n9t^t>wO><",C''!!Oh!HQq:WF>\uEG?E=Mkj|!u}TC{7C7xU:bb`We;3T/2:Zw90["$R25uh0732USbz>Q;q"

Hindenburg HindenburgCompressed

Mona Lisa

=lyZ(i>P/z8]Wmfu>] T55vZB:/>xMz#Jqs6U3z,)n|VJw<{Mu2D{!uyl)b7B6x&I"G0Y<wdD/K4hfrd62_8C\W7ArNi6R\Xz%f U[);YTZFliUEu{m%[gw10rNY_`ICNN?_IB/C&=T

MonaLisa MonaLisaCompressed

Dağlar

+L5#~i%X1aE?ugVCulSf*%-sgIg8hQ3j/df=xZv2v?'XoNdq=sb7e '=LWm\E$y?=:"#l7/P,H__W/v]@pwH#jI?sx|n@h\L %y(|Ry.+CvlN $Kf`5W(01l2j/sdEjc)J;Peopo)HJ

Dağlar MountainsCompressed

Şekiller

3A"3yD4gpFtPeIImZ$g&2rsdQmj]}gEQM;e.ckbVtKE(U$r?{,S>tW5JzQZDzoTy^mc+bUV vTUG8GXs{HX'wYR[Af{1gKwY|BD]V1Z'J+76^H<K3Db>Ni/D}][n#uwll[s'c:bR56:

Şekiller ShapesCompressed

İşte program. Tweet'i python compress.py -c img.pngsıkıştırır img.pngve yazdırır.

python compress.py -d img.pngtweet'i stdin'den alır ve görüntüyü kaydeder img.png.

from PIL import Image
import sys
quanta  = 3
width   = 24
height  = 24

def compress(img):
    pix = img.load()
    psums = [0]*(256*3)
    for x in range(width):
        for y in range(height):
            r,g,b,a = pix[x,y]
            psums[r+g+b] += 1
    s = 0
    for i in range(256*3):
        s = psums[i] = psums[i]+s

    i = 0
    for x in range(width):
        for y in range(height):
            r,g,b,a = pix[x,y]
            t = psums[r+g+b]*quanta / (width*height)
            if t == quanta:
                t -= 1
            i *= quanta
            i += t
    s = []
    while i:
        s += chr(i%95 + 32)
        i /= 95
    return ''.join(s)

def decompress(s):
    i = 0
    for c in s[::-1]:
        i *= 95
        i += ord(c) - 32
    img = Image.new('RGB',(width,height))
    pix = img.load()
    for x in range(width)[::-1]:
        for y in range(height)[::-1]:
            t = i % quanta
            i /= quanta
            t *= 255/(quanta-1)
            pix[x,y] = (t,t,t)
    return img

if sys.argv[1] == '-c':
    img = Image.open(sys.argv[2]).resize((width,height))
    print compress(img)
elif sys.argv[1] == '-d':
    img = decompress(raw_input())
    img.resize((256,256)).save(sys.argv[2],'PNG')

Sınırlandırılmamış görünüş oranları için Lol, +1.
jdstankosky

7

R'deki mütevazı katkım:

encoder<-function(img_file){
    img0 <- as.raster(png::readPNG(img_file))
    d0 <- dim(img0)
    r <- d0[1]/d0[2]
    f <- floor(sqrt(140/r))
    d1 <- c(floor(f*r),f)
    dx <- floor(d0[2]/d1[2])
    dy <- floor(d0[1]/d1[1])
    img1 <- matrix("",ncol=d1[2],nrow=d1[1])
    x<-seq(1,d0[1],by=dy)
    y<-seq(1,d0[2],by=dx)
    for(i in seq_len(d1[1])){
        for (j in seq_len(d1[2])){
            img1[i,j]<-names(which.max(table(img0[x[i]:(x[i]+dy-1),y[j]:(y[j]+dx-1)])))
            }
        }
    img2 <- as.vector(img1)
    table1 <- array(sapply(seq(0,255,length=4),function(x)sapply(seq(0,255,length=4),function(y)sapply(seq(0,255,length=4),function(z)rgb(x/255,y/255,z/255)))),dim=c(4,4,4))
    table2 <- array(strsplit(rawToChar(as.raw(48:(48+63))),"")[[1]],dim=c(4,4,4))
    table3 <- cbind(1:95,sapply(32:126,function(x)rawToChar(as.raw(x))))
    a <- as.array(cut(colorspace::hex2RGB(img2)@coords,breaks=seq(0,1,length=5),include.lowest=TRUE))
    dim(a) <- c(length(img2),3)
    img3 <- apply(a,1,function(x)paste("#",c("00","55","AA","FF")[x[1]],c("00","55","AA","FF")[x[2]],c("00","55","AA","FF")[x[3]],sep=""))
    res<-paste(sapply(img3,function(x)table2[table1==x]),sep="",collapse="")
    paste(table3[table3[,1]==d1[1],2],table3[table3[,1]==d1[2],2],res,collapse="",sep="")
    }

decoder<-function(string){
    s <- unlist(strsplit(string,""))
    table1 <- array(sapply(seq(0,255,length=4),function(x)sapply(seq(0,255,length=4),function(y)sapply(seq(0,255,length=4),function(z)rgb(x/255,y/255,z/255)))),dim=c(4,4,4))
    table2 <- array(strsplit(rawToChar(as.raw(48:(48+63))),"")[[1]],dim=c(4,4,4))
    table3 <- cbind(1:95,sapply(32:126,function(x)rawToChar(as.raw(x))))
    nr<-as.integer(table3[table3[,2]==s[1],1])
    nc<-as.integer(table3[table3[,2]==s[2],1])
    img <- sapply(s[3:length(s)],function(x){table1[table2==x]})
    png(w=nc,h=nr,u="in",res=100)
    par(mar=rep(0,4))
    plot(c(1,nr),c(1,nc),type="n",axes=F,xaxs="i",yaxs="i")
    rasterImage(as.raster(matrix(img,nr,nc)),1,1,nr,nc)
    dev.off()
    }

Fikir basitçe rasterin (dosyanın png cinsinden olması gerekir) hücre sayısı 140'tan daha düşük olan bir matrise indirgenmesidir, tweetler daha sonra iki karakterden önce sıralanan bir dizi renk (64 renkte) olur. ve taramanın sütunları.

encoder("Mona_Lisa.png")
[1] ",(XXX000@000000XYi@000000000TXi0000000000TX0000m000h00T0hT@hm000000T000000000000XX00000000000XXi0000000000TXX0000000000"

görüntü tanımını buraya girin

encoder("630x418.png") # Not a huge success for this one :)
[1] "(-00000000000000000000EEZZooo00E0ZZooo00Z00Zooo00Zo0oooooEZ0EEZoooooooEZo0oooooo000ooZ0Eo0000oooE0EE00oooEEEE0000000E00000000000"

görüntü tanımını buraya girin

encoder("2d shapes.png")
[1] "(,ooooooooooooooooooooo``ooooo0o``oooooooooo33ooooooo33oo0ooooooooooo>>oooo0oooooooo0ooooooooooooolloooo9oolooooooooooo"

görüntü tanımını buraya girin

encoder("mountains.png")
[1] "(,_K_K0005:_KKK0005:__OJJ006:_oKKK00O:;;_K[[4OD;;Kooo4_DOKK_o^D_4KKKJ_o5o4KK__oo4_0;K___o5JDo____o5Y0____440444040400D4"

görüntü tanımını buraya girin


4

Tam bir çözüm değil, sadece yöntemi oraya koymak. (Matlab)

Ağırlıklı bir voronoi diyagramı oluşturmak için 16 renk paleti ve 40 pozisyon kullandım . Görüntüye uyması için genetik algoritma ve basit tepe tırmanma algoritması kullanılmıştır.

Orjinal resimli albüm ve ayrıca 4 renkli ve sabit pozisyonlu 16 byte versiyonum var. :)

görüntü tanımını buraya girin

(Görüntüyü burada yeniden boyutlandırabilir miyim?)


1
Diğer görüntüleri de gönderir misin? Bu sıkıştırma ile nasıl göründüklerini görmek istiyorum!
jdstankosky

@jdstankosky Üzgünüm, şimdi yapamam. Belki bir süre sonra ...
randomra

4

C #

Güncelleme - Sürüm 2


JPEG verilerini kodlamak için şimdi MagickImage.NET ( https://magick.codeplex.com/ ) kullanarak bu konuda başka bir girişimde bulundum , JPEG başlık verilerini daha iyi işlemek için bazı temel kodlar yazdım (primo'nun önerdiği gibi). JPEG sıkıştırma bazı yumuşatmaya yardımcı olan çıkışında GuassianBlur kullanılır. Yeni sürüm daha iyi performans gösterdiğinden, yayınımı yeni yöntemi yansıtacak şekilde güncelledim.


Yöntem


Renk derinliğini veya kenar kimliğini değiştirmeye çalışmak veya görüntü boyutunu azaltmak için farklı yöntemler kullanmaya çalışmak yerine, benzersiz bir şey (umarım) denedim, JPEG algoritmasını küçültülmüş sürümlerinde maksimum sıkıştırmada kullandım görüntüleri, daha sonra "StartOfScan" dışında her şeyi ortadan kaldırarak ( http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure) ) ve birkaç temel başlık öğesinden başka boyutu kabul edilebilir bir miktara indirebilirim. Sonuçlar 140 karakter için oldukça etkileyici, JPEG'lerde bana yeni bir saygı duyuyor:

Hindenburg

Hindenburg orijinal

,$`"(b $!   _ &4j6k3Qg2ns2"::4]*;12T|4z*4n*4<T~a4- ZT_%-.13`YZT;??e#=*!Q033*5>z?1Ur;?2i2^j&r4TTuZe2444b*:>z7.:2m-*.z?|*-Pq|*,^Qs<m&?:e-- 

Dağlar

Dağlar orijinal

,$  (a`,!  (1 Q$ /P!U%%%,0b*2nr4 %)3t4 +3#UsZf3S2 7-+m1Yqis k2U'm/#"h q2T4#$s.]/)%1T &*,4Ze w$Q2Xqm&: %Q28qiqm Q,48Xq12 _

Mona Lisa

Mona Lisa orijinal

23  (a`,!  (1 Q$ /P q1Q2Tc$q0,$9--/!p Ze&:6`#*,Tj6l0qT%(:!m!%(84|TVk0(*2k24P)!e(U,q2x84|Tj*8a1a-%** $r4_--Xr&)12Tj8a2Tj* %r444 %%%% !

Şekiller

Şekiller orijinal

(ep 1# ,!  (1 Q$ /P"2`#=WTp $X[4 &[Vp p<T +0 cP* 0W=["jY5cZ9(4 (<]t  ]Z %ZT -P!18=V+UZ4" #% i6%r}#"l p QP>*r $!Yq(!]2 jo* zp!0 4 % !0 4 % '!


kod


Sürüm 2 - http://pastebin.com/Tgr8XZUQ

Gerçekten ReSharper + 'ı özlemeye başladım + Geliştirmem gereken çok şey var, hala zor kodlamanın bir parçası oluyor, yine de uğraşması ilginç (bunun için VS'de çalıştırmak için MagickImage dll's'a ihtiyacınız olduğunu unutmayın)


Orijinal (Kaldırıldı) - http://pastebin.com/BDPT0BKT

Hala biraz dağınıklık.


“Bu gerçekten şu anda bir karmaşa”, buna katılıyorum - bu başlığı oluşturmak için daha iyi bir yol olmalı? Ama sonuçların en önemli şey olduğunu düşünüyorum. +1
primo

1

Python 3

Yöntem

Programın ilk yaptığı şey görüntüyü küçültmek ve büyüklüğünü azaltmak.

İkincisi, rgb değerlerini ikiliye dönüştürür ve son birkaç rakamdan kopar.

Daha sonra, taban 2 verisini, resmin boyutlarını eklediği taban 10'a dönüştürür.

Sonra bulabildiğim bütün bilgiyi kullanarak, taban 10'daki verileri taban 95'e dönüştürür. Bununla birlikte, / x01 ve benzerlerini, metin dosyasını yazan işlevi reddetme kabiliyeti nedeniyle kullanamadım.

Ve (ek belirsizlik için), kod çözme işlevi tersine yapar.

compress.py

    from PIL import Image
def FromBase(digits, b): #converts to base 10 from base b
    numerals='''0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_-+={[}]|:;"',<.>/?`~\\ '''
    D=[]
    for d in range(0,len(digits)):
        D.append(numerals.index(digits[d]))
    s=0
    D=D[::-1]
    for x in range(0,len(D)):
        s+=D[x]*(b**x)
    return(str(s))
def ToBase(digits,b): #converts from base 10 to base b
    numerals='''0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_-+={[}]|:;"',<.>/?`~\\ '''
    d=int(digits)
    D=''
    B=b
    while(B<=d):
        B*=b
    B//=b
    while(B>=1):
        D+=numerals[d//B]
        d-=((d//B)*B)
        B//=b
    return(D)
im=Image.open('1.png')
size=im.size
scale_factor=40
im=im.resize((int(size[0]/scale_factor),int(size[1]/scale_factor)), Image.ANTIALIAS)
a=list(im.getdata())
K=''
for x in a:
    for y in range(0,3):
        Y=bin(x[y])[2:]
        while(len(Y))<9:
            Y='0'+Y
        K+=str(Y)[:-5]
K='1'+K
print(len(K))
K=FromBase(K,2)
K+=str(size[0])
K+=str(size[1])
K=ToBase(K,95)
with open('1.txt', 'w') as outfile:
    outfile.write(K)

decode.py

    from random import randint, uniform
from PIL import Image, ImageFilter
import math
import json
def FromBase(digits, b): #str converts to base 10 from base b
    numerals='''0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_-+={[}]|:;"',<.>/?`~\\ \x01\x02\x03\x04\x05\x06\x07'''
    D=[]
    for d in range(0,len(digits)):
        D.append(numerals.index(digits[d]))
    s=0
    D=D[::-1]
    for x in range(0,len(D)):
        s+=D[x]*(b**x)
    return(str(s))
def ToBase(digits,b): #str converts from base 10 to base b
    numerals='''0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_-+={[}]|:;"',<.>/?`~\\ \x01\x02\x03\x04\x05\x06\x07'''
    d=int(digits)
    D=''
    B=b
    while(B<=d):
        B*=b
    B//=b
    while(B>=1):
        D+=numerals[d//B]
        d-=((d//B)*B)
        B//=b
    return(D)
scale_factor=40
K=open('1.txt', 'r').read()
K=FromBase(K,95)
size=[int(K[-6:][:-3])//scale_factor,int(K[-6:][-3:])//scale_factor]
K=K[:-6]
K=ToBase(K,2)
K=K[1:]
a=[]
bsize=4
for x in range(0,len(K),bsize*3):
    Y=''
    for y in range(0,bsize*3):
        Y+=K[x+y]
    y=[int(Y[0:bsize]+'0'*(9-bsize)),int(Y[bsize:bsize*2]+'0'*(9-bsize)),int(Y[bsize*2:bsize*3]+'0'*(9-bsize))]
    y[0]=int(FromBase(str(y[0]),2))
    y[1]=int(FromBase(str(y[1]),2))
    y[2]=int(FromBase(str(y[2]),2))
    a.append(tuple(y))
im=Image.new('RGB',size,'black')
im.putdata(a[:size[0]*size[1]])
im=im.resize((int(size[0]*scale_factor),int(size[1]*scale_factor)), Image.ANTIALIAS)
im.save('pic.png')

Çığlık

Scream1 Scream2

hqgyXKInZo9-|A20A*53ljh[WFUYu\;eaf_&Y}V/@10zPkh5]6K!Ur:BDl'T/ZU+`xA4'\}z|8@AY/5<cw /8hQq[dR1S 2B~aC|4Ax"d,nX`!_Yyk8mv6Oo$+k>_L2HNN.#baA

Mona Lisa

Mona Lisa 1 Mona Lisa 2

f4*_!/J7L?,Nd\#q$[f}Z;'NB[vW%H<%#rL_v4l_K_ >gyLMKf; q9]T8r51it$/e~J{ul+9<*nX0!8-eJVB86gh|:4lsCumY4^y,c%e(e3>sv(.y>S8Ve.tu<v}Ww=AOLrWuQ)

Küreler

Küreler 1 Küreler 2

})|VF/h2i\(D?Vgl4LF^0+zt$d}<M7E5pTA+=Hr}{VxNs m7Y~\NLc3Q"-<|;sSPyvB[?-B6~/ZHaveyH%|%xGi[Vd*SPJ>9)MKDOsz#zNS4$v?qM'XVe6z\
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.