MS Boya yetersiz


48

MS Paint her zaman harika bir zaman kaybıydı, ancak çoğu grafik tasarımcı tarafından kapatıldı. Belki insanlar sarsıcı renk paleti ya da sınırlı geri alma seviyeleri nedeniyle ilgilerini yitirdiler. Yine de, sadece standart fırça ve varsayılan renk paleti ile güzel görüntüler elde etmek hala mümkün.

Meydan okuma

Yalnızca varsayılan fırçayı (köşesiz 4x4 kare) ve varsayılan renk paletini (aşağıdaki 28 renk) kullanarak, stokastik tepe tırmanmaya dayanan bir tekniği kullanarak kaynak görüntüyü kopyalamaya çalışın .

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

Algoritma

Her cevap aynı temel algoritmayı (stokastik tepe tırmanışı) izlemelidir. Detaylar her adımda değiştirilebilir. Bir hareket fırçanın darbesi olarak kabul edilir (yani, boyaya tıklamak).

  1. Bir sonraki hareketi tahmin et. İstediğiniz şekilde bir sonraki hareket (ler) için bir tahmin (koordinat ve renk) yapın. Bununla birlikte, tahmin kaynak görüntüye referans vermemelidir.
  2. Tahmini uygula. Hareketi yapmak için fırçayı boyaya uygulayın.
  3. Hareket (ler) in yararını ölçün. Kaynak resme atıfta bulunarak, hareketlerin resme fayda sağlayıp sağlamadığını belirleyin (yani resim kaynak resme daha çok benziyor). Yararlı ise, hareketleri saklayın, aksi takdirde hareketleri atın.
  4. Yakınsama kadar tekrarlayın. Adım 1'e gidin ve algoritma yeterince birleşene kadar bir sonraki tahmini deneyin. Resim bu noktada kaynak resme çok benzemelidir.

Programınız bu dört adımla eşleşmiyorsa, muhtemelen stokastik bir tepe noktası değildir. Bunu popülerlik yarışması olarak etiketledim, çünkü amaç sınırlı renk paleti ve fırçasına dayalı ilginç boyama algoritmaları üretmek.

contraints

  • Algoritma bir şekilde stokastik olmalıdır .
  • Bir sonraki tahmin kaynak görüntüden etkilenmemelidir. Her yeni hareketi tahmin ediyor ve sonra yardım edip etmediğini kontrol ediyorsun. Örneğin, kaynak görüntü renklerine göre fırçanın nereye yerleştirileceğini belirlemenize izin verilmez (bu, hedef olmayan kaynak görüntüyü taklit etmeye benzer).

  • Algoritmanın adımlarını istediğiniz gibi ayarlayarak yerleşimi etkilemenize izin verilir. Örneğin, tahminde bulunmaya başlayabilir ve içe doğru hareket edebilir, her tahminde çizgiler oluşturmak için fırçayı sürükleyebilir veya önce koyu renkleri boyamaya karar verebilirsiniz. Bir sonraki istenen hareketi hesaplamak için önceki yinelenen görüntülere (ancak kaynak görüntüye değil) başvurabilirsiniz. Bunlar istediğiniz gibi kısıtlayıcı olabilir (yani, yalnızca geçerli yineleme için sol üst kadran içinde tahminlerde bulunun).

  • Kaynak görüntü ile mevcut yineleme arasındaki "fark" ölçüsü, bu hareketin "en iyi" olarak kabul edilip edilmediğini belirlemek için diğer potansiyel hareketleri hesaplamadığı sürece istediğiniz şekilde ölçülebilir . Mevcut hareketin "en iyisi" olup olmadığını, yalnızca kabul kriterlerinin toleransına uyup uymadığını bilmemelidir. Örneğin, abs(src.R - current.R) + abs(src.G - current.G) + abs(src.B - current.B)etkilenen her piksel için olduğu kadar iyi bilinen renk farkı tekniklerinden herhangi biri kadar basit olabilir .

palet

Şunları yapabilirsiniz Bir 28x1 görüntüsü olarak paletini indirmek veya kodunda doğrudan oluşturur.

Fırça

Fırça köşesiz 4x4 karedir. Bu, ölçekli bir versiyonudur:

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

(Kodunuz 4x4 sürümünü kullanmalıdır)

Örnek

Giriş:

Van Gogh - Yıldızlı Gece

Çıktı:

Üretilen Yıldızlı Gece

Temel algoritmanın yaptığım kısa bir videoda nasıl ilerlediğini görebilirsiniz (her kare 500 yinelemedir): Starry Night . İlk aşamaları izlemek ilginç:

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


1
@ vihan1086: Kaynak görüntünün saydamlığı yok. Geçerli görüntü, eğer demek istediğin buysa, önceki yinelemeye (örneğin, yeni noktaların öncekilerin üstüne eklendiği örneğe bağlı olarak) bağlı olabilir.
grovesNL

Stokastik tepe tırmanışının neye benzediğini göremiyorum ... tahmin etmenizi sağlar, ancak iyi olmasalar da onları istediğiniz gibi atabilirsiniz, ancak kontrol ederken bir sürü tahminden geçme ile aynıdır. en iyisini seçmek.
Sp3000,

@ Sp3000: Önemli olan, potansiyel bir hareket gerçekleştirilinceye kadar "en iyi" hareketi bilmemeniz, hangi noktada kendi kabul kriterlerinize uyuyorsa (yani, "yeterince yakınsa) kabul etmeyi seçebilirsiniz. "). Kabul kriterleri tüm olası hareketler hakkında bilgi sahibi olmamalıdır (bunu daha fazla açıklığa kavuşturmam gerekebilir). Temel olarak, zamanın önündeki “en iyi” hareketi belirleyememelisiniz, bunun yerine görüntüyü adım adım iyileştirmelisiniz.
korularNL

Bu fırça şekliyle, köşe piksellerini boyamak imkansız mı (örneğin bu izlenimi elde ediyorum) veya fırça görüntü sınırlarının dışına kısmen yerleştirilebilir mi?
Oliphaunt

Sp3000'in endişesi belki de aşağıdaki deterministik algoritmayı içerir: her piksel için sırayla her rengi deneyin. Stokastiklik yok ve çok titizlik gibi, yine de kurallara uygun görünüyor.
Oliphaunt

Yanıtlar:


35

JavaScript

Bu çözüm, görüntü verilerini çıkarmak için HTML5 canvas öğesini kullanır, ancak HTML kullanmanıza gerek kalmadan konsolunuzda çalıştırılabileceği anlamına gelir. Renk paleti görüntüsüne bir dizi olarak erişir; Palet görüntüsündeki tüm renkleri bir dizide sakladım). Konsola çıkış yapar (bittikten sonra) ve sonucu bir değişkende saklar.

Kodun en güncel sürümü kemanın içindedir . Keman ayrıca resimlerdeki gürültüyü azaltmak için daha iyi bir algoritma kullanır. Algoritmadaki gelişme çoğunlukla ters rengin seçilmesine neden olan bir fonksiyonun (maks. Min.) Sabitlenmesidir.

MS Paint Icon şeklinde kodlayın! (keman veya Yığın Parçacığında biçimlendirilmiş kod)

eval(`                                                   function                  
                                                        Paint(t){fun              
                                                         ction n(t){va            
                                                         r n=t.toString(          
                                                         16);return 1==n.         
                                                         length?"0"+n:n}fu        
                                                         nction e(t){return       
                                                         "#"+n(t[0])+n(t[1]       
                                                          )+n(t[2])}var a=ne      
                                                          w Image,i=document.     
                                                          createElement("canv     
                                                          as"),h=null,o=docum     
                                                          ent.createElement(      
                                    "canvas"),r=          o.getContext("2d        
                               ")     ,l=[],u=this,c      =[[0,0,0],[255          
                            ,2       55,255],[ 192,192,   192],[128,12            
                          8     ,128],[126,3,8],[252,13,27] ,[255,25              
                       3,    56],[128,127,23],[15,127,18],[ 41,253                
                      ,    46],[45,255,254],[17,128,127],[2 ,12,1                 
                    2    6],[ 11,36,2 51],[252,40,252],[12 7,15,1                 
                  2    6],[  128,127 ,68],[255,253,136],[4 2,253,                 
                 1   33],   [4,64,64],[23 ,131,251],[133,255,254],                
               [    129   ,132,252],[6,6 6,126],[127,37,2 51],[127,               
              6   4,1    3],[253,128,73],[252,22,129]];a.crossOrigin              
             =   "",   a.src=t,this.done=this.done||function(){},a.o              
            n   load=function(){function t(t){var n=0,e=0,a=0;return              
           t  .forEach(function(t){n+=t[0],e+=t[1],a+=t[2]}),[n/t.leng            
          t  h,e /t.length,a/t.length]}function n(t){for(var n=[],e=0;e           
         <  t.l  ength;e+=1)n.push(t[e]);return n}function g(t,n){retur           
        n  (Ma  th.abs(t[0]-n[0])/255+Math.abs(t[1]-n[1])/255+Math.abs(t          
       [  2]- n[2])/255)/3}function f(t,n){for(var e=Math.floor(Math.ran          
          do m()*n.length),a=n[e],i=(g(t,a),1-.8),h=56,o=[];o.length<=h&          
         &g (t,a)>i;)if(o.push(a),a=n[Math.floor(Math.random()*n.length)]         
     ,  o.length==h){var r=o.map(function(n){return g(t,n)});a=o[r.indexO         
       f(Math.max.apply(Math,r))],o.push(a)}return a}function s(t,n){for(         
    v  ar e=[];t.length>0;)e.push(t.splice(0,n).slice(0,-1));return e}i.w         
   i  dth=a.width,i.height=2*a.height,h=i.getContext("2d"),h.drawImage(a,0        
   ,0,a.width,a.height);for(var d=(function(t){reduce=t.map(function(t){re        
  turn(t[ 0]+t[1]+t[2])/3})}(c),0),m=0,v=i.width*i.height/4,p=0;v>p;p+=1)d        
  >2*Mat h.ceil(a.width/2)&&(d=0,m+=1),l.push(f(t(s(n(h.getImageData(2*d,2        
  *m,4,4).data),4)),c)),d+=1;o.width=i.width,o.height=i.height;for(var d=0        
 ,m=0,v=i.width*i.height/4,p=0;v>p;p+=1)d>2*Math.ceil(a.width/2)&&(d=0,m+=        
 1),console.log("Filling point ("+d+", "+m+") : "+e(l[p])),r.fillStyle=e(l        
 [p]),r.fillRect(2*d+1,2*m,2,1)  ,r.fillRect(2*d,2*m+1,4,2),r.fillRect(2*d        
+1,2*m+3,2,1),d+=1;u.result=o      .toDataURL("image/png"),u.resultCanvas         
=o,u.imageCanvas=i,u.image=a       ,u.done(),console.log(u.result)},a.one         
rror=function(t){console.log       ("The image failed to load. "+t)}}/*..         
............................       ......................................         
. ..........................       .....................................          
............................      ......................................          
.............................    .......................................          
.......................................................................           
.......................................................................           
..................  ..................................................            
................     .................................................            
..............       ................................................             
.............       ................................................              
...........        .................................................              
 .........         ................................................               
 .......          ................................................                
  ....           ................................................                 
                ................................................                  
                ...............................................                   
               ...............................................                    
              ..............................................                      
              .............................................                       
             ............................................                         
             ..........................................                           
              .......................................                             
              .....................................                               
               .................................                                  
                .............................                                     
                  ......................                                          
                                   .....                                          
                                  .....                                           
                                  .....                                           
                                  ....                                            
                                   */`
.replace(/\n/g,''))                                             

Kullanımı:

Paint('DATA URI');

Keman .

Keman, crossorigin.me'yi kullanır, böylece çapraz kaynak-kaynak paylaşımı konusunda endişelenmenize gerek yoktur.

Fiddle'ı da güncelledim, böylece en iyi görünen resmi üretmek için bazı değerleri ayarlayabilirsiniz. Bazı resimlerin renkleri kapalı olabilir, bundan kaçınmak için algoritmayı ayarlamak için accept_rate değerini ayarlayın. Daha düşük bir sayı daha iyi gradyanlar anlamına gelir, daha yüksek bir sayı daha net renklere neden olur.


İşte bir Stack-Snippet olarak keman (kemanın çalışmaması durumunda güncellenmez):


New Horizon'un Pluto bayrağını anmak için Pluto'nun bir görüntüsünü girdim:

orijinal çekilmiş

orijinal çekilmiş

Aşağıdakiler için orijinaline mümkün olduğunca yakın olmalarını sağlamak için ayarladım:

Bunu OS X Yosemite'nin varsayılan duvar kağıdına koştum. Bir süre çalıştırıldıktan sonra sonuçlar kesinlikle çok etkileyici. Orijinal dosya çok büyüktü (26 MB), böylece yeniden boyutlandırdım ve sıkıştırdım:

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

Yıldızlı gece (Daha iyi sonuçlar için daha yüksek çözünürlüklü bir görüntü kullandım )

Google'da bulduğum bir resim: görüntü tanımını buraya girin görüntü tanımını buraya girin


12

JavaScript + HTML

Rastgele:

Rastgele nokta

Rasgele Hizalanmış:

Tuvali 4x4 karelere böler ve karelerden birinin içinde rastgele bir nokta seçer. Ofsetler ızgarayı hareket ettirir, böylece küçük boşlukları doldurabilirsiniz.

Döngü:

Bir ızgara oluşturur ve tüm noktaların üzerinden geçirir. Ofsetler ızgarayı hareket ettirir. Boşluk, her hücrenin boyutunu belirler. (Üst üste gelmeye başlayacaklar)

Renk farkı:

  • RGB
  • HSL
  • HSV

var draw = document.getElementById("canvas").getContext("2d");
var data = document.getElementById("data").getContext("2d");
colors = [
    [0, 0, 0],
    [255, 255, 255],
    [192, 192, 192],
    [128, 128, 128],
    [126, 3, 8],
    [252, 13, 27],
    [255, 253, 56],
    [128, 127, 23],
    [15, 127, 18],
    [41, 253, 46],
    [45, 255, 254],
    [17, 128, 127],
    [2, 12, 126],
    [11, 36, 251],
    [252, 40, 252],
    [127, 15, 126],
    [128, 127, 68],
    [255, 253, 136],
    [42, 253, 133],
    [4, 64, 64],
    [23, 131, 251],
    [133, 255, 254],
    [129, 132, 252],
    [6, 66, 126],
    [127, 37, 251],
    [127, 64, 13],
    [253, 128, 73],
    [252, 22, 129]
];
iteration = 0;
fails = 0;
success = 0;
x = 0;
y = 0;
//Init when the Go! button is pressed
document.getElementById("file").onchange = function (event) {
    document.getElementById("img").src = URL.createObjectURL(event.target.files[0]);
    filename = document.getElementById("file").value;
    /*if (getCookie("orginal") == filename) {
        console.log("Loading from Cookie");
        reload = true;
        document.getElementById("reload").src = getCookie("picture");
    }*/
};

/*function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1);
        if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
    }
    return "";
}*/

//Run when the image has been loaded into memory
document.getElementById("img").onload = function () {
    document.getElementById("file").disable = "true";
    document.getElementById("canvas").hidden = "";
    document.getElementById("canvas").height = document.getElementById("img").height;
    document.getElementById("data").height = document.getElementById("img").height;
    document.getElementById("canvas").width = document.getElementById("img").width;
    document.getElementById("data").width = document.getElementById("img").width;

    var imgData = draw.createImageData(document.getElementById("img").width, document.getElementById("img").height);
    for (var i = 0; i < imgData.data.length; i += 4) {
        imgData.data[i + 0] = 0;
        imgData.data[i + 1] = 0;
        imgData.data[i + 2] = 0;
        imgData.data[i + 3] = 255;
    }
    draw.putImageData(imgData, 0, 0);
    data.putImageData(imgData, 0, 0);
    if (reload == true) {
        draw.drawImage(document.getElementById("reload"), 0, 0);
    }
    data.drawImage(document.getElementById("img"), 0, 0);
    setInterval(function () {
        for (var u = 0; u < document.getElementById("selectColor").value; u++) {
            doThing();
        }
    }, 0);
};

//The core function. Every time this function is called, is checks/adds a dot.
function doThing() {
    getCoords();
    paintBucket();
    console.count("Iteration");
    if (compare(x, y)) {
        draw.putImageData(imgData, x, y);
    }
}

function getCoords() {
    switch (document.getElementById("selectCord").value) {
        case "1":
            x = Math.floor(Math.random() * (document.getElementById("img").width + 4));
            y = Math.floor(Math.random() * (document.getElementById("img").height + 4));
            break;
        case "2":
            x = Math.floor(Math.random() * ((document.getElementById("img").width + 4) / 4)) * 4;
            console.log(x);
            x += parseInt(document.getElementById("allignX").value);
            console.log(x);
            y = Math.floor(Math.random() * ((document.getElementById("img").height + 4) / 4)) * 4;
            y += parseInt(document.getElementById("allignY").value);
            break;
        case "3":
            x += parseInt(document.getElementById("loopX").value);
            if (x > document.getElementById("img").width + 5) {
                x = parseInt(document.getElementById("allignX").value);
                y += parseInt(document.getElementById("loopY").value);
            }
            if (y > document.getElementById("img").height + 5) {
                y = parseInt(document.getElementById("allignY").value);
            }
    }
}

function compare(arg1, arg2) {
    var arg3 = arg1 + 4;
    var arg4 = arg2 + 4;
    imgData2 = data.getImageData(arg1, arg2, 4, 4);
    imgData3 = draw.getImageData(arg1, arg2, 4, 4);
    N = 0;
    O = 0;
    i = 4;
    addCompare();
    addCompare();
    i += 4;
    for (l = 0; l < 8; l++) {
        addCompare();
    }
    i += 4;
    addCompare();
    addCompare();
    i += 4;
    //console.log("New Score: " + N + " Old Score: " + O);
    iteration++;
    /*if(iteration>=1000){
        document.cookie="orginal="+filename;
        document.cookie="picture length="+document.getElementById("canvas").toDataURL().length;
        document.cookie="picture="+document.getElementById("canvas").toDataURL();
        
    }*/
    if (N < O) {
        return true;
    } else {
        return false;
    }
}

function addCompare() {
    if (document.getElementById("colorDif").value == "HSL") {
        HSLCompare();
        i += 4;
        return;
    }
    if (document.getElementById("colorDif").value == "HSV") {
        HSVCompare();
        i += 4;
        return;
    }
    N += Math.abs(imgData.data[i] - imgData2.data[i]);
    N += Math.abs(imgData.data[i + 1] - imgData2.data[i + 1]);
    N += Math.abs(imgData.data[i + 2] - imgData2.data[i + 2]);
    O += Math.abs(imgData3.data[i] - imgData2.data[i]);
    O += Math.abs(imgData3.data[i + 1] - imgData2.data[i + 1]);
    O += Math.abs(imgData3.data[i + 2] - imgData2.data[i + 2]);
    i += 4;
}

function HSVCompare() {
    var NewHue = rgbToHsv(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2])[0];
    var PicHue = rgbToHsv(imgData2.data[i], imgData2.data[i + 1], imgData2.data[i + 2])[0];
    var OldHue = rgbToHsv(imgData3.data[i], imgData3.data[i + 1], imgData3.data[i + 2])[0];

    var NScore = [Math.abs(NewHue - PicHue), ((NewHue < PicHue) ? NewHue + (1 - PicHue) : PicHue + (1 - NewHue))];
    var OScore = [Math.abs(OldHue - PicHue), ((OldHue < PicHue) ? OldHue + (1 - PicHue) : PicHue + (1 - OldHue))];
    
    
    NScore = Math.min(NScore[0], NScore[1]);
    OScore = Math.min(OScore[0], OScore[1]);
    
    NewHue = rgbToHsv(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2])[1];
    PicHue = rgbToHsv(imgData2.data[i], imgData2.data[i + 1], imgData2.data[i + 2])[1];
    OldHue = rgbToHsv(imgData3.data[i], imgData3.data[i + 1], imgData3.data[i + 2])[1];
    
    NScore += Math.abs(NewHue-PicHue);
    OScore += Math.abs(OldHue-PicHue);
    
    NewHue = rgbToHsv(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2])[2];
    PicHue = rgbToHsv(imgData2.data[i], imgData2.data[i + 1], imgData2.data[i + 2])[2];
    OldHue = rgbToHsv(imgData3.data[i], imgData3.data[i + 1], imgData3.data[i + 2])[2];
    
    N += Math.abs(NewHue-PicHue) + NScore;
    O += Math.abs(OldHue-PicHue) + OScore;
}

function rgbToHsv(r, g, b){
    r = r/255, g = g/255, b = b/255;
    var max = Math.max(r, g, b), min = Math.min(r, g, b);
    var h, s, v = max;

    var d = max - min;
    s = max == 0 ? 0 : d / max;

    if(max == min){
        h = 0; // achromatic
    }else{
        switch(max){
            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
            case g: h = (b - r) / d + 2; break;
            case b: h = (r - g) / d + 4; break;
        }
        h /= 6;
    }

    return [h, s, v];
}

function HSLCompare() {
    var result = 0;
    rgb = false;

    var NewHue = rgbToHue(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2])[0];
    var PicHue = rgbToHue(imgData2.data[i], imgData2.data[i + 1], imgData2.data[i + 2])[0];
    var OldHue = rgbToHue(imgData3.data[i], imgData3.data[i + 1], imgData3.data[i + 2])[0];
    if (rgb == true) {
        N += Math.abs(imgData.data[i] - imgData2.data[i]);
        N += Math.abs(imgData.data[i + 1] - imgData2.data[i + 1]);
        N += Math.abs(imgData.data[i + 2] - imgData2.data[i + 2]);
        O += Math.abs(imgData3.data[i] - imgData2.data[i]);
        O += Math.abs(imgData3.data[i + 1] - imgData2.data[i + 1]);
        O += Math.abs(imgData3.data[i + 2] - imgData2.data[i + 2]);
        return;
    }
    var NScore = [Math.abs(NewHue - PicHue), ((NewHue < PicHue) ? NewHue + (1 - PicHue) : PicHue + (1 - NewHue))];
    var OScore = [Math.abs(OldHue - PicHue), ((OldHue < PicHue) ? OldHue + (1 - PicHue) : PicHue + (1 - OldHue))];
    
    
    NScore = Math.min(NScore[0], NScore[1]);
    OScore = Math.min(OScore[0], OScore[1]);
    
    NewHue = rgbToHue(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2])[1];
    PicHue = rgbToHue(imgData2.data[i], imgData2.data[i + 1], imgData2.data[i + 2])[1];
    OldHue = rgbToHue(imgData3.data[i], imgData3.data[i + 1], imgData3.data[i + 2])[1];
    
    NScore += Math.abs(NewHue-PicHue);
    OScore += Math.abs(OldHue-PicHue);
    
    NewHue = rgbToHue(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2])[2];
    PicHue = rgbToHue(imgData2.data[i], imgData2.data[i + 1], imgData2.data[i + 2])[2];
    OldHue = rgbToHue(imgData3.data[i], imgData3.data[i + 1], imgData3.data[i + 2])[2];
    
    N += Math.abs(NewHue-PicHue) + NScore;
    O += Math.abs(OldHue-PicHue) + OScore;
}

function rgbToHue(r, g, b) {
    if (Math.max(r, g, b) - Math.min(r, g, b) < 50) {
        rgb = true
    }
    r /= 255, g /= 255, b /= 255;
    var max = Math.max(r, g, b),
        min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2;

    if (max == min) {
        h = s = 0; // achromatic
    } else {
        var d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
            case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }
    return [h,s,l];
}

//Create a 4x4 ImageData object, random color selected from the colors var, transparent corners.
function paintBucket() {
    color = Math.floor(Math.random() * 28);
    imgData = draw.createImageData(4, 4);
    imgData2 = draw.getImageData(x, y, 4, 4);
    i = 0;
    createCorn();
    createColor();
    createColor();
    createCorn();
    for (l = 0; l < 8; l++) {
        createColor();
    }
    createCorn();
    createColor();
    createColor();
    createCorn();
}

function createCorn() {
    imgData.data[i] = imgData2.data[i];
    imgData.data[i + 1] = imgData2.data[i + 1];
    imgData.data[i + 2] = imgData2.data[i + 2];
    imgData.data[i + 3] = 255;
    i += 4;
}

function createColor() {
    imgData.data[i] = colors[color][0];
    imgData.data[i + 1] = colors[color][1];
    imgData.data[i + 2] = colors[color][2];
    imgData.data[i + 3] = 255;
    i += 4;
}
<canvas id="canvas" hidden></canvas>
<br>
<canvas id="data" hidden></canvas>
<br>
<input type="file" id="file"></input>
<br>
<img id="img">
<img id="reload" hidden>
<p>Algorithms:</p>
<select id="selectCord">
    <option value="1">Random</option>
    <option value="2">Random Alligned</option>
    <option value="3" selected>Loop</option>
</select>
<select id="selectColor">
    <option value="2000">Super Speedy</option>
    <option value="1000">Very Speedy</option>
    <option value="500" selected>Speedy</option>
    <option value="1">Visual</option>
</select>
<select id="colorDif">
    <option value="RGB" selected>RGB</option>
    <option value="HSL">HSL</option>
    <option value="HSV">HSV</option>
</select>
<p>Algorithm Options:
    <br>
</p>
<p>X Offset:
    <input id="allignX" type="range" min="0" max="3" value="0"></input>
</p>
<p>Y Offset:
    <input id="allignY" type="range" min="0" max="3" value="0"></input>
</p>
<p>Spacing X:
    <input id="loopX" type="range" min="1" max="4" value="2"></input>
</p>
<p>Spacing Y:
    <input id="loopY" type="range" min="1" max="4" value="2"></input>
</p>

görüntü tanımını buraya girin
RGB: görüntü tanımını buraya girin
HSL: görüntü tanımını buraya girin
HSV: görüntü tanımını buraya girin


Çok havalı. document.cookieBelge korumalı alan olduğundan, "Çalıştır kod pasajı" ayarlamaya çalıştığında (1000 yinelemeden sonra) kırılıyor . Kurabiye gerekli mi?
korularNL

Hayır, bir zamanlar programı birkaç saat çalıştırdım, ancak daha sonra tarayıcım çöktü. Bu yüzden kurabiyeyi yedek bir şey olarak yaptım. Ancak bunu kaldıracağım çünkü yığın değişimi çerezleri beğenmiyor.
Grant Davis

1
Kodunuzdaki bakınca ben aynı speedup için faydalı olacağını söyleyebiliriz wolfhammer en Yanıta önerdi uygulanan hariç doThingyerine loop. Ekstra hatta değecek hız artışını bulabilirsiniz ...
trichoplax

1
@trichoplax Çok teşekkürler, Düzeltmeniz sadece programımın hızını arttırmakla kalmadı, aynı zamanda onarımı yaptığım bir matematik hatasını bulup düzeltdim ve programım artık bu küçük siyah noktaları oluşturmuyor.
Grant Davis,

Bu harika! Yeni çıktı görüntüsü çok daha iyi görünüyor.
trichoplax

8

C # (referans uygulaması)

Bu, söz konusu görüntüleri oluşturmak için kullanılan koddur. Bazı insanlara algoritmalarını organize etmeleri için referans vermenin faydalı olacağını düşündüm. Her hareket için tamamen rasgele bir koordinat ve renk seçilir. Fırça büyüklüğü / kabul kriterlerinin getirdiği sınırlamaları göz önüne alarak şaşırtıcı derecede iyi bir performans sergiliyor.

Açık kaynak kodlu ColorMine'deki renk farklılıklarını ölçmek için CIEDE2000 algoritmasını kullanıyorum . Bu, daha yakın renk eşleşmeleri sağlamalıdır (insan bakış açısıyla), ancak bu paletle kullanıldığında gözle görülür bir fark görünmüyor.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using ColorMine.ColorSpaces;
using ColorMine.ColorSpaces.Comparisons;

namespace Painter
{
    public class Painter
    {
        private readonly Bitmap _source;
        private readonly Bitmap _painting;
        private readonly int _width;
        private readonly int _height;
        private readonly CieDe2000Comparison _comparison = new CieDe2000Comparison();
        private const int BRUSHSIZE = 4;
        private readonly Random _random = new Random();
        private readonly ColorPalette _palette;

        private static readonly int[][] BRUSH = {
            new[] {1, 0}, new[] {2, 0},
            new[] {0, 1}, new[] {1, 1}, new[] {2, 1}, new[] {3, 1}, 
            new[] {0, 2}, new[] {1, 2}, new[] {2, 2}, new[] {3, 2}, 
            new[] {1, 3}, new[] {2, 3}
        };

        public Painter(string sourceFilename, string paletteFilename)
        {
            _source = (Bitmap)Image.FromFile(sourceFilename);
            _width = _source.Width;
            _height = _source.Height;

            _palette = Image.FromFile(paletteFilename).Palette;
            _painting = new Bitmap(_width, _height, PixelFormat.Format8bppIndexed) {Palette = _palette};

            // search for black in the color palette
            for (int i = 0; i < _painting.Palette.Entries.Length; i++)
            {
                Color color = _painting.Palette.Entries[i];
                if (color.R != 0 || color.G != 0 || color.B != 0) continue;
                SetBackground((byte)i);
            }
        }

        public void Paint()
        {
            // pick a color from the palette
            int brushIndex = _random.Next(0, _palette.Entries.Length);
            Color brushColor = _palette.Entries[brushIndex];

            // choose coordinate
            int x = _random.Next(0, _width - BRUSHSIZE + 1);
            int y = _random.Next(0, _height - BRUSHSIZE + 1);

            // determine whether to accept/reject brush
            if (GetBrushAcceptance(brushColor, x, y))
            {
                BitmapData data = _painting.LockBits(new Rectangle(0, y, _width, BRUSHSIZE), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
                byte[] bytes = new byte[data.Height * data.Stride];
                Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);

                // apply 4x4 brush without corners
                foreach (int[] offset in BRUSH)
                {
                    bytes[offset[1] * data.Stride + offset[0] + x] = (byte)brushIndex;
                }
                Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
                _painting.UnlockBits(data);
            }
        }

        public void Save(string filename)
        {
            _painting.Save(filename, ImageFormat.Png);
        }

        private void SetBackground(byte index)
        {
            BitmapData data = _painting.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
            byte[] bytes = new byte[data.Height * data.Stride];
            for (int i = 0; i < data.Height; i++)
            {
                for (int j = 0; j < data.Stride; j++)
                {
                    bytes[i*data.Stride + j] = index;
                }
            }
            Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
            _painting.UnlockBits(data);
        }

        private bool GetBrushAcceptance(Color brushColor, int x, int y)
        {
            double currentDifference = 0.0;
            double brushDifference = 0.0;
            foreach (int[] offset in BRUSH)
            {
                Color sourceColor = _source.GetPixel(x + offset[0], y + offset[1]);
                Rgb sourceRgb = new Rgb {R = sourceColor.R, G = sourceColor.G, B = sourceColor.B};
                Color currentColor = _painting.GetPixel(x + offset[0], y + offset[1]);

                currentDifference += sourceRgb.Compare(new Rgb {R = currentColor.R, G = currentColor.G, B = currentColor.B}, _comparison);
                brushDifference += sourceRgb.Compare(new Rgb {R = brushColor.R, G = brushColor.G, B = brushColor.B}, _comparison);
            }
            return brushDifference < currentDifference;
        }
    }
}

Ardından, aşağıdaki koda benzer şekilde bir örnek çağırarak (videolarım gibi) bir dizi görüntü oluşturabilirsiniz (istenen yinelemeler / çerçeveler / adın sayısına göre çimdikleyin). İlk argüman kaynak görüntünün dosya yolu, ikinci argüman paletin dosya yolu (soruya bağlı) ve üçüncü argüman çıktı görüntüleri için dosya yoludur.

namespace Painter
{
    class Program
    {
        private static void Main(string[] args)
        {
            int i = 0;
            int counter = 1;
            Painter painter = new Painter(args[0], args[1]);
            while (true)
            {
                painter.Paint();
                if (i%500000 == 0)
                {
                    counter++;
                    painter.Save(string.Format("{0}{1:D7}.png", args[2], counter));
                }
                i++;
            }
        }
    }
}

Çevrimiçi bazı renkli kanvas tablolarını araştırdım ve harika (karmaşık) testler gibi görünen aşağıdaki resimlerle karşılaştım. Tüm telif hakları kendi sahiplerine aittir.

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

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

Kaynak

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

Kaynak


Bu şekilde biliyorum ki hiçbir şey hakkında hiçbir şey bilmiyorum. Güzel bir çözüm
Brandon

6

JavaScript Canvas

Güncelleme

Yorumlarda mükemmel öneriler. Artık daha hızlı ve kullanıcı arayüzünü yavaşlatmıyor!

function previewFile() {
  var srcImage = document.getElementById('srcImage');
  var file = document.querySelector('input[type=file]').files[0];
  var reader = new FileReader();

  reader.onloadend = function() {
    srcImage.src = reader.result;
  }

  if (file) {
    reader.readAsDataURL(file);
  } else {
    srcImage.src = "";
  }
}

var buckets = []; // buckets / iterations
var iter_per_focus = 5000;

var pal = "00FFFF,0000FF,00FF00,FFFF00,\
C0C0C0,FF0000,FF00FF,FFFF78,\
FF0078,FF7848,7878FF,78FFFF,\
00FF78,784800,007800,\
007878,787800,780000,787878,\
000078,780078,004878,7800FF,\
0078FF,004848,787848,000000,FFFFFF".split(",");
var pLen = pal.length;
var p = 0;
var _R = 0;
var _G = 1;
var _B = 2;
var _CAN = 3;

// Create fast access palette with r,g,b values and
// brush image for color.
function initPal() {

  for (var i = 0; i < pal.length; i++) {
    var r = parseInt(pal[i].substr(0, 2), 16);
    var g = parseInt(pal[i].substr(2, 2), 16);
    var b = parseInt(pal[i].substr(4, 2), 16);
    var pcan = document.createElement('canvas');
    pcan.width = 4;
    pcan.height = 4;
    var pctx = pcan.getContext('2d');
    pctx.fillStyle = '#' + pal[i];
    pctx.beginPath();
    pctx.rect(1, 0, 2, 4);
    pctx.rect(0, 1, 4, 2);
    pctx.fill();

    pal[i] = [r,g,b,pcan];

  }
}
initPal();

var score = [];
var can = document.getElementById("canB");
var ctx = can.getContext('2d');
var mainDiv = document.getElementById("main");
var bCan = document.createElement('canvas');
bCan.width = can.width;
bCan.height = can.height;
var bCtx = bCan.getContext('2d');

var canA = document.getElementById("canA");
can.width = can.height = canA.width = canA.height = 200;
var ctxA = canA.getContext('2d');
var imageData;
var data;

function getSrcImage() {
  var img = document.getElementById('srcImage');
  can.width = canA.width = img.width;
  can.height = canA.height = img.height;
  ctxA.drawImage(img, 0, 0);
  imageData = ctxA.getImageData(0, 0, img.width, img.height);
  data = imageData.data;
  
  // adjust for brush offset
  var w = can.width - 2;
  var h = can.height - 2;
  
  var n = Math.floor((w * h) / iter_per_focus);
  buckets = [];
  for (var i = 0; i < n; i++) {
    var bucket = [];
    bucket.r = Math.floor(Math.random() * pLen);
    buckets.push(bucket);
  }
  var b = 0;
  var pt = 0;
  for (var y = 0; y < h; y++) {
    for (var x = 0; x < w; x++, pt+=4) {
      var r = Math.floor((Math.random() * n));
      buckets[r].push([x,y,pt,256 * 12,Math.floor(Math.random()*pLen)]);
      b %= n;
    }
    pt += 8; // move past brush offset.
  }
    
}

var loopTimeout = null;

function loopInit() {
  var r, y, x, pt, c, s;
  var row = can.width * 4;
  
  var b = 0;

  function loop() {
    clearTimeout(loopTimeout);
    var bucket = buckets[b++];
    var len = bucket.length;
    // Stepping color
    //c = pal[p];
    // Pulsing color;
    //c = pal[Math.floor(Math.random()*pLen)]
    // Pulsting step
    c = pal[bucket.r++];
    bucket.r%=pLen;
    b %= buckets.length;
    if (b === 0) {
      p++;
      p%=pLen;
    }
    
    for (var i = 0; i < len; i++) {

      var x = bucket[i][0]
      var y = bucket[i][1];
      var pt = bucket[i][2];
      // Random color
      //c = pal[bucket[i][4]++];
      //bucket[i][4]%=pLen;
      
     
      s = Math.abs(data[pt] - c[_R]) +
        Math.abs(data[pt + 1] - c[_G]) +
        Math.abs(data[pt + 2] - c[_B]) +
        Math.abs(data[pt + 4] - c[_R]) +
        Math.abs(data[pt + 5] - c[_G]) +
        Math.abs(data[pt + 6] - c[_B]) +
        Math.abs(data[pt + row] - c[_R]) +
        Math.abs(data[pt + row + 1] - c[_G]) +
        Math.abs(data[pt + row + 2] - c[_B]) +
        Math.abs(data[pt + row + 4] - c[_R]) +
        Math.abs(data[pt + row + 5] - c[_G]) +
        Math.abs(data[pt + row + 6] - c[_B]);
      if (bucket[i][3] > s) {
        bucket[i][3] = s;
        bCtx.drawImage(c[_CAN], x - 1, y - 1);
      }

    }
    loopTimeout = setTimeout(loop, 0);
  }

  loop();
}

// Draw function is separate from rendering. We render
// to a backing canvas first.
function draw() {
  ctx.drawImage(bCan, 0, 0);
  setTimeout(draw, 100);
}

function start() {

  getSrcImage();
  imageData = ctxA.getImageData(0, 0, can.width, can.height);
  data = imageData.data;
  bCan.width = can.width;
  bCan.height = can.height;
  bCtx.fillStyle = "black";
  bCtx.fillRect(0, 0, can.width, can.height);
  loopInit();

  draw();
}
body {
  background-color: #444444;
  color: #DDDDEE;
}
#srcImage {
  display: none;
}
#testBtn {
  display: none;
}
#canA {
  display:none;
}
<input type="file" onchange="previewFile()">
<br>
<img src="" height="200" alt="Upload Image for MS Painting">

<button onclick="genImage()" id="testBtn">Generate Image</button>

<div id="main">
  <img id="srcImage" src="" onload="start()">
  <canvas id="canA"></canvas>
  <canvas id="canB"></canvas>
</div>


@trichoplax Görüntünün yüklenmesinde siteler arası bazı sorunlar vardı. Bir şeyleri çözebilecek miyim bir bakalım.
wolfhammer, 13.03.2015

1
@trichoplax Hiçbir karanlık kasıtlı değildi. Oluşturulan görüntüde saydamlığı olan bir hataydı. Şeffaf olan düşünce kodunun siyah olması gerekir.
wolfhammer,

@trichoplax Ben sadece rastgele bir renk karşılaştırmak için değiştirdim.
wolfhammer,

1
Kodunu bir jsfiddle'a kopyaladım ve bir deney denedim. Yakınsama biraz daha hızlı oldu. Denemek isteyebilirsiniz ... Tüm yaptığım, döngü işlevinin içeriğini, içerikleri 1000 kez tekrarlamak için bir for döngüsü ile çevrelemekti. Bu, fare ve klavye olaylarının her yineleme yerine yalnızca her 1000 yineleme için denetleneceği anlamına gelir. Kişisel döngü her 1000 yineleme hala duyarlı fare ve klavyeyi bırakır yeterince hızlı olduğunu ve :) yakınlaşma için saat bekledikten kazandırır
placozoa

1
@tricholplax Wow bu önerileri işleri çok daha hızlı hale getirdi. S / = 4 gerekli olduğunu düşünüyorum, onunla serin renkli animasyon alamadım.
wolfhammer

3

Mathematica

O kadar da hızlı değil, ama en azından belli belirsiz tanınabilir görüntüler yaratıyor, bu yüzden mutluyum.

img = Import["http://i.stack.imgur.com/P7X6g.jpg"]
squigglesize = 20;
squiggleterb = 35;
colors = Import["http://i.stack.imgur.com/u9JAD.png"];
colist = Table[RGBColor[PixelValue[colors, {x, 1}]], {x, 28}];
imgdim0 = ImageDimensions[img];
curimg = Image[ConstantArray[0, Reverse[imgdim0]]];

rp := RandomInteger[squigglesize, 2] - squigglesize/2;
i = 0; j = 0;
Module[{imgdim = imgdim0, randimg, points, randcol, squigmid, st, 
  randist, curdist = curdist0, i = 0, j = 0},

 While[j < 10,
  squigmid = {RandomInteger[imgdim[[1]]], RandomInteger[imgdim[[1]]]};      
  While[i < 20,
   randcol = RandomChoice[colist];
   st = RandomInteger[squiggleterb, 2] - squiggleterb/2;
   points = {rp + squigmid + st, rp + squigmid + st, rp + squigmid + st, rp + squigmid + st};

   randimg = 
    Rasterize[
     Style[Graphics[{Inset[curimg, Center, Center, imgdim],
        {randcol, BezierCurve[Table[{-1, 0}, {4}] + points]},
        {randcol, BezierCurve[Table[{-1, 1}, {4}] + points]},
        {randcol, BezierCurve[Table[{0, -1}, {4}] + points]},
        {randcol, BezierCurve[points]},
        {randcol, BezierCurve[Table[{0, 1}, {4}] + points]},
        {randcol, BezierCurve[Table[{0, 2}, {4}] + points]},
        {randcol, BezierCurve[Table[{1, -1}, {4}] + points]},
        {randcol, BezierCurve[Table[{1, 0}, {4}] + points]},
        {randcol, BezierCurve[Table[{1, 1}, {4}] + points]},
        {randcol, BezierCurve[Table[{1, 2}, {4}] + points]},
        {randcol, BezierCurve[Table[{2, 0}, {4}] + points]},
        {randcol, BezierCurve[Table[{2, 1}, {4}] + points]}
       }, ImageSize -> imgdim, PlotRange -> {{0, imgdim[[1]]}, {0, imgdim[[2]]}}], 
      Antialiasing -> False], RasterSize -> imgdim];
   randist = ImageDistance[img, randimg];
   If[randist < curdist, curimg = randimg; curdist = randist; i = 0; 
    j = 0;];
   i += 1;
   ]; j += 1; i = 0;];
 Print[curimg]]

Çıktı:

giriş Çıktı

giriş çıktı

Çıktı muhtemelen daha fazla yineleme ile biraz daha iyi olabilir ve hala hızlanmayı / yakınsamayı iyileştirmeyi deneyebileceğim çok şey var, ama şimdilik bu yeterince iyi görünüyor.


2

SmileBASIC

OPTION STRICT
OPTION DEFINT

DEF MSPAINT(IMAGE,WIDTH,HEIGHT,STEPS)
 'read color data
 DIM COLORS[28]
 COPY COLORS%,@COLORS
 @COLORS
 DATA &H000000,&H808080,&H800000
 DATA &H808000,&H008000,&H008080
 DATA &H000080,&H800080,&H808040
 DATA &H004040,&H0080FF,&H004080
 DATA &H8000FF,&H804000,&HFFFFFF
 DATA &HC0C0C0,&HFF0000,&HFFFF00
 DATA &H00FF00,&H00FFFF,&H0000FF
 DATA &HFF00FF,&HFFFF80,&H00FF80
 DATA &H80FFFF,&H8080FF,&HFF0080
 DATA &HFF8040

 'create output array and fill with white
 DIM OUTPUT[WIDTH,HEIGHT]
 FILL OUTPUT,&HFFFFFFFF

 VAR K
 FOR K=1 TO STEPS
  'Pick random position/color
  VAR X=RND(WIDTH -3)
  VAR Y=RND(HEIGHT-3)
  VAR COLOR=COLORS[RND(28)]

  'Extract average (really the sum) color in a 4x4 area.
  'this is less detailed than checking the difference of every pixel
  'but it's better in some ways...
  'corners are included so it will do SOME dithering
  'R1/G1/B1 = average color in original image
  'R2/G2/B2 = average color in current drawing
  'R3/G3/B3 = average color if brush is used
  VAR R1=0,G1=0,B1=0,R2=0,G2=0,B2=0,R3=0,G3=0,B3=0
  VAR R,G,B
  VAR I,J
  FOR J=0 TO 3
   FOR I=0 TO 3
    'original image average
    RGBREAD IMAGE[Y+J,X+I] OUT R,G,B
    INC R1,R
    INC G1,G
    INC B1,B
    'current drawing average
    RGBREAD OUTPUT[Y+J,X+I] OUT R,G,B
    INC R2,R
    INC G2,G
    INC B2,B
    'add the old color to the brush average if we're in a corner
    IF (J==0||J==3)&&(I==0||I==3) THEN
     INC R3,R
     INC G3,G
     INC B3,B
    ENDIF
   NEXT
  NEXT
  'brush covers 12 pixels
  RGBREAD COLOR OUT R,G,B
  INC R3,R*12
  INC G3,G*12
  INC B3,B*12

  'Compare
  BEFORE=ABS(R1-R2)+ABS(G1-G2)+ABS(B1-B2)
  AFTER =ABS(R1-R3)+ABS(G1-G3)+ABS(B1-B3)

  'Draw if better
  IF AFTER<BEFORE THEN
   FILL OUTPUT,COLOR, Y   *WIDTH+X+1,2 ' ##
   FILL OUTPUT,COLOR,(Y+1)*WIDTH+X  ,4 '####
   FILL OUTPUT,COLOR,(Y+2)*WIDTH+X  ,4 '####
   FILL OUTPUT,COLOR,(Y+3)*WIDTH+X+1,2 ' ##
  ENDIF
 NEXT

 RETURN OUTPUT
END

MSPAINT image% [] , genişlik% , yükseklik% , % OUT çıkışı% []

  • image% - 2D [y, x] görüntü verisi ile tamsayı dizisi (32 bit ARGB formatı (alfa yok sayılır))
  • genişlik% - resim genişliği
  • yükseklik% - resim yüksekliği
  • % adımlar - yineleme sayısı
  • çıktı% - çıktı dizisi,% resim ile aynı.

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


bazı örnekler ekleyebilir misiniz?
drham,

Evet, birazdan ekleyeceğim (Yine de görüntüleri aktarmak çok iş, bu yüzden sadece ekranın fotoğraflarını çekmek zorunda kalacağım)
12Me21
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.