Bir Yılanla Bir Resim Çiz


28

Yalnızca sola, sağa ya da dümdüz gidebilen, kesişemeyen ve görüntüdeki piksellerin ızgarası gibi dikdörtgen bir ızgarayı doldurması gereken sürekli 2 boyutlu bir yol hayal edin. Bu tür yola yılan diyeceğiz .

Yılan örneği

Bu genişletilmiş örnek, kırmızı renkte başlayan ve her adımda mor olana kadar renk tonunda yaklaşık% 2 artış gösteren 10 x 4 ızgaradaki bir yılan yolunu gösterir. (Siyah çizgiler yalnızca aldığı yönü vurgulamak içindir.)

Hedef

Bu popülerlik yarışmasındaki amaç, rengi sürekli küçük miktarlarda değişen tek bir yılan kullanarak belirli bir görüntüyü yeniden oluşturmaya çalışan bir algoritma yazmaktır.

Programınız, tolerans dahil, 0 ile 1 arasında bir kayar nokta değerinin yanı sıra herhangi bir boyutta gerçek renkli bir görüntü almalıdır .

Tolerans , yılan renginin her piksel büyüklüğündeki adımda değişmesine izin verilen maksimum miktarı tanımlar. Bir yerleştirilmiş iken bu RGB noktaları arasındaki Euclidean mesafesinin iki RGB renk arasındaki mesafeyi olacak RGB renk küp . Mesafe normalize olur, böylece maksimum mesafe 1 olur ve minimum mesafe 0 olur.

Renk mesafesi sözde kodu: (Tüm giriş değerlerinin aralıktaki tam sayı olduğunu varsayar [0, 255]; çıktı normalleştirilir.)

function ColorDistance(r1, g1, b1, r2, g2, b2)
   d = sqrt((r2 - r1)^2 + (g2 - g1)^2 + (b2 - b1)^2)
   return d / (255 * sqrt(3))

Bu işlevi yılanın mevcut renginde ve başka bir renkte çağırmanın sonucu verilen toleranstan büyükse, yılan o renkte değişmeyebilir.

Tercih ederseniz, farklı bir renk mesafesi işlevi kullanabilirsiniz. Http://en.wikipedia.org/wiki/Color_difference adresinde listelenenler gibi doğru ve iyi belgelenmiş bir şey olmalıdır . Ayrıca içeride olması için normalleştirmeniz gerekir [0, 1], yani mümkün olan maksimum mesafe 1 ve minimum 0 olmalıdır. Farklı bir mesafe ölçümü kullanıyorsanız cevabınızı bize bildirin.

Test görüntüleri

Elbette çıktı resimlerinizi de yayınlamanız gerekir (ve isterseniz yılanın yarattığı animasyonlar bile). Farklı düşük toleranslar kullanarak (belki de yaklaşık 0.005-0.03) bu görüntülerin çeşitli gönderilmesini öneririm.

mandril Mona Lisa Büyük Dalga Lena rastgele renkler, gradyanlar misc (Büyük Büyük Dalga)

Kazanma Kriterleri

Belirtildiği gibi, bu bir popülerlik yarışması. Oylanan en yüksek cevap kazanacak. Giriş görüntülerinin en doğru ve estetik açıdan hoş "yılan yolu" tasvirini sağlayan cevaplar oylanmalıdır.

Gerçek yılan olmayan görüntüleri kötü niyetli olarak gönderdiği tespit edilen kullanıcılar sonsuza kadar diskalifiye olur.

notlar

  • Sadece bir yılan yolu kullanılabilir ve aynı piksele iki kez dokunmadan görüntüyü tamamen doldurması gerekir.
  • Yılan, görüntünün herhangi bir yerinde başlayabilir ve bitebilir.
  • Yılan herhangi bir renkle başlayabilir.
  • Yılan görüntünün sınırlarında kalmalıdır. Sınırlar döngüsel değil.
  • Yılan aynı anda çapraz veya birden fazla piksele hareket edemez.

14
Cidden, onlardan birini kumlamadan geçirmeden, 16 gün içinde gerçekten iyi olan 14 yarışmayı (biri şimdiki zamanın en iyisi olanlardan biri) yayınlamayı nasıl başardınız? Büyük şerefler, PPCG'nin senin gibi daha fazla insana ihtiyacı var! ;)
Martin Ender

@ MartinBüttner Emin değil. Bana sadece doğal olarak geliyorlar :) Adil olmak gerekirse, sandbox'a yaptığım bir soru çok iyi karşılanmadı: meta.codegolf.stackexchange.com/a/1820/26997
Calvin'in Hobileri

Çözümümün sonsuz bir döngüde sıkışıp kaldığından emin değilim, ya da gerçekten çok uzun zaman alıyor. Ve bu sadece 80x80 görüntü!
Doorknob

1
Ah benim ... bu gerçekten eğlenceli görünüyor.
cjfaure

1
@belisarius Tam olarak orijinal görüntü olması gerektiğini sanmıyorum, sadece bir kopya kadar.
Julurous

Yanıtlar:


24

piton

Yılan seyahat ederken renk değişikliklerini en aza indirmek için dinamik bir yol oluşturuyorum. İşte bazı resimler:

tolerans = 0,01

Mona Lisa 0.01 toleransı Mandrill 0.01 toleransı

Yukarıdaki görüntüler için döngüsel renk yolları (maviden kırmızıya, tekrar ettikçe daha yeşil hale gelir):

Mona Lisa Yılan Yolu Döngüsel Renklerde Döngüsel renklerde Mandrill'di yılan yolu

Yol, başlangıçtaki bir yolla başlanarak oluşturulur, daha sonra görüntü dolana kadar 2x2 lup eklenir. Bu yöntemin avantajı, halkaların yol üzerinde herhangi bir yere eklenebilmesidir, bu nedenle kendinizi bir köşeye boyayamaz ve istediğiniz yolu oluşturmak için daha fazla özgürlüğe sahip olamazsınız. Geçerli yola bitişik olası döngüleri izliyorum ve bunları döngü boyunca renk değişimi ağırlıklı bir yığın halinde saklıyorum. Daha sonra en az renk değişikliği ile döngüyü açtım ve yola ekledim ve görüntü dolana kadar tekrarladım.

Aslında sadece döngüleri izliyorum (koddaki 'DetourBlock'), sonra yolu yeniden yapılandırdım; Tek bir genişlik / yükseklik için bazı özel durumlar olduğundan ve rekonstrüksiyon yönteminde hata ayıklamak için birkaç saat harcadığım için bu bir hataydı. Oh iyi.

Yol oluşturma metriğinin ayarlanması gerekiyor ve ayrıca daha iyi renklendirme için bir fikrim var, ancak ilk önce oldukça iyi çalıştığından bunu başaracağımı düşündüm. Bunun dışında, bazı sabit yollarda daha iyi görünen:

Çeşitli Öğeler 0.01 Tolerans

İşte korkunç kodlama alışkanlığım için özür dileyen Python kodu:

# snakedraw.py
# Image library: Pillow
# Would like to animate with matplotlib... (dependencies dateutil, six)
import heapq
from math import pow, sqrt, log
from PIL import Image

tolerance = 0.001
imageList = [ "lena.png", "MonaLisa.png", "Mandrill.png", "smallGreatWave.png", "largeGreatWave.png", "random.png"]

# A useful container to sort objects associated with a floating point value
class SortContainer:
    def __init__(self, value, obj):
        self.fvalue = float(value)
        self.obj = obj
    def __float__(self):
        return float(self.fvalue)
    def __lt__(self, other):
        return self.fvalue < float(other)
    def __eq__(self, other):
        return self.fvalue == float(other)
    def __gt__(self, other):
        return self.fvalue > float(other)

# Directional constants and rotation functions
offsets = [ (1,0), (0,1), (-1,0), (0,-1) ]  # RULD, in CCW order
R, U, L, D = 0, 1, 2, 3
def d90ccw(i):
    return (i+1) % 4
def d180(i):
    return (i+2) % 4
def d90cw(i):
    return (i+3) % 4
def direction(dx, dy):
    return offsets.index((dx,dy))


# Standard color metric: Euclidean distance in the RGB cube. Distance between opposite corners normalized to 1.
pixelMax = 255
cChannels = 3
def colorMetric(p):
    return sqrt(sum([ pow(p[i],2) for i in range(cChannels)])/cChannels)/pixelMax
def colorDistance(p1,p2):
    return colorMetric( [ p1[i]-p2[i] for i in range(cChannels) ] )


# Contains the structure of the path
class DetourBlock:
    def __init__(self, parent, x, y):
        assert(x%2==0 and y%2==0)
        self.x = x
        self.y = y
        self.parent = None
        self.neighbors = [None, None, None, None]
    def getdir(A, B):
        dx = (B.x - A.x)//2
        dy = (B.y - A.y)//2
        return direction(dx, dy)

class ImageTracer:
    def __init__(self, imgName):

        self.imgName = imgName
        img = Image.open(imgName)
        img = img.convert(mode="RGB")       # needed for BW images
        self.srcImg = [ [ [ float(c) for c in img.getpixel( (x,y) ) ] for y in range(img.size[1]) ] for x in range(img.size[0])]
        self.srcX = img.size[0]
        self.srcY = img.size[1]

        # Set up infrastructure
        self.DetourGrid = [ [ DetourBlock(None, 2*x, 2*y) \
                    for y in range((self.srcY+1)//2)] \
                    for x in range((self.srcX+1)//2)]
        self.dgX = len(self.DetourGrid)
        self.dgY = len(self.DetourGrid[0])
        self.DetourOptions = list()    # heap!
        self.DetourStart = None
        self.initPath()

    def initPath(self):
        print("Initializing")
        if not self.srcX%2 and not self.srcY%2:
            self.AssignToPath(None, self.DetourGrid[0][0])
            self.DetourStart = self.DetourGrid[0][0]
        lastDB = None
        if self.srcX%2:     # right edge initial path
            self.DetourStart = self.DetourGrid[-1][0]
            for i in range(self.dgY):
                nextDB = self.DetourGrid[-1][i]
                self.AssignToPath(lastDB, nextDB)
                lastDB = nextDB
        if self.srcY%2:     # bottom edge initial path
            if not self.srcX%2:
                self.DetourStart = self.DetourGrid[-1][-1]
            for i in reversed(range(self.dgX-(self.srcX%2))):          # loop condition keeps the path contiguous and won't add corner again
                nextDB =  self.DetourGrid[i][-1]
                self.AssignToPath(lastDB, nextDB)
                lastDB = nextDB

    # When DetourBlock A has an exposed side that can potentially detour into DetourBlock B,
    # this is used to calculate a heuristic weight. Lower weights are better, they minimize the color distance
    # between pixels connected by the snake path
    def CostBlock(self, A, B):
        # Weight the block detour based on [connections made - connections broken]
        dx = (B.x - A.x)//2
        dy = (B.y - A.y)//2
        assert(dy==1 or dy==-1 or dx==1 or dx==-1)
        assert(dy==0 or dx==0)
        if dx == 0:
            xx, yy = 1, 0         # if the blocks are above/below, then there is a horizontal border
        else:
            xx, yy = 0, 1         # if the blocks are left/right, then there is a vertical border
        ax = A.x + (dx+1)//2
        ay = A.y + (dy+1)//2 
        bx = B.x + (1-dx)//2
        by = B.y + (1-dy)//2
        fmtImg = self.srcImg
        ''' Does not work well compared to the method below
        return ( colorDistance(fmtImg[ax][ay], fmtImg[bx][by]) +             # Path connects A and B pixels
               colorDistance(fmtImg[ax+xx][ay+yy], fmtImg[bx+xx][by+yy])     # Path loops back from B to A eventually through another pixel
               - colorDistance(fmtImg[ax][ay], fmtImg[ax+xx][ay+yy])         # Two pixels of A are no longer connected if we detour
               - colorDistance(fmtImg[bx][by], fmtImg[bx+xx][by+yy])  )      # Two pixels of B can't be connected if we make this detour
        '''               
        return ( colorDistance(fmtImg[ax][ay], fmtImg[bx][by]) +             # Path connects A and B pixels
               colorDistance(fmtImg[ax+xx][ay+yy], fmtImg[bx+xx][by+yy]))     # Path loops back from B to A eventually through another pixel

    # Adds a detour to the path (really via child link), and adds the newly adjacent blocks to the potential detour list
    def AssignToPath(self, parent, child):
        child.parent = parent
        if parent is not None:
            d = parent.getdir(child)
            parent.neighbors[d] = child
            child.neighbors[d180(d)] = parent
        for (i,j) in offsets:
            x = int(child.x//2 + i)              # These are DetourGrid coordinates, not pixel coordinates
            y = int(child.y//2 + j)
            if x < 0 or x >= self.dgX-(self.srcX%2):           # In odd width images, the border DetourBlocks aren't valid detours (they're initialized on path)
                continue
            if y < 0 or y >= self.dgY-(self.srcY%2):
                continue
            neighbor = self.DetourGrid[x][y]
            if neighbor.parent is None:
                heapq.heappush(self.DetourOptions, SortContainer(self.CostBlock(child, neighbor), (child, neighbor)) )

    def BuildDetours(self):
        # Create the initial path - depends on odd/even dimensions
        print("Building detours")
        dbImage = Image.new("RGB", (self.dgX, self.dgY), 0)
        # We already have our initial queue of detour choices. Make the best choice and repeat until the whole path is built.
        while len(self.DetourOptions) > 0:
            sc = heapq.heappop(self.DetourOptions)       # Pop the path choice with lowest cost
            parent, child = sc.obj
            if child.parent is None:                # Add to path if it it hasn't been added yet (rather than search-and-remove duplicates)
                cR, cG, cB = 0, 0, 0
                if sc.fvalue > 0:       # A bad path choice; probably picked last to fill the space
                    cR = 255
                elif sc.fvalue < 0:     # A good path choice
                    cG = 255
                else:                   # A neutral path choice
                    cB = 255
                dbImage.putpixel( (child.x//2,child.y//2), (cR, cG, cB) )
                self.AssignToPath(parent, child)
        dbImage.save("choices_" + self.imgName)

    # Reconstructing the path was a bad idea. Countless hard-to-find bugs!
    def ReconstructSnake(self):
        # Build snake from the DetourBlocks.
        print("Reconstructing path")
        self.path = []
        xi,yi,d = self.DetourStart.x, self.DetourStart.y, U   # good start? Okay as long as CCW
        x,y = xi,yi
        while True:
            self.path.append((x,y))
            db = self.DetourGrid[x//2][y//2]                     # What block do we occupy?
            if db.neighbors[d90ccw(d)] is None:                  # Is there a detour on my right? (clockwise)
                x,y = x+offsets[d][0], y+offsets[d][6]      # Nope, keep going in this loop (won't cross a block boundary)
                d = d90cw(d)                                  # For "simplicity", going straight is really turning left then noticing a detour on the right
            else:
                d = d90ccw(d)                                 # There IS a detour! Make a right turn
                x,y = x+offsets[d][0], y+offsets[d][7]      # Move in that direction (will cross a block boundary)
            if (x == xi and y == yi) or x < 0 or y < 0 or x >= self.srcX or y >= self.srcY:                         # Back to the starting point! We're done!
                break
        print("Retracing path length =", len(self.path))       # should = Width * Height

        # Trace the actual snake path
        pathImage = Image.new("RGB", (self.srcX, self.srcY), 0)
        cR, cG, cB = 0,0,128
        for (x,y) in self.path:
            if x >= self.srcX or y >= self.srcY:
                break
            if pathImage.getpixel((x,y)) != (0,0,0):
                print("LOOPBACK!", x, y)
            pathImage.putpixel( (x,y), (cR, cG, cB) )
            cR = (cR + 2) % pixelMax
            if cR == 0:
                cG = (cG + 4) % pixelMax
        pathImage.save("path_" + self.imgName)

    def ColorizeSnake(self):
        #Simple colorization of path
        traceImage = Image.new("RGB", (self.srcX, self.srcY), 0)
        print("Colorizing path")
        color = ()
        lastcolor = self.srcImg[self.path[0][0]][self.path[0][8]]
        for i in range(len(self.path)):
            v = [ self.srcImg[self.path[i][0]][self.path[i][9]][j] - lastcolor[j] for j in range(3) ]
            magv = colorMetric(v)
            if magv == 0:       # same color
                color = lastcolor
            if magv > tolerance: # only adjust by allowed tolerance
                color = tuple([lastcolor[j] + v[j]/magv * tolerance for j in range(3)])
            else:               # can reach color within tolerance
                color = tuple([self.srcImg[self.path[i][0]][self.path[i][10]][j] for j in range(3)])
            lastcolor = color
            traceImage.putpixel( (self.path[i][0], self.path[i][11]), tuple([int(color[j]) for j in range(3)]) )
        traceImage.save("snaked_" + self.imgName)


for imgName in imageList:
    it = ImageTracer(imgName)
    it.BuildDetours()
    it.ReconstructSnake()
    it.ColorizeSnake()

Ve 0.001'in çok düşük bir toleransında bazı görüntüler :

Büyük Dalga 0.001 toleransı Mona Lisa 0.001 toleransı Lena 0.001 toleransı

Ve ayrıca harika dalga yolu, çünkü temiz:

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

DÜZENLE

Yol oluşturma, bitişik piksellerin arasındaki renk mesafelerinin toplamını en aza indirmektense, bitişik blokların ortalama renkleri arasındaki renk mesafesini en aza indirirken daha iyi görünür. Ayrıca, herhangi iki tolerans uyumlu yılan yolunun renklerini ortalayabilir ve başka bir tolerans uyumlu yılan yolunu bulabilirsin. Böylece yolu hem yollardan hem de ortalamalarından geçiriyorum, bu da birçok eseri düzeltiyor. Zombie Lena ve Scary Hands Mona çok daha iyi görünüyor. Son sürümler:

Tolerans 0.01 :

Final Mona 0.01 Final Lena 0.01

Son Büyük Dalga 0.01

Tolerans 0.001 :

Son Mona Final Lena

Son Büyük Dalga


4
Henüz en iyisi! Büyük Dalga'nın nasıl göründüğünü seviyorum!
Calvin'in Hobileri

Bu meydan okumaya verdiğim cevabı seviyorum python heh
Albert Renshaw

17

Java

Programım, Hilbert eğrisini oluşturana benzer bir algoritma kullanarak, verilen genişlik ve yükseklik için bir yılan yolu oluşturur.

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

(küçük oyun: yukarıdaki resimde, yılan sol üst köşede başlar. Nerede bittiğini bulabilir misin? İyi şanslar :)

İşte çeşitli tolerans değerleri için sonuçlar:

Tolerans = 0.01

tolerans = 0.01

Tolerans = 0.05

tolerans = 0.05

Tolerans = 0,1

tolerans = 0.01

Tolerans = 0.01

Dalga

4x4 piksel blok ve görünür yol ile

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

Yılan yolunu hesaplama

Bir yılan yolu, çift boyutlu bir tamsayı dizisinde saklanır. Yılan her zaman ızgaraya sol üst köşeden girer. Programımın belirli bir yılan yolu üzerinde yapabileceği 4 temel işlem var:

  • genişlik 1 veya yükseklik 1 olan bir ızgara için yeni bir yılan yolu oluşturun. Yol, duruma bağlı olarak, yalnızca sola veya sağa veya sola doğru giden basit bir çizgidir.

  • yukarıdan soldan sağa bir yılan yolu ekleyerek, sonra da ızgarayı yansıtarak ızgara yüksekliğini artırın (yılan her zaman sol üst köşeden ızgaraya girmelidir)

  • Soldan yukarıdan aşağıya bir yılan yolu ekleyerek, sonra ızgarayı çevirerek ızgara genişliğini arttırın (yılan her zaman sol üst köşeden ızgaraya girmelidir)

  • "Hilbert tarzı" algoritması kullanarak ızgara boyutunu iki katına çıkarın (aşağıdaki açıklamaya bakın)

Bu atomik işlemlerin bir serisini kullanarak, program herhangi bir boyutta bir yılan yolu oluşturabilmektedir.

Aşağıdaki kod, belirli bir genişlik ve yükseklik elde etmek için hangi işlemlerin gerekli olacağını (ters sırada) hesaplar. Hesaplandıktan sonra, beklenen boyuttaki bir yılan yolunu alana kadar eylemler birer birer yürütülür.

enum Action { ADD_LINE_TOP, ADD_LINE_LEFT, DOUBLE_SIZE, CREATE};

public static int [][] build(int width, int height) {
    List<Action> actions = new ArrayList<Action>();
    while (height>1 && width>1) {
        if (height % 2 == 1) {
            height--;
            actions.add(Action.ADD_LINE_TOP);
        }
        if (width % 2 == 1) {
            width--;                
            actions.add(Action.ADD_LINE_LEFT);
        }
        if (height%2 == 0 && width%2 == 0) {
            actions.add(Action.DOUBLE_SIZE);
            height /= 2;
            width /= 2;
        }
    }
    actions.add(Action.CREATE);
    Collections.reverse(actions);
    int [][] tab = null;
    for (Action action : actions) {
        // do the stuff
    }

Yılan yolu boyutunu iki katına çıkarmak:

Boyutu iki katına çıkaran algoritma şu şekilde çalışır:

RIGHT ve BOTTOM ile bağlantılı olan bu düğümü düşünün. Onun boyutunu iki katına çıkarmak istiyorum.

 +-
 |

Boyutunu ikiye katlamanın ve aynı çıkışları korumanın 2 yolu var (sağ ve alt)

 +-+- 
 |
 +-+
   |

veya

+-+
| |
+ +-
|

Hangisini seçeceğimi belirlemek için, her bir düğüm yönü için çıkış kapısının sola veya sağa veya yukarı / aşağı kaydırılıp kaydırılmadığını belirten bir "shift" değeri kullanmam gerekir. Yılanın yaptığı gibi yolu izliyorum ve yoldaki öteleme değerini güncelle. Öteleme değeri, bir sonraki adım için hangi genişletilmiş bloğu kullanmam gerektiğini belirler.


3
Hilbert eğrisi için +1. Bu oldukça doğal görünüyor ama kodunuzu gönderebilirseniz çok iyi olurdu.
izlin

@izlin Çok fazla kod var - Bazı bölümleri göndermeye çalışacağım
Arnaud

1
@SuperChafouin 30k karakterden az ise, lütfen hepsini gönderin. SE otomatik olarak bir kaydırma çubuğu ekleyecektir.
Martin Ender

Biraz hızlı ve kirli olan kodumu
Arnaud

3
Pes ediyorum, nerede bitiyor?
TMH

10

piton

İşte işleri başlatmak için çok basit bir algoritma. Görüntünün sol üst köşesinde başlar ve saat yönünde içeri doğru spiraller geçirerek rengi tolerans dahilinde kalırken bir sonraki pikselin rengine mümkün olduğunca yakın hale getirir.

import Image

def colorDist(c1, c2): #not normalized
    return (sum((c2[i] - c1[i])**2 for i in range(3)))**0.5

def closestColor(current, goal, tolerance):
    tolerance *= 255 * 3**0.5
    d = colorDist(current, goal)
    if d > tolerance: #return closest color in range
        #due to float rounding this may be slightly outside of tolerance range
        return tuple(int(current[i] + tolerance * (goal[i] - current[i]) / d) for i in range(3))
    else:
        return goal

imgName = 'lena.png'
tolerance = 0.03

print 'Starting %s at %.03f tolerance.' % (imgName, tolerance)

img = Image.open(imgName).convert('RGB')

imgData = img.load()
out = Image.new('RGB', img.size)
outData = out.load()

x = y = 0
c = imgData[x, y]
traversed = []
state = 'right'

updateStep = 1000

while len(traversed) < img.size[0] * img.size[1]:
    if len(traversed) > updateStep and len(traversed) % updateStep == 0:
        print '%.02f%% complete' % (100 * len(traversed) / float(img.size[0] * img.size[1]))
    outData[x, y] = c
    traversed.append((x, y))
    oldX, oldY = x, y
    oldState = state
    if state == 'right':
        if x + 1 >= img.size[0] or (x + 1, y) in traversed:
            state = 'down'
            y += 1
        else:
            x += 1
    elif state == 'down':
        if y + 1 >= img.size[1] or (x, y + 1) in traversed:
            state = 'left'
            x -= 1
        else:
            y += 1
    elif state == 'left':
        if x - 1 < 0 or (x - 1, y) in traversed:
            state = 'up'
            y -= 1
        else:
            x -= 1
    elif state == 'up':
        if y - 1 < 0 or (x, y - 1) in traversed:
            state = 'right'
            x += 1
        else:
             y -= 1
    c = closestColor(c, imgData[x, y], tolerance)

out.save('%.03f%s' % (tolerance, imgName))
print '100% complete'

Daha büyük görüntüleri çalıştırmak bir veya iki dakika sürer, ancak spiral mantığın büyük ölçüde optimize edilebildiğinden eminim.

Sonuçlar

İlginçler ama muhteşem değiller. Şaşırtıcı bir şekilde, 0.1'in üzerindeki tolerans, oldukça doğru görünen sonuçlar verir.

0.03 toleransında Büyük Dalga:

0.03 toleransında Büyük Dalga

Mona Lisa, 0.02 toleransta:

Mona Lisa 0.02 toleransta

Lena, 0.03 toleransta, sonra 0.01, sonra 0.005, sonra 0.003:

Lena 0.03 toleransta Lena 0.01 toleransta 0.005 toleransında Lena [Lena 0,003 toleransta

0,1 toleransta, ardından 0,07, sonra 0,04, sonra 0,01'de çeşitli maddeler:

0.1 toleransta çeşitli şeyler 0,07 toleransta çeşitli şeyler 0.04 toleransta çeşitli şeyler 0.01 toleransta çeşitli şeyler


13
Python ile bir yılan programı yazmak meşru görünüyor.
Arnaud

10

kobra

@number float
use System.Drawing
class Program
    var source as Bitmap?
    var data as List<of uint8[]> = List<of uint8[]>()
    var canvas as List<of uint8[]> = List<of uint8[]>()
    var moves as int[] = @[0,1]
    var direction as bool = true
    var position as int[] = int[](0)
    var tolerance as float = 0f
    var color as uint8[] = uint8[](4)
    var rotated as bool = false
    var progress as int = 0
    def main
        args = CobraCore.commandLineArgs
        if args.count <> 3, throw Exception()
        .tolerance = float.parse(args[1])
        if .tolerance < 0 or .tolerance > 1, throw Exception()
        .source = Bitmap(args[2])
        .data = .toData(.source to !)
        .canvas = List<of uint8[]>()
        average = float[](4)
        for i in .data
            .canvas.add(uint8[](4))
            for n in 4, average[n] += i[n]/.source.height
        for n in 4, .color[n] = (average[n]/.source.width).round to uint8
        if .source.width % 2
            if .source.height % 2
                .position = @[0, .source.height-1]
                .update
                while .position[1] > 0, .up
                .right
            else
                .position = @[.source.width-1, .source.height-1]
                .update
                while .position[1] > 0, .up
                while .position[0] > 0, .left
                .down
        else
            if .source.height % 2
                .position = @[0,0]
                .update
            else
                .position = @[.source.width-1,0]
                .update
                while .position[0] > 0, .left
                .down
        .right
        .down
        while true
            if (1-.source.height%2)<.position[1]<.source.height-1
                if .moves[1]%2==0
                    if .direction, .down
                    else, .up
                else
                    if .moves[0]==2, .right
                    else, .left
            else
                .right
                if .progress == .data.count, break
                .right
                .right
                if .direction
                    .direction = false
                    .up
                else
                    .direction = true
                    .down
        image = .toBitmap(.canvas, .source.width, .source.height)
        if .rotated, image.rotateFlip(RotateFlipType.Rotate270FlipNone)
        image.save(args[2].split('.')[0]+'_snake.png')

    def right
        .position[0] += 1
        .moves = @[.moves[1], 0]
        .update

    def left
        .position[0] -= 1
        .moves = @[.moves[1], 2]
        .update

    def down
        .position[1] += 1
        .moves = @[.moves[1], 1]
        .update

    def up
        .position[1] -= 1
        .moves = @[.moves[1], 3]
        .update

    def update
        .progress += 1
        index = .position[0]+.position[1]*(.source.width)
        .canvas[index] = .closest(.color,.data[index])
        .color = .canvas[index]

    def closest(color1 as uint8[], color2 as uint8[]) as uint8[]
        d = .difference(color1, color2)
        if d > .tolerance
            output = uint8[](4)
            for i in 4, output[i] = (color1[i] + .tolerance * (color2[i] - _
            color1[i]) / d)to uint8
            return output
        else, return color2

    def difference(color1 as uint8[], color2 as uint8[]) as float
        d = ((color2[0]-color1[0])*(color2[0]-color1[0])+(color2[1]- _
        color1[1])*(color2[1]-color1[1])+(color2[2]-color1[2])*(color2[2]- _
        color1[2])+0f).sqrt
        return d / (255 * 3f.sqrt)

    def toData(image as Bitmap) as List<of uint8[]>
        rectangle = Rectangle(0, 0, image.width, image.height)
        data = image.lockBits(rectangle, System.Drawing.Imaging.ImageLockMode.ReadOnly, _
        image.pixelFormat) to !
        ptr = data.scan0
        bytes = uint8[](data.stride*image.height)
        System.Runtime.InteropServices.Marshal.copy(ptr, bytes, 0, _
        data.stride*image.height)
        pfs = Image.getPixelFormatSize(data.pixelFormat)//8
        pixels = List<of uint8[]>()
        for y in image.height, for x in image.width
            position = (y * data.stride) + (x * pfs)
            red, green, blue, alpha = bytes[position+2], bytes[position+1], _
            bytes[position], if(pfs==4, bytes[position+3], 255u8)
            pixels.add(@[red, green, blue, alpha])
        image.unlockBits(data)
        return pixels

    def toBitmap(pixels as List<of uint8[]>, width as int, height as int) as Bitmap
        image = Bitmap(width, height, Imaging.PixelFormat.Format32bppArgb)
        rectangle = Rectangle(0, 0, image.width, image.height)
        data = image.lockBits(rectangle, System.Drawing.Imaging.ImageLockMode.ReadWrite, _
        image.pixelFormat) to !
        ptr = data.scan0
        bytes = uint8[](data.stride*image.height)
        pfs = System.Drawing.Image.getPixelFormatSize(image.pixelFormat)//8
        System.Runtime.InteropServices.Marshal.copy(ptr, bytes, 0, _
        data.stride*image.height)
        count = -1
        for y in image.height, for x in image.width 
            pos = (y*data.stride)+(x*pfs)
            bytes[pos+2], bytes[pos+1], bytes[pos], bytes[pos+3] = pixels[count+=1]
        System.Runtime.InteropServices.Marshal.copy(bytes, 0, ptr, _
        data.stride*image.height)
        image.unlockBits(data)
        return image

Görüntüyü bir yılanla doldurur:

#--#
   |
#--#
|
#--#
   |

Bu, alternatif yönlerdeki çizgilerden çok daha hızlı renk ayarına izin verir, ancak 3 genişlikte bir sürüm kadar bloklu olmaz.

Çok düşük toleranslarda bile, görüntünün kenarları hala görünür (daha küçük çözünürlüklerde ayrıntı kaybına rağmen).

0.01

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

0.1

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

0.01

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

0.01

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

0.1

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

0.03

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

0.005

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


1

C #

Yılan sol üst pikselde beyaz renkle başlar ve görüntüyü soldan sağa ve sonra sağdan sola doğru değiştirir.

using System;
using System.Drawing;

namespace snake
{
    class Snake
    {
        static void MakeSnake(Image original, double tolerance)
        {
            Color snakeColor = Color.FromArgb(255, 255, 255);//start white
            Bitmap bmp = (Bitmap)original;
            int w = bmp.Width;
            int h = bmp.Height;
            Bitmap snake = new Bitmap(w, h);

            //even rows snake run left to right else run right to left
            for (int y = 0; y < h; y++)
            {
                if (y % 2 == 0)
                {
                    for (int x = 0; x < w; x++)//L to R
                    {
                        Color pix = bmp.GetPixel(x, y);
                        double diff = Snake.RGB_Distance(snakeColor, pix);
                        if (diff < tolerance)
                        {
                            snakeColor = pix;
                        }
                        //else keep current color
                        snake.SetPixel(x, y, snakeColor);
                    }
                }
                else
                {
                    for (int x = w - 1; x >= 0; x--)//R to L
                    {
                        Color pix = bmp.GetPixel(x, y);
                        double diff = Snake.RGB_Distance(snakeColor, pix);
                        if (diff < tolerance)
                        {
                            snakeColor = pix;
                        }
                        //else keep current color
                        snake.SetPixel(x, y, snakeColor);
                    }
                }
            }

            snake.Save("snake.png");
        }

        static double RGB_Distance(Color current, Color next)
        {
            int dr = current.R - next.R;
            int db = current.B - next.B;
            int dg = current.G - next.G;
            double d = Math.Pow(dr, 2) + Math.Pow(db, 2) + Math.Pow(dg, 2);
            d = Math.Sqrt(d) / (255 * Math.Sqrt(3));
            return d;
        }

        static void Main(string[] args)
        {
            try
            {
                string file = "input.png";
                Image img = Image.FromFile(file);
                double tolerance = 0.03F;
                Snake.MakeSnake(img, tolerance);
                Console.WriteLine("Complete");
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }

        }
    }
}

Sonuç görüntü toleransı = 0,1

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.