ट्वीट्स (चरम छवि संपीड़न संस्करण) में छवियाँ सांकेतिक शब्दों में बदलना [बंद]


59

स्टैक ओवरफ्लो में बहुत सफल ट्विटर छवि एन्कोडिंग चुनौती के आधार पर ।

यदि एक छवि 1000 शब्दों के लायक है, तो आप 114.97 बाइट्स में कितनी छवि फिट कर सकते हैं?

मैं आपको एक मानक ट्विटर टिप्पणी में छवियों को संपीड़ित करने के लिए एक सामान्य-उद्देश्य विधि के साथ आने के लिए चुनौती देता हूं जिसमें केवल मुद्रण योग्य ASCII पाठ शामिल है

नियम:

  1. आपको एक प्रोग्राम लिखना होगा जो एक छवि ले सकता है और एन्कोडेड टेक्स्ट को आउटपुट कर सकता है।
  2. कार्यक्रम द्वारा बनाया गया पाठ अधिकतम 140 वर्णों पर लंबा होना चाहिए और इसमें केवल वे वर्ण होने चाहिए जिनके कोड बिंदु 32-126 की सीमा में हों, समावेशी हों।
  3. आपको एक प्रोग्राम (संभवतः एक ही प्रोग्राम) लिखना होगा जो एन्कोडेड टेक्स्ट को ले जा सके और फोटोग्राफ का डिकोड किया हुआ संस्करण आउटपुट कर सके।
  4. आपका प्रोग्राम बाहरी पुस्तकालयों और फ़ाइलों का उपयोग कर सकता है, लेकिन इसके लिए इंटरनेट कनेक्शन या अन्य कंप्यूटरों के कनेक्शन की आवश्यकता नहीं हो सकती है।
  5. डिकोडिंग प्रक्रिया किसी भी तरह से मूल छवियों तक नहीं पहुंच सकती है या इसमें शामिल नहीं हो सकती है।
  6. आपके कार्यक्रम को इनमें से कम से कम एक प्रारूप में छवियों को स्वीकार करना चाहिए (आवश्यक रूप से अधिक नहीं): बिटमैप, जेपीईजी, जीआईएफ, टीआईएफएफ, पीएनजी। यदि कुछ या सभी सैंपल इमेज सही फॉर्मेट में नहीं हैं, तो आप उन्हें अपने प्रोग्राम से कम्प्रेशन से पहले खुद को कन्वर्ट कर सकते हैं।

आंकना:

यह कुछ व्यक्तिपरक चुनौती है, इसलिए विजेता (अंततः) मेरे द्वारा आंका जाएगा। मैं अपने फैसले को महत्वपूर्ण कारकों में से एक पर केंद्रित करूंगा, जो महत्व कम करने में नीचे सूचीबद्ध हैं:

  1. नमूना छवि के रूप में सूचीबद्ध नहीं सहित, चित्रों की एक विस्तृत विविधता को संपीड़ित करने का एक उचित काम करने की क्षमता
  2. एक छवि में प्रमुख तत्वों की रूपरेखा को संरक्षित करने की क्षमता
  3. एक छवि में प्रमुख तत्वों के रंगों को संपीड़ित करने की क्षमता
  4. एक छवि में मामूली विवरण की रूपरेखा और रंगों को संरक्षित करने की क्षमता
  5. संपीड़न समय। यद्यपि यह उतना महत्वपूर्ण नहीं है कि किसी छवि को कितनी अच्छी तरह से संकुचित किया जाता है, लेकिन तेज कार्यक्रम धीमे कार्यक्रमों से बेहतर होते हैं जो समान कार्य करते हैं।

आपके प्रस्तुत में विघटन के बाद परिणामी छवियां शामिल होनी चाहिए, साथ ही ट्विटर टिप्पणी उत्पन्न हुई। यदि संभव हो, तो आप स्रोत कोड का लिंक भी दे सकते हैं।

नमूना चित्र:

हिंडनबर्ग , पर्वतीय परिदृश्य , मोना लिसा , 2 डी आकार


U + 007F (127) और U + 0080 (128) नियंत्रण वर्ण हैं। मैं उन पर भी प्रतिबंध लगाने का सुझाव दूंगा।
कृपया

अच्छा अवलोकन। मैं उसे ठीक कर दूंगा।
PhiNotPi

क्या ट्विटर कुछ हद तक यूनिकोड की अनुमति नहीं देता है?
मारिनस

4
मुझे लगता है कि मैं इसके समाधान का पेटेंट कराना चाहूंगा।
शमिड्टी

2
"पर्वतीय परिदृश्य" 1024x768 - जाने से पहले इसे प्राप्त करें! -> i.imgur.com/VaCzpRL.jpg <-
jdstankosky

जवाबों:


58

मैंने वास्तविक संपीड़न जोड़कर अपनी विधि में सुधार किया है। अब यह निम्नलिखित रूप से चलने के द्वारा संचालित होता है:

  1. छवि को YUV में बदलें
  2. पहलू अनुपात को संरक्षित करने वाली छवि को छोटा करें (यदि छवि रंग है, तो क्रोमा को 1/3 की चौड़ाई और ऊंचाई पर नमूना दिया जाता है)

  3. नमूना प्रति 4 बिट के लिए बिट गहराई कम करें

  4. छवि के लिए माध्य भविष्यवाणी लागू करें, जिससे नमूना वितरण अधिक समान हो

  5. छवि के लिए अनुकूली श्रेणी संपीड़न लागू करें।

  6. देखें कि संपीड़ित छवि का आकार <= 112 है या नहीं

112 बाइट्स में फिट होने वाली सबसे बड़ी छवि को तब अंतिम छवि के रूप में उपयोग किया जाता है, शेष दो बाइट्स संपीड़ित छवि की चौड़ाई और ऊंचाई को संग्रहीत करने के लिए उपयोग किया जाता है, साथ ही एक ध्वज इंगित करता है कि क्या छवि रंग में है। डिकोडिंग के लिए, प्रक्रिया को उल्टा कर दिया जाता है, और छवि को छोटा कर दिया जाता है ताकि छोटे आयाम 128 हो।

सुधार के लिए कुछ जगह है, अर्थात् सभी उपलब्ध बाइट्स आमतौर पर उपयोग नहीं किए जाते हैं, लेकिन मुझे लगता है कि मैं डाउनसम्पलिंग + दोषरहित संपीड़न के लिए महत्वपूर्ण रूप से कम रिटर्न के बिंदु पर हूं।

त्वरित और गंदा सी ++ स्रोत

Windows exe

मोना लिसा (13x20 luminance, 4x6 क्रोमा)

&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           

मोना लीसा मोना लिसा ट्विटर पर छा गई

हिंडनबर्ग (21x13 luminance)

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'       

हिंडनबर्ग हिंडनबर्ग ट्विटर ने एनकाउंटर किया

पर्वत (19x14 luminance, 6x4 क्रोमा)

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           

पहाड़ पहाड़ का ट्विटर एनकाउंटर

2 डी आकार (21x15 luminance, 7x5 क्रोमा)

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!    

2 डी आकार 2 डी आकार ट्विटर एन्कोडेड


7
इससे मुझे महसूस होता है कि मैं मोतियाबिंद या कुछ विकसित कर रहा हूं। हाहा, बढ़िया काम!
jdstankosky

अच्छा सुधार!
jdstankosky

37

जाओ

छवि को क्षेत्रों में पुनरावर्ती रूप से विभाजित करके काम करता है। मैं उच्च सूचना सामग्री वाले क्षेत्रों को विभाजित करने की कोशिश करता हूं, और दो क्षेत्रों के बीच रंग में अंतर को अधिकतम करने के लिए विभाजन रेखा को चुनता हूं।

प्रत्येक विभाजन को कुछ बिट्स का उपयोग करके विभाजित किया जाता है ताकि विभाजन रेखा को एनकोड किया जा सके। प्रत्येक पत्ती क्षेत्र को एक ही रंग के रूप में एन्कोड किया गया है।

यहाँ छवि विवरण दर्ज करें

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

यहाँ छवि विवरण दर्ज करें

<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>

यहाँ छवि विवरण दर्ज करें

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&

यहाँ छवि विवरण दर्ज करें

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.,(

हिंडनबर्ग चित्र बहुत भद्दा लगता है, लेकिन दूसरों को मैं पसंद करता हूं।

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
यार, वो मस्त लगते हैं।
श्रीजेंडर

2
ओह गोश जो कि अद्भुत है।
jdstankosky

4
रुको, तुम्हारा तार कहाँ है?
jdstankosky

1
यह अब तक का मेरा पसंदीदा है।
प्रिमो


36

अजगर

एन्कोडिंग के लिए सुन्न , SciPy और स्किटिट-छवि की आवश्यकता होती है ।
डिकोडिंग के लिए केवल पीआईएल की आवश्यकता होती है ।

यह सुपरपिक्सल इंटरपोलेशन पर आधारित एक विधि है। शुरू करने के लिए, प्रत्येक छवि को समान रंग के 70 समान आकार के क्षेत्रों में विभाजित किया गया है । उदाहरण के लिए, परिदृश्य चित्र को निम्न तरीके से विभाजित किया गया है:

यहाँ छवि विवरण दर्ज करें

प्रत्येक क्षेत्र का केन्द्रक स्थित है (402 से अधिक अंक वाले ग्रिड पर निकटतम रेखापुंज बिंदु पर), साथ ही यह औसत रंग (216 रंग पैलेट से) है, और इनमें से प्रत्येक क्षेत्र 0 से एक संख्या के रूप में एन्कोड किया गया है करने के लिए 86,832 , करने में सक्षम में संग्रहीत किया जा रहा 2.5 प्रिंट योग्य ASCII वर्ण (वास्तव में 2.497 , एक ग्रेस्केल बिट के लिए सांकेतिक शब्दों में बदलना करने के लिए सिर्फ पर्याप्त जगह छोड़कर)।

यदि आप चौकस हैं, तो आपने देखा होगा कि 140 / 2.5 = 56 क्षेत्र, और 70 नहीं जैसा कि मैंने पहले कहा था। हालाँकि, सूचना है कि इनमें से प्रत्येक क्षेत्र एक अद्वितीय, तुलनीय वस्तु है, जिसे किसी भी क्रम में सूचीबद्ध किया जा सकता है। इस वजह से, हम पहले 56 क्षेत्रों के क्रमांकन का उपयोग अन्य 14 के लिए सांकेतिक शब्दों में बदलना कर सकते हैं , साथ ही पहलू अनुपात को संग्रहीत करने के लिए कुछ बिट्स शेष रह सकते हैं।

अधिक विशेष रूप से, अतिरिक्त 14 क्षेत्रों में से प्रत्येक को एक संख्या में बदल दिया जाता है, और फिर इनमें से प्रत्येक संख्या एक साथ समाप्‍त हो जाती है (वर्तमान मान 86832 गुणा करके , और अगला जोड़कर)। यह (विशाल) संख्या 56 वस्तुओं पर क्रमचय में बदल जाती है ।

उदाहरण के लिए:

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)

उत्पादन होगा:

[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

परिणामी क्रमपरिवर्तन को फिर मूल 56 क्षेत्रों में लागू किया जाता है । मूल संख्या (और इस तरह अतिरिक्त 14 क्षेत्र) को 56 एन्कोडेड क्षेत्रों के क्रमांकन को इसके संख्यात्मक प्रतिनिधित्व में परिवर्तित करके निकाला जा सकता है ।

जब --greyscaleविकल्प को एनकोडर के साथ उपयोग किया जाता है , तो 558 रेखापुंज बिंदुओं और 16 रंगों के ग्रे के साथ 94 क्षेत्रों ( 70 , 24 को अलग ) के बजाय उपयोग किया जाता है ।

जब डिकोडिंग की जाती है, तो इन क्षेत्रों में से प्रत्येक को 3 डी शंकु के रूप में माना जाता है, जो अनंत में विस्तारित होता है, इसके शीर्ष पर इस क्षेत्र के केंद्र में, जैसा कि ऊपर से देखा गया (उर्फ वोरोनोई आरेख)। फिर अंतिम उत्पाद बनाने के लिए सीमाओं को एक साथ मिश्रित किया जाता है।

भविष्य में सुधार

  1. मोना लिसा के आयाम थोड़े बंद हैं, जिस तरह से मैं पहलू अनुपात को संग्रहीत कर रहा हूं। मुझे एक अलग प्रणाली का उपयोग करने की आवश्यकता होगी। फिक्स्ड, यह मानते हुए कि मूल पहलू अनुपात 1:21 और 21: 1 के बीच कहीं है, जो मुझे लगता है कि एक उचित धारणा है।
  2. हिंडनबर्ग में बहुत सुधार किया जा सकता था। रंग पैलेट मैं केवल 6 रंगों का उपयोग कर रहा हूँ ग्रे। अगर मैंने एक ग्रेस्केल-ओनली मोड की शुरुआत की है, तो मैं रंग की गहराई, क्षेत्रों की संख्या, रेखापुंज बिंदुओं की संख्या, या तीनों के किसी भी संयोजन को बढ़ाने के लिए अतिरिक्त जानकारी का उपयोग कर सकता हूं। मैंने --greyscaleएनकोडर में एक विकल्प जोड़ा है , जो तीनों को करता है।
  3. 2 डी आकृतियाँ संभवतया सम्मिश्रण के साथ बेहतर दिखेंगी। मैं संभवतः इसके लिए एक ध्वज जोड़ूंगा। विभाजन अनुपात को नियंत्रित करने के लिए एक एनकोडर विकल्प जोड़ा गया, और एक डिकोडर विकल्प सम्मिश्रण बंद करने के लिए।
  4. कोम्बिनेटरिक्स के साथ अधिक मज़ा। 56! 15 अतिरिक्त क्षेत्रों और 15 को संग्रहीत करने के लिए वास्तव में काफी बड़ा है ! 73 के कुल के लिए 2 और स्टोर करने के लिए पर्याप्त बड़ा है । लेकिन रुकिए, और भी है! इन 73 ऑब्जेक्ट का विभाजन अधिक जानकारी संग्रहीत करने के लिए भी किया जा सकता है। उदाहरण के लिए, शुरुआती 56 क्षेत्रों का चयन करने के लिए 73 चुनिंदा 56 तरीके हैं, और फिर अगले 15 का चयन करने के लिए 17 15 तरीकों का चयन करें । 2403922132944423072 विभाजन का एक भव्य कुल , 76 के कुल 3 और क्षेत्रों को संग्रहीत करने के लिए पर्याप्त है। मैं विशिष्ट संख्या के सभी विभाजनों एक चतुर रास्ता साथ आने के लिए आवश्यकता होगी 73 के समूह में 56 , 15 , 2 ... और वापस । शायद व्यावहारिक नहीं, लेकिन सोचने के लिए एक दिलचस्प समस्या।

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

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें


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

तथा

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

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

दूसरा --greyscaleविकल्प के साथ एन्कोड किया गया ।


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(:%

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

--greyscaleविकल्प के साथ एन्कोड किया गया ।


.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

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

साथ एन्कोड किया गया --ratio 60, और --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
यह अद्भुत कुछ भी कम नहीं है
lochok

मोना लिसा का रंग संस्करण उसके एक स्तन की तरह दिखता है। एक तरफ से, यह अविश्वसनीय है।
jdstankosky

4
अतिरिक्त डेटा सांकेतिक शब्दों में बदलना करने के लिए क्रमपरिवर्तन का उपयोग करना बल्कि चालाक है।
सर_जगलोट

वास्तव में बहुत बढ़िया। क्या आप इस 3 फाइलों के साथ एक जिस्ट बना सकते हैं? gist.github.com
rubik

2
@ रबिक यह अविश्वसनीय रूप से हानिपूर्ण है, जैसा कि इस चुनौती के सभी समाधान हैं;)
प्रिमो

17

पीएचपी

ठीक है, मुझे थोड़ी देर लगी, लेकिन यहाँ यह है। सभी चित्र greyscale में मेरी विधि के लिए सांकेतिक शब्दों में बदलने के लिए रंगों को बहुत अधिक बिट्स मिले: पी


मोना लिसा
47 कलर्स मोनोक्रोम
101 बाइट स्ट्रिंग।

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

मोना lisa


2 डी आकार
36 रंग मोनोक्रोम
105 बाइट स्ट्रिंग।

oAAAAAAABMIDUAAEBAyoAAAAAgAwAAAAADYBtsAAAJIDbYAAAAA22AGwAAAAAGwAAAAAAAAAAKgAAAAAqgAAAACoAAAAAAAAAAAAAAAAA

2 डी 2dc


हिंडनबर्ग
62 कलर्स मोनोक्रोम
112 वर्ण।

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

यहाँ पिक्स यहाँ छवि विवरण दर्ज करें


पर्वत
63 रंग मोनोक्रोम
122 वर्ण।

qAE3VTkaIAKgqSFigAKoABgQEqAABuAgUQAGenRIBoUh2eqhABCee/2qSSAQntt/s2kJCQbf/bbaJgbWebzqsPZ7bZttwABTc3VAUFDbKqqpzY5uqpudnp5vZg

picshere यहाँ छवि विवरण दर्ज करें


मेरी विधि

मैं अपने बिटस्ट्रीम को एक प्रकार की बेस 64 एनकोडिंग के साथ एनकोड करता हूं। इससे पहले कि यह पठनीय पाठ में कूटबद्ध हो जाए, यहाँ क्या होता है।

मैं स्रोत छवि को लोड करता हूं और इसे 20 पिक्सल के अधिकतम ऊंचाई या चौड़ाई (अभिविन्यास, चित्र / परिदृश्य के आधार पर) का आकार देता हूं।

इसके बाद मैं नई छवि के प्रत्येक पिक्सेल को एक 6 रंग के ग्रेस्केल पैलेट पर निकटतम मैच में पुन: जोड़ देता हूं।

ऐसा करने के बाद, मैं प्रत्येक पिक्सेल रंग के साथ एक स्ट्रिंग बनाता हूं जो अक्षरों द्वारा दर्शाया गया है [AF]।

मैं तब स्ट्रिंग के भीतर 6 अलग-अलग अक्षरों के वितरण की गणना करता हूं और पत्र आवृत्तियों के आधार पर एन्कोडिंग के लिए सबसे अनुकूलित बाइनरी ट्री का चयन करता हूं। 15 संभावित बाइनरी पेड़ हैं।

मैं अपनी बिट स्ट्रीम को एक बिट के साथ शुरू करता हूं, जो [1|0]इस बात पर निर्भर करता है कि छवि लंबी या चौड़ी है। फिर मैं डिकोडर को सूचित करने के लिए स्ट्रीम में अगले 4 बिट्स का उपयोग करता हूं जो छवि को डीकोड करने के लिए बाइनरी ट्री का उपयोग किया जाना चाहिए।

क्या इस प्रकार छवि का प्रतिनिधित्व बिट्स की धारा है। प्रत्येक पिक्सेल और यह रंग 2 या 3 बिट्स द्वारा दर्शाया गया है। यह मुझे कम से कम 2 और अप करने के लिए 3 पिक्सेल के लायक हर मुद्रित ascii चरित्र के लिए जानकारी की अनुमति देता है। यहां बाइनरी ट्री का एक नमूना है 1110, जो मोना लिसा द्वारा उपयोग किया जाता है:

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

00और एफ अक्षर 10मोना लिसा में सबसे आम रंग हैं। ए 010, बी 011, सी 110, और डी 111सबसे कम लगातार हैं।

बाइनरी पेड़ इस तरह से काम करते हैं: बिट से बिट में जा रहे हैं, 0मतलब बाएं चले गए, 1मतलब सही चले। जब तक आप पेड़ पर एक पत्ता, या एक मृत अंत तक चलते रहें। जिस पत्ते पर आप समाप्त होते हैं, वह वह पात्र है जो आप चाहते हैं।

वैसे भी, मैं बाइनरी स्टिंग को बेस 64 अक्षरों में एन्कोड करता हूं। स्ट्रिंग को डीकोडिंग करते समय, प्रक्रिया को रिवर्स में किया जाता है, सभी पिक्सल को उपयुक्त रंग में निर्दिष्ट किया जाता है, और फिर छवि को एन्कोडेड आकार (अधिकतम 40 पिक्सल या तो एक्स या वाई, जो भी बड़ा हो) से दो बार स्केल किया जाता है और फिर एक कन्वेंशन मैट्रिक्स रंगों को चिकना करने के लिए पूरी चीज़ पर लागू किया जाता है।

वैसे भी, यहाँ वर्तमान कोड है: " pastebin लिंक "

यह बदसूरत है, लेकिन अगर आप सुधार के लिए कोई जगह देखते हैं, तो मुझे बताएं। मैंने जैसे चाहा, साथ में हैक कर लिया। मैं इस चुनौती से बहुत दूर था । धन्यवाद ओपी इसे पोस्ट करने के लिए!


2
ये अविश्वसनीय रूप से अच्छे लगते हैं कि आपके पास कितना अप्रयुक्त भंडारण स्थान है (मोना लिसा 920 से केवल 606 बिट्स का उपयोग करता है)।
प्रिमो

धन्यवाद, प्राइमो, मैं वास्तव में सराहना करता हूं। मैं हमेशा आपके काम की प्रशंसा करता हूं, इसलिए आपको यह कहते हुए सुनना काफी चापलूसी है!
jdstankosky

13

मेरा पहला प्रयास। इसमें सुधार की गुंजाइश है। मुझे लगता है कि प्रारूप वास्तव में काम करता है, मुद्दा एनकोडर में है। वह, और मैं अपने आउटपुट से अलग-अलग बिट्स को याद कर रहा हूं ... मेरी (थोड़ी अधिक गुणवत्ता तो यहां) फ़ाइल 144 वर्णों पर समाप्त हुई, जब कुछ बचा होना चाहिए था। (और मैं वास्तव में कामना करता हूं - इन और उन के बीच अंतर ध्यान देने योग्य हैं)। मैं हालांकि सीखा है, कभी नहीं overestimate कितना बड़ा 140 अक्षर है ...

मैं इसे आरआईएससी-ओएस पैलेट के एक संशोधित संस्करण में लाता हूं - मूल रूप से, क्योंकि मुझे 32 रंग पैलेट की जरूरत थी, और यह शुरू करने के लिए एक अच्छी जगह की तरह लग रहा था। मैं कुछ सोच बदल सकता हूं। पैलेट

मैं इसे निम्न आकृतियों में तोड़ता हूं: आकृतियाँ और छवि को पैलेट ब्लॉक (इस मामले में, 2x2 पिक्सल) में एक फ्रंट और बैक रंग में विभाजित किया।

परिणाम:

निम्नलिखित ट्वीट हैं, मूल और कैसे ट्वीट डिकोड किया गया है

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

Hindeberg मेरा हिंडेनबर्ग

"&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

पहाड़ मेरा पहाड़

)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]?

आकृतियाँ मेरी आकृतियाँ

%\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_

मोना लीसा मोना लिसा मेरा

मुझे पता है कि रंग गलत हैं, लेकिन मुझे वास्तव में मोनालिसा पसंद है। अगर मैंने धब्बा हटा दिया (जो बहुत कठिन नहीं होगा), तो यह एक उचित क्यूबिस्ट छाप है: पी

मुझे काम करने की जरूरत है

  • आकार का पता लगाना
  • एक बेहतर रंग "अंतर" एल्गोरिदम
  • यह पता लगाना कि मेरे लापता बिट्स कहां गए

मैं इसे बाद में उन लोगों को ठीक करने की कोशिश करने और एनकोडर में सुधार करने के लिए कुछ और काम दूंगा। उन अतिरिक्त 20 या तो वर्णों में बड़े पैमाने पर अंतर होता है। मैं उन्हें वापस चाहूंगा।

C # स्रोत और रंग पैलेट https://dl.dropboxusercontent.com/u/46145976/Base96.zip पर हैं - हालाँकि, हेंडसाइट में, अलग से चलाने पर पूरी तरह से काम नहीं हो सकता है (क्योंकि प्रोग्राम के लिए तर्कों में स्थान ऐसा नहीं होता है कुंआ)।

एनकोडर कम लेता है तो मेरे काफी औसत मशीन पर कुछ सेकंड।


11
Dude। वे किसी भी समकालीन कला से बेहतर दिखते हैं जो मैंने एक गैलरी में देखी है ... आपको उनमें से बहुत से प्रिंट बनाने चाहिए और उन्हें बेचना चाहिए!
jdstankosky

1
ऐसा लगता है कि मुझे अपने अटारी से कारतूस निकालने और इसे वापस प्लग करने की आवश्यकता है। मुझे यह पसंद है।
भूमिगत '

13

मैंने रंग रखने की कोशिश करना छोड़ दिया और काले और सफेद हो गए, क्योंकि मैंने रंग के साथ जो कुछ भी आजमाया वह पहचानने योग्य नहीं था।

मूल रूप से यह सब करता है पिक्सल को लगभग 3 बराबर भागों में विभाजित किया जाता है: काला, ग्रे और सफेद। यह भी आकार नहीं रखता है।

हिंडनबर्ग

~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"

हिंडनबर्ग HindenburgCompressed

मोना लीसा

=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

मोना लीसा MonaLisaCompressed

पहाड़ों

+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

पहाड़ों MountainsCompressed

आकृतियाँ

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:

आकृतियाँ ShapesCompressed

यहाँ कार्यक्रम है। ट्वीट को python compress.py -c img.pngसंपीड़ित img.pngऔर प्रिंट करता है।

python compress.py -d img.pngस्टीन से ट्वीट लेता है और छवि को बचाता है 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')

गैर-विवश पहलू अनुपात के लिए लोल, +1।
jdstankosky

7

आर में मेरा मामूली योगदान:

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()
    }

यह विचार एक मैट्रिक्स के लिए रेखापुंज (फ़ाइल को पीएनजी में होना है) को कम करने के लिए है, जिसकी सेल की संख्या 140 से कम है, ट्वीट्स फिर रंगों की एक सीरी (64 रंगों में) दो वर्णों से पहले पंक्तियों की संख्या को इंगित करता है और रेखापुंज के कॉलम।

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

यहाँ छवि विवरण दर्ज करें

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

यहाँ छवि विवरण दर्ज करें

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

यहाँ छवि विवरण दर्ज करें

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

यहाँ छवि विवरण दर्ज करें


4

एक पूर्ण समाधान नहीं है, बस विधि को वहां से बाहर रखना है। (Matlab)

मैं एक 16 रंग पैलेट और 40 स्थिति का उपयोग करता था एक भारित वोरोनोई आरेख बनाने के लिए । छवि को फिट करने के लिए इस्तेमाल किया आनुवंशिक एल्गोरिथ्म और सरल पहाड़ी-चढ़ाई एल्गोरिथ्म।

मूल छवि के साथ एल्बम और मेरे पास 16 बाइट संस्करण भी हैं, जिसमें 4 रंग और निश्चित स्थान हैं। :)

यहाँ छवि विवरण दर्ज करें

(क्या मैं यहाँ चित्र का आकार बदल सकता हूँ?)


1
क्या आप अन्य चित्र पोस्ट कर सकते हैं? मैं देखना चाहता हूँ कि वे इस संपीड़न के साथ क्या देखते हैं!
jdstankosky

@jdstankosky क्षमा करें, मैं अब ऐसा नहीं कर सकता। शायद कुछ समय बाद ...
randomra

4

सी#

अद्यतन - संस्करण २


मैंने इस पर एक और प्रयास किया, अब JPEG डेटा को एन्कोड करने के लिए MagickImage.NET ( https://magick.codeplex.com/ ) का उपयोग करते हुए , मैंने जेपीईजी हेडर डेटा को बेहतर बनाने के लिए कुछ बुनियादी कोड भी लिखे (जैसा कि प्रिमो ने सुझाव दिया था), मैंने भी। उत्पादन पर GuassianBlur का उपयोग किया जो जेपीईजी संपीड़न के कुछ नरम करने में मदद करता है। जैसे ही नया संस्करण बेहतर होता है, मैंने नई पद्धति को दर्शाने के लिए अपनी पोस्ट अपडेट कर दी है।


तरीका


मैंने रंग की गहराई, या किनारे की पहचान में हेर-फेर करने की कोशिश करने के बजाय कुछ अद्वितीय (उम्मीद) की कोशिश की है, या छवियों के आकार को कम करने के लिए अलग-अलग तरीकों का उपयोग करने की कोशिश कर रहा हूं जो मैंने खुद को जेपीईजी एल्गोरिथ्म में स्केल किए गए संस्करणों पर अधिकतम संपीड़न पर उपयोग किया है। छवियां, फिर सब कुछ खत्म कर देती हैं लेकिन "StartOfScan" ( http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure ) और कुछ प्रमुख हेडर तत्व मैं आकार को स्वीकार्य राशि तक प्राप्त करने में सक्षम हूं। परिणाम वास्तव में 140 पात्रों के लिए बहुत प्रभावशाली हैं, मुझे JPEG के लिए एक नया सम्मान मिला है:

हिंडनबर्ग

हिंडनबर्ग मूल

,$`"(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-- 

पहाड़ों

पहाड़ों मूल

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

मोना लीसा

मोना लीसा मूल

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 %%%% !

आकृतियाँ

आकृतियाँ मूल

(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 % '!


कोड


संस्करण 2 - http://pastebin.com/Tgr8XZUQ

मैं वास्तव में मिस करना शुरू कर रहा हूं


मूल (पदावनत) - http://pastebin.com/BDPT0BKT

अभी भी थोड़ा गड़बड़ है।


"यह वास्तव में एक गड़बड़ है अभी," मैं इससे सहमत हूँ - निश्चित रूप से उस हेडर को उत्पन्न करने का एक बेहतर तरीका होना चाहिए? लेकिन मुझे लगता है कि परिणाम सबसे ज्यादा मायने रखते हैं। +1
प्राइमो सिप

1

अजगर ३

तरीका

पहले जो कार्यक्रम करता है, वह छवि को छोटा कर रहा है, इसके आकार को बहुत कम कर रहा है।

दूसरा, यह आरजीबी मूल्यों को बाइनरी में परिवर्तित करता है, और पिछले कुछ अंकों को हटा देता है।

फिर यह आधार 2 डेटा को बेस 10 में परिवर्तित करता है, जहां यह चित्र के आयाम जोड़ता है।

फिर यह डेटा को बेस 10 से बेस 95 तक पहुंचाता है, सभी एससीआई जो मुझे मिल सकते हैं। हालाँकि, मैं / x01 का उपयोग नहीं कर सका और पाठ फ़ाइल को लिखने वाले फ़ंक्शन को नकारात्मक करने की अपनी क्षमता के कारण।

और (अतिरिक्त अस्पष्टता के लिए), डीकोड फ़ंक्शन इसे रिवर्स में करता है।

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')

चीख़

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

मोना लीसा

मोना लिसा १ मोना लिसा २

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)

क्षेत्रों

गोलाकार १ गोलाकार २

})|VF/h2i\(D?Vgl4LF^0+zt$d}<M7E5pTA+=Hr}{VxNs m7Y~\NLc3Q"-<|;sSPyvB[?-B6~/ZHaveyH%|%xGi[Vd*SPJ>9)MKDOsz#zNS4$v?qM'XVe6z\
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.