Mã hóa hình ảnh thành Tweets (Phiên bản nén hình ảnh cực lớn) [đã đóng]


59

Dựa trên thử thách mã hóa hình ảnh Twitter rất thành công tại Stack Overflow.

Nếu một hình ảnh có giá trị 1000 từ, bạn có thể chứa bao nhiêu hình ảnh trong 114,97 byte?

Tôi thách bạn đưa ra một phương pháp có mục đích chung để nén hình ảnh vào một bình luận Twitter tiêu chuẩn chỉ chứa văn bản ASCII có thể in được .

Quy tắc:

  1. Bạn phải viết một chương trình có thể chụp ảnh và xuất văn bản được mã hóa.
  2. Văn bản được tạo bởi chương trình phải dài tối đa 140 ký tự và chỉ được chứa các ký tự có điểm mã nằm trong phạm vi 32-126, bao gồm.
  3. Bạn phải viết một chương trình (có thể là cùng một chương trình) có thể lấy văn bản được mã hóa và xuất ra một phiên bản được giải mã của bức ảnh.
  4. Chương trình của bạn có thể sử dụng các thư viện và tệp bên ngoài, nhưng không thể yêu cầu kết nối internet hoặc kết nối với các máy tính khác.
  5. Quá trình giải mã không thể truy cập hoặc chứa hình ảnh gốc theo bất kỳ cách nào.
  6. Chương trình của bạn phải chấp nhận hình ảnh ở ít nhất một trong các định dạng này (không nhất thiết phải nhiều hơn): Bitmap, JPEG, GIF, TIFF, PNG. Nếu một số hoặc tất cả các hình ảnh mẫu không ở định dạng chính xác, bạn có thể tự chuyển đổi chúng trước khi nén bởi chương trình của mình.

Đánh giá:

Đây là một thử thách hơi chủ quan, vì vậy người chiến thắng (cuối cùng) sẽ được đánh giá bởi tôi. Tôi sẽ tập trung đánh giá của tôi vào một vài yếu tố quan trọng, được liệt kê dưới đây trong tầm quan trọng giảm dần:

  1. Khả năng thực hiện công việc hợp lý là nén nhiều loại hình ảnh, bao gồm cả những hình ảnh không được liệt kê dưới dạng hình ảnh mẫu
  2. Khả năng lưu giữ các phác thảo của các yếu tố chính trong một hình ảnh
  3. Khả năng nén màu của các yếu tố chính trong ảnh
  4. Khả năng lưu giữ các đường viền và màu sắc của các chi tiết nhỏ trong ảnh
  5. Thời gian nén. Mặc dù không quan trọng bằng việc hình ảnh được nén tốt như thế nào, nhưng các chương trình nhanh hơn sẽ tốt hơn các chương trình chậm hơn làm điều tương tự.

Nội dung gửi của bạn phải bao gồm các hình ảnh thu được sau khi giải nén, cùng với nhận xét Twitter được tạo. Nếu có thể, bạn cũng có thể cung cấp một liên kết đến mã nguồn.

Hình ảnh mẫu:

The Hindenburg , Phong cảnh núi non , Mona Lisa , Hình dạng 2D


U + 007F (127) và U + 0080 (128) là các ký tự điều khiển. Tôi sẽ đề nghị cấm những người là tốt.
Vui lòng khởi động

Quan sát tốt. Tôi sẽ sửa nó.
PhiNotPi

Không Twitter cho phép Unicode ở một mức độ nào đó?
bến tàu

4
Tôi cảm thấy như tôi muốn sáng chế một giải pháp cho việc này.
Shmiddty

2
"Phong cảnh miền núi" 1024x768 - Nhận nó trước khi nó biến mất! -> i.imgur.com/VaCzpRL.jpg <-
jdstankosky

Câu trả lời:


58

Tôi đã cải thiện phương pháp của mình bằng cách thêm nén thực tế. Bây giờ nó hoạt động bằng cách lặp đi lặp lại như sau:

  1. Chuyển đổi hình ảnh sang YUV
  2. Giảm kích thước hình ảnh giữ tỷ lệ khung hình (nếu hình ảnh là màu sắc, sắc độ được lấy mẫu ở 1/3 chiều rộng và chiều cao của độ chói)

  3. Giảm độ sâu bit xuống 4 bit cho mỗi mẫu

  4. Áp dụng dự đoán trung bình cho hình ảnh, làm cho phân phối mẫu đồng đều hơn

  5. Áp dụng nén phạm vi thích ứng cho hình ảnh.

  6. Xem kích thước của hình ảnh nén là <= 112

Hình ảnh lớn nhất phù hợp với 112 byte sau đó được sử dụng làm hình ảnh cuối cùng, với hai byte còn lại được sử dụng để lưu trữ chiều rộng và chiều cao của hình ảnh nén, cộng với một cờ cho biết hình ảnh có màu hay không. Để giải mã, quá trình được đảo ngược và hình ảnh được thu nhỏ lại để kích thước nhỏ hơn là 128.

Có một số chỗ cần cải thiện, cụ thể là không phải tất cả các byte có sẵn thường được sử dụng, nhưng tôi cảm thấy tôi đang ở điểm giảm đáng kể lợi nhuận cho việc lấy mẫu xuống + nén không mất dữ liệu.

Nguồn C ++ nhanh & bẩn

Windows exe

Mona Lisa (độ chói 13x20, sắc độ 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           

nàng mô na Li Sa Mona Lisa được mã hóa Twitter

Hindenburg (độ chói 21x13)

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 Mã hóa twitter Hindenburg

Núi (độ chói 19x14, sắc độ 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           

núi Mã hóa twitter trên núi

Hình dạng 2D (độ chói 21x15, sắc độ 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!    

Hình dạng 2D Hình dạng 2D được mã hóa twitter


7
Điều này khiến tôi cảm thấy như mình đang phát triển đục thủy tinh thể hoặc một cái gì đó. Haha, công việc tuyệt vời
jdstankosky

Cải tiến tốt đẹp!
jdstankosky

37

Đi

Hoạt động bằng cách chia hình ảnh thành các khu vực đệ quy. Tôi cố gắng phân chia các vùng có nội dung thông tin cao và chọn đường phân chia để tối đa hóa sự khác biệt về màu sắc giữa hai vùng.

Mỗi bộ phận được mã hóa bằng một vài bit để mã hóa đường phân chia. Mỗi vùng lá được mã hóa thành một màu duy nhất.

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây

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&

nhập mô tả hình ảnh ở đây

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

Bức tranh Hindenburg trông khá nhảm nhí, nhưng những bức khác tôi thích.

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
Dude, những người nhìn mát mẻ.
MrZander

2
Ôi trời thật TUYỆT VỜI.
jdstankosky

4
Đợi đã, chuỗi của bạn ở đâu?
jdstankosky

1
Đây là yêu thích của tôi cho đến nay.
primo

4
+1 cho giao diện của người Cuba .
Ilmari Karonen

36

Con trăn

Mã hóa yêu cầu numpy , SciPyscikit-image .
Giải mã chỉ yêu cầu PIL .

Đây là một phương pháp dựa trên phép nội suy superpixel. Để bắt đầu, mỗi hình ảnh được chia thành 70 vùng có kích thước tương tự có màu tương tự. Ví dụ: ảnh phong cảnh được chia theo cách sau:

nhập mô tả hình ảnh ở đây

Trọng tâm của từng khu vực được đặt (đến điểm raster gần nhất trên lưới chứa không quá 402 điểm), cũng như màu trung bình (từ bảng màu 216) và mỗi vùng trong số này được mã hóa thành một số từ 0 đến 86832 , có khả năng được lưu trữ trong 2,5 ký tự ascii có thể in được (thực tế là 2.497 , chỉ còn đủ chỗ để mã hóa cho một bit thang độ xám).

Nếu bạn chú ý, bạn có thể nhận thấy rằng 140 / 2.5 = 56 khu vực chứ không phải 70 như tôi đã nêu trước đó. Tuy nhiên, lưu ý rằng mỗi khu vực này là một đối tượng duy nhất, có thể so sánh, có thể được liệt kê theo bất kỳ thứ tự nào. Do đó, chúng ta có thể sử dụng hoán vị của 56 vùng đầu tiên để mã hóa cho 14 vùng còn lại , cũng như có một vài bit còn lại để lưu trữ tỷ lệ khung hình.

Cụ thể hơn, mỗi trong số 14 vùng bổ sung được chuyển đổi thành một số và sau đó mỗi số này được nối với nhau (nhân giá trị hiện tại với 86832 và thêm số tiếp theo). Số (khổng lồ) này sau đó được chuyển đổi thành hoán vị trên 56 đối tượng.

Ví dụ:

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)

sẽ xuất ra:

[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

Hoán vị kết quả sau đó được áp dụng cho 56 khu vực ban đầu . Số gốc (và do đó 14 vùng bổ sung ) cũng có thể được trích xuất bằng cách chuyển đổi hoán vị của 56 vùng được mã hóa thành biểu diễn số của nó.

Khi --greyscaletùy chọn được sử dụng với bộ mã hóa, 94 vùng được sử dụng thay thế (tách 70 , 24 ), với 558 điểm raster và 16 màu xám.

Khi giải mã, mỗi vùng trong số này được coi là hình nón 3D kéo dài vô tận, với đỉnh của nó ở tâm của vùng, như được nhìn từ trên xuống (còn gọi là Biểu đồ Voronoi). Các đường viền sau đó được trộn với nhau để tạo ra sản phẩm cuối cùng.

Những cải tiến trong tương lai

  1. Kích thước của Mona Lisa hơi khác một chút, do cách tôi lưu trữ tỷ lệ khung hình. Tôi sẽ cần sử dụng một hệ thống khác. Đã sửa, bằng cách giả sử rằng tỷ lệ khung hình gốc nằm ở đâu đó trong khoảng từ 1:21 đến 21: 1, mà tôi nghĩ là một giả định hợp lý.
  2. Hindenburg có thể được cải thiện rất nhiều. Bảng màu tôi đang sử dụng chỉ có 6 màu xám. Nếu tôi giới thiệu chế độ chỉ có thang độ xám, tôi có thể sử dụng thông tin bổ sung để tăng độ sâu màu, số vùng, số điểm raster hoặc bất kỳ kết hợp nào của cả ba. Tôi đã thêm một --greyscaletùy chọn vào bộ mã hóa, cả ba đều.
  3. Hình dạng 2d có thể sẽ trông tốt hơn với tắt pha trộn. Tôi có thể sẽ thêm một lá cờ cho điều đó. Đã thêm tùy chọn bộ mã hóa để kiểm soát tỷ lệ phân đoạn và tùy chọn bộ giải mã để tắt pha trộn.
  4. Vui hơn với tổ hợp. 56! thực sự đủ lớn để lưu trữ 15 vùng bổ sung và 15! là đủ lớn để lưu trữ thêm 2 cho tổng số 73 . Nhưng xin chờ chút nữa! Việc phân vùng của 73 đối tượng này cũng có thể được sử dụng để lưu trữ thêm thông tin. Ví dụ, có 73 chọn 56 cách để lựa chọn ban đầu 56 vùng, và sau đó 17 chọn 15 cách để lựa chọn tiếp theo 15 . Tổng cộng có 2403922132944423072 phân vùng, đủ lớn để lưu trữ thêm 3 vùng với tổng số 76. Tôi cần đưa ra một cách thông minh để đánh số duy nhất tất cả các phân vùng 73 thành các nhóm 56 , 15 , 2 ... và trở lại . Có lẽ không thực tế, nhưng một vấn đề thú vị để suy nghĩ.

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

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây


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

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Cái thứ hai được mã hóa với --greyscaletùy chọn.


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

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Được mã hóa với --greyscaletùy chọn.


.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

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Được mã hóa với --ratio 60, và được giải mã với --no-blendingcác tùy chọn.


mã hóa

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
Điều đó không có gì đáng kinh ngạc
lochok

Phiên bản màu của Mona Lisa trông giống như một trong những bộ ngực của cô ấy xuất hiện. Bỏ qua một bên, điều này là không thể tin được.
jdstankosky

4
Sử dụng hoán vị để mã hóa dữ liệu bổ sung là khá thông minh.
Sir_Lagsalot

Thực sự thực sự tuyệt vời. Bạn có thể làm một ý chính với 3 tập tin này? gist.github.com
rubik

2
@rubik thật là mất mát vô cùng, cũng như tất cả các giải pháp cho thử thách này;)
primo

17

PHP

OK, đưa tôi một lúc, nhưng đây rồi. Tất cả hình ảnh trong thang độ xám. Màu sắc mất quá nhiều bit để mã hóa cho phương thức của tôi: P


Mona Lisa
47 Màu Chuỗi đơn sắc
101 byte.

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

mona lisa


Hình dạng 2D
36 màu Chuỗi đơn sắc
105 byte.

oAAAAAAABMIDUAAEBAyoAAAAAgAwAAAAADYBtsAAAJIDbYAAAAA22AGwAAAAAGwAAAAAAAAAAKgAAAAAqgAAAACoAAAAAAAAAAAAAAAAA

2ngày 2dc


Hindenburg
62 Màu đơn sắc
112 ký tự.

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

bức ảnh ở đây nhập mô tả hình ảnh ở đây


Núi
63 Màu đơn sắc
122 ký tự.

qAE3VTkaIAKgqSFigAKoABgQEqAABuAgUQAGenRIBoUh2eqhABCee/2qSSAQntt/s2kJCQbf/bbaJgbWebzqsPZ7bZttwABTc3VAUFDbKqqpzY5uqpudnp5vZg

hình ảnh nhập mô tả hình ảnh ở đây


Phương pháp của tôi

Tôi mã hóa dòng bit của mình bằng một loại mã hóa base64. Trước khi nó được mã hóa thành văn bản có thể đọc được, đây là những gì xảy ra.

Tôi tải hình ảnh nguồn và thay đổi kích thước của nó thành chiều cao hoặc chiều rộng tối đa (tùy theo hướng, dọc / ngang) là 20 pixel.

Tiếp theo, tôi tô màu lại từng pixel của hình ảnh mới cho phù hợp gần nhất trên bảng màu thang độ 6 màu.

Sau khi xong, tôi tạo một chuỗi với mỗi màu pixel được biểu thị bằng các chữ cái [AF].

Sau đó, tôi tính toán phân phối của 6 chữ cái khác nhau trong chuỗi và chọn cây nhị phân được tối ưu hóa nhất để mã hóa dựa trên tần số chữ cái. Có 15 cây nhị phân có thể.

Tôi bắt đầu luồng bit của mình bằng một bit duy nhất, [1|0]tùy thuộc vào hình ảnh cao hay rộng. Sau đó tôi sử dụng 4 bit tiếp theo trong luồng để thông báo cho bộ giải mã nên sử dụng cây nhị phân nào để giải mã hình ảnh.

Những gì tiếp theo là dòng bit đại diện cho hình ảnh. Mỗi pixel và màu của nó được biểu thị bằng 2 hoặc 3 bit. Điều này cho phép tôi lưu trữ thông tin ít nhất 2 và tối đa 3 pixel cho mỗi ký tự ascii được in. Đây là một mẫu cây nhị phân 1110, được sử dụng bởi Mona Lisa:

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

Chữ E 00và F 10là màu phổ biến nhất trong Mona Lisa. A 010, B 011, C 110và D 111là ít thường xuyên nhất.

Cây nhị phân hoạt động như thế này: Đi từ bit này sang bit khác, 0có nghĩa là đi bên trái, 1có nghĩa là đi bên phải. Tiếp tục đi cho đến khi bạn đâm một chiếc lá trên cây, hoặc ngõ cụt. Chiếc lá bạn kết thúc là nhân vật bạn muốn.

Dù sao, tôi mã hóa nhị phân thành các ký tự base64. Khi giải mã chuỗi, quá trình được thực hiện ngược lại, gán tất cả các pixel cho màu thích hợp và sau đó hình ảnh được chia tỷ lệ gấp đôi kích thước được mã hóa (tối đa 40 pixel hoặc X hoặc Y, lớn hơn) và sau đó là ma trận tích chập áp dụng cho toàn bộ điều để làm mịn màu sắc.

Dù sao, đây là mã hiện tại: " liên kết pastebin "

Thật là xấu xí, nhưng nếu bạn thấy bất kỳ phòng nào để cải thiện, hãy cho tôi biết. Tôi đã hack nó cùng nhau như tôi muốn. TÔI TÌM HIỂU RẤT NHIỀU TỪ THÁCH THỨC NÀY. Cảm ơn OP đã đăng nó!


2
Chúng trông cực kỳ tốt khi xem xét bạn có bao nhiêu dung lượng lưu trữ chưa sử dụng (Mona Lisa chỉ sử dụng 60 bit từ 920 có sẵn!).
primo

Cảm ơn bạn, primo, tôi thực sự đánh giá cao điều đó. Tôi luôn ngưỡng mộ công việc của bạn, vì vậy nghe bạn nói rằng đó là khá tâng bốc!
jdstankosky

13

Nỗ lực đầu tiên của tôi. Điều này có chỗ để cải thiện. Tôi nghĩ rằng định dạng thực sự hoạt động, vấn đề là ở bộ mã hóa. Điều đó, và tôi đang thiếu các bit riêng lẻ từ đầu ra của tôi ... tệp của tôi (chất lượng cao hơn một chút ở đây) đã kết thúc ở 144 ký tự, khi đáng lẽ phải có một số còn lại. (và tôi thực sự muốn có - sự khác biệt giữa những điều này và những điều đáng chú ý). Mặc dù vậy, tôi đã học được, không bao giờ đánh giá quá cao 140 ký tự ...

Tôi đưa nó xuống một phiên bản sửa đổi của bảng màu RISC-OS - về cơ bản, vì tôi cần một bảng màu 32, và đó dường như là một nơi đủ tốt để bắt đầu. Điều này có thể làm với một số thay đổi quá tôi nghĩ. Bảng màu

Tôi chia nó thành các hình dạng sau: Hình dạng và chia hình ảnh thành các khối bảng màu (trong trường hợp này là 2x2 pixel) của màu trước và sau.

Các kết quả:

Sau đây là các tweet, bản gốc và cách giải mã tweet

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

Hindeberg Chân sau của tôi

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

núi Núi của tôi

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

Hình dạng Hình của tôi

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

nàng mô na Li Sa Mỏ Lisa

Tôi biết màu sắc là sai, nhưng tôi thực sự thích Monalisa. Nếu tôi xóa mờ (điều này sẽ không quá khó), đó là một ấn tượng lập thể hợp lý: p

Tôi cần phải làm việc

  • Thêm phát hiện hình dạng
  • Một thuật toán "khác biệt" màu sắc tốt hơn
  • Tìm ra nơi mà các bit bị mất của tôi đã đi

Tôi sẽ cho nó thêm một số công việc sau này để cố gắng sửa chúng và cải thiện bộ mã hóa. Những nhân vật thêm khoảng 20 hoặc hơn tạo ra sự khác biệt lớn. Tôi muốn họ trở lại.

Bảng màu và nguồn C # có trên https://dl.dropboxusercontent.com/u/46145976/Base96.zip - mặc dù, trong nhận thức muộn, có thể không hoạt động hoàn hảo khi chạy riêng (vì không gian trong các đối số cho các chương trình không đi như vậy tốt).

Bộ mã hóa mất ít hơn vài giây trên máy khá trung bình của tôi.


11
Anh bạn Chúng trông đẹp hơn bất kỳ tác phẩm nghệ thuật đương đại nào tôi từng thấy trong một bộ sưu tập ... Bạn nên tạo ra những bản in lớn của chúng và bán chúng!
jdstankosky

1
Có vẻ như tôi cần lấy hộp mực ra khỏi Atari của mình và cắm lại. Tôi thích nó.
undergroundmonorail

13

Tôi đã từ bỏ việc cố gắng giữ màu và chuyển sang màu đen và trắng, vì mọi thứ tôi đã thử với màu sắc đều không thể nhận ra.

Về cơ bản, tất cả những gì nó làm là chia pixel thành 3 phần gần bằng nhau: đen, xám và trắng. Nó cũng không giữ kích thước.

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 HindenburgCompced

nàng mô na Li Sa

=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

Nàng mô na Li Sa MonaLisaCompced

Núi

+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

Núi NúiCompced

Hình dạng

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:

Hình dạng Hình dạng nén

Đây là chương trình. python compress.py -c img.pngnén img.pngvà in tweet.

python compress.py -d img.pnglấy tweet từ stdin và lưu hình ảnh vào 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')

Lol, +1 cho tỷ lệ khung hình không bị hạn chế.
jdstankosky

7

Đóng góp khiêm tốn của tôi trong R:

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

Ý tưởng chỉ đơn giản là giảm raster (tệp phải ở dạng png) thành ma trận có số ô thấp hơn 140, các tweet sau đó là một chuỗi màu (trong 64 màu) trước hai ký tự chỉ ra số lượng hàng và các cột của raster.

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

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây


4

Không phải là một giải pháp hoàn chỉnh, chỉ cần đưa phương pháp ra khỏi đó. (Matlab)

Tôi đã sử dụng bảng màu 16 và vị trí 40 để tạo sơ đồ voronoi có trọng số . Sử dụng thuật toán di truyền và thuật toán leo đồi đơn giản để phù hợp với hình ảnh.

Album với hình ảnh gốc và tôi cũng có phiên bản 16 byte với 4 màu và vị trí cố định ở đó. :)

nhập mô tả hình ảnh ở đây

(Tôi có thể thay đổi kích thước hình ảnh ở đây không?)


1
Bạn có thể đăng những hình ảnh khác? Tôi muốn xem những gì họ trông như thế nào với nén này!
jdstankosky

@jdstankosky Xin lỗi, tôi không thể làm điều đó ngay bây giờ. Có lẽ một thời gian sau ...
Randomra

4

C #

Cập nhật - Phiên bản 2


Tôi đã thực hiện một nỗ lực khác, bây giờ sử dụng MagickImage.NET ( https://magick.codeplex.com/ ) để mã hóa dữ liệu JPEG, tôi cũng đã viết một số mã cơ bản để xử lý tốt hơn dữ liệu tiêu đề JPEG (như đề xuất của primo), tôi cũng vậy đã sử dụng GuassianBlur trên đầu ra giúp làm mềm một số nén JPEG. Khi phiên bản mới hoạt động tốt hơn, tôi đã cập nhật bài viết của mình để phản ánh phương pháp mới.


phương pháp


Tôi đã thử một cái gì đó độc đáo (hy vọng), thay vì cố gắng điều khiển độ sâu màu hoặc nhận dạng cạnh hoặc cố gắng sử dụng các cách khác nhau để giảm kích thước hình ảnh bản thân tôi đã sử dụng thuật toán JPEG ở mức nén tối đa trên các phiên bản thu nhỏ các hình ảnh, sau đó bằng cách loại bỏ mọi thứ trừ "StartOfScan" ( http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure ) và một vài yếu tố tiêu đề chính tôi có thể giảm kích thước xuống mức chấp nhận được. Kết quả thực sự ấn tượng với 140 ký tự, mang lại cho tôi sự tôn trọng mới được tìm thấy đối với JPEG:

Hindenburg

Hindenburg Nguyên

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

Núi

Núi Nguyên

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

nàng mô na Li Sa

nàng mô na Li Sa Nguyên

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

Hình dạng

Hình dạng Nguyên

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



Phiên bản 2 - http://pastebin.com/Tgr8XZUQ

Tôi thực sự bắt đầu nhớ ReSharper + Tôi có rất nhiều thứ cần cải thiện, vẫn còn phân bổ mã hóa cứng đang diễn ra ở đây, thật thú vị để giải quyết vấn đề (hãy nhớ rằng bạn cần MagickImage dll để chạy nó trong VS)


Bản gốc (Không dùng nữa) - http://pastebin.com/BDPT0BKT

Vẫn còn một chút lộn xộn.


"Đây thực sự là một mớ hỗn độn ngay bây giờ", tôi sẽ đồng ý với điều đó - chắc chắn phải có cách nào tốt hơn để tạo tiêu đề đó? Nhưng tôi cho rằng kết quả là điều quan trọng nhất. +1
primo

1

Con trăn 3

phương pháp

Những gì chương trình làm đầu tiên là thu nhỏ hình ảnh, giảm đáng kể kích thước của nó.

Thứ hai, nó chuyển đổi các giá trị rgb thành nhị phân và loại bỏ một vài chữ số cuối cùng.

Sau đó, nó chuyển đổi dữ liệu cơ sở 2 thành cơ sở 10, nơi nó thêm kích thước của hình ảnh.

Sau đó, nó chuyển đổi dữ liệu trong cơ sở 10 thành cơ sở 95, sử dụng tất cả các ascii tôi có thể tìm thấy. Tuy nhiên, tôi không thể sử dụng / x01 và tương tự vì khả năng phủ nhận chức năng đã ghi ra tệp văn bản.

Và (để thêm sự mơ hồ), hàm giải mã thực hiện ngược lại.

nén

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

Tiếng hét

Tiếng hét1 Tiếng thét2

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

nàng mô na Li Sa

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)

Hình cầu

Hình cầu 1 Hình cầu 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\
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.