MS Paint มีค่าน้อยเกินไป


48

MS Paint เป็นตัวต่อเวลาที่ยอดเยี่ยม แต่มันก็เป็นที่รังเกียจของนักออกแบบกราฟิกส่วนใหญ่ บางทีคนอาจหมดความสนใจเพราะจานสีที่สั่นสะเทือนหรือเพราะระดับการเลิกทำที่ จำกัด ไม่ว่าจะยังคงสามารถสร้างภาพที่สวยงามได้ด้วยแปรงมาตรฐานและจานสีเริ่มต้น

ท้าทาย

โดยใช้เพียงแปรงเริ่มต้น (ตาราง 4x4 ไม่มีมุม) และจานสีเริ่มต้น (28 สีด้านล่าง) ความพยายามที่จะทำซ้ำภาพแหล่งที่มาโดยใช้เทคนิคที่อยู่บนพื้นฐานของการปีนเนินเขาสุ่ม

ป้อนคำอธิบายรูปภาพที่นี่

ขั้นตอนวิธี

ทุกคำตอบจะต้องทำตามอัลกอริธึมพื้นฐานเดียวกัน (Stochastic hillclimb) รายละเอียดสามารถ tweaked ภายในแต่ละขั้นตอน การเคลื่อนไหวถือว่าเป็นเส้นขีดของแปรง (เช่นการคลิกสี)

  1. เดาการเคลื่อนไหวครั้งต่อไป ทำการเดา (ของพิกัดและสี) สำหรับการเคลื่อนไหวถัดไป แต่คุณต้องการ อย่างไรก็ตามการเดาต้องไม่อ้างอิงอิมเมจต้นฉบับ
  2. ใช้การเดา ใช้แปรงกับภาพวาดเพื่อทำให้การเคลื่อนไหว
  3. วัดประโยชน์ของการเคลื่อนไหว โดยการอ้างอิงภาพต้นฉบับให้พิจารณาว่าการเคลื่อนไหวนั้นเป็นประโยชน์ต่อภาพวาดหรือไม่ (เช่นภาพนั้นคล้ายกับภาพต้นฉบับมากขึ้น) หากเป็นประโยชน์ให้เก็บการเคลื่อนไหวไว้มิฉะนั้นให้ยกเลิกการเคลื่อนไหว
  4. ทำซ้ำจนกระทั่งมาบรรจบกัน ไปที่ขั้นตอนที่ 1 และลองเดาต่อไปจนกว่าอัลกอริทึมจะมาบรรจบกันอย่างเพียงพอ ภาพวาดควรคล้ายกับภาพต้นฉบับ ณ จุดนี้มาก

หากโปรแกรมของคุณไม่ตรงกับสี่ขั้นตอนเหล่านี้ก็อาจไม่ใช่ Hillclimb ที่สุ่ม ฉันติดแท็กเป็นประกวดความนิยมเนื่องจากเป้าหมายคือการสร้างอัลกอริทึมการวาดภาพที่น่าสนใจโดยใช้จานสีและแปรงที่มี จำกัด

contraints

  • อัลกอริทึมควรสุ่มในบางวิธี
  • การคาดเดาครั้งถัดไปไม่ควรได้รับอิทธิพลจากรูปภาพต้นฉบับ คุณกำลังเดาการเคลื่อนไหวใหม่แต่ละครั้งแล้วตรวจสอบเพื่อดูว่ามันช่วยหรือไม่ ตัวอย่างเช่นคุณไม่ได้รับอนุญาตให้กำหนดตำแหน่งที่จะวางแปรงตามสีของภาพต้นฉบับ (ซึ่งคล้ายกับการทำซ้ำภาพต้นฉบับซึ่งไม่ใช่เป้าหมาย)

  • คุณได้รับอนุญาตให้มีอิทธิพลต่อตำแหน่งโดยการปรับขั้นตอนของอัลกอริทึมตามที่คุณต้องการ ตัวอย่างเช่นคุณสามารถเริ่มเดาของคุณที่ขอบและย้ายเข้ามาด้านในลากแปรงเพื่อสร้างเส้นสำหรับการเดาแต่ละครั้งหรือตัดสินใจที่จะวาดสีเข้มก่อน คุณได้รับอนุญาตให้อ้างอิงภาพการวนซ้ำก่อนหน้า (แต่ไม่ใช่ภาพต้นฉบับ) เพื่อคำนวณการเคลื่อนไหวที่ต้องการต่อไป สิ่งเหล่านี้อาจมีข้อ จำกัด ตามที่คุณต้องการ (เช่นทำการเดาเฉพาะใน Quadrant บนซ้ายสำหรับการวนซ้ำปัจจุบัน)

  • การวัด "ความแตกต่าง" ระหว่างภาพต้นฉบับและการวนซ้ำปัจจุบันสามารถวัดได้ แต่คุณต้องการตราบใดที่มันไม่ได้คำนวณการเคลื่อนไหวที่เป็นไปได้อื่น ๆ เพื่อพิจารณาว่าการเคลื่อนไหวนี้ถูกพิจารณาว่าเป็น "ดีที่สุด" หรือไม่ ไม่ควรทราบว่าการเคลื่อนไหวในปัจจุบันเป็น "ดีที่สุด" เท่านั้นไม่ว่าจะเหมาะสมกับเกณฑ์การยอมรับหรือไม่ ตัวอย่างเช่นมันอาจจะง่ายเหมือนabs(src.R - current.R) + abs(src.G - current.G) + abs(src.B - current.B)กันสำหรับแต่ละพิกเซลที่ได้รับผลกระทบหรือเทคนิคการแตกต่างของสีที่รู้จักกันดี

จานสี

คุณสามารถดาวน์โหลดจานสีเป็นภาพขนาด 28x1หรือสร้างได้โดยตรงในรหัส

แปรง

แปรงเป็นสี่เหลี่ยม 4x4 ไม่มีมุม นี่เป็นรุ่นที่ปรับสัดส่วนแล้ว:

ป้อนคำอธิบายรูปภาพที่นี่

(รหัสของคุณต้องใช้เวอร์ชัน 4x4)

ตัวอย่าง

การป้อนข้อมูล:

Van Gogh - The Starry Night

เอาท์พุท:

The Starry Night สร้างขึ้น

คุณสามารถดูวิธีขั้นตอนวิธีการดำเนินขั้นพื้นฐานในวิดีโอสั้น ๆ ที่ผมทำ (แต่ละเฟรม 500 ซ้ำ): สตาร์รี่ไนท์ ขั้นตอนแรกน่าสนใจที่จะดู:

ป้อนคำอธิบายรูปภาพที่นี่


1
@ vihan1086: ภาพต้นฉบับไม่โปร่งใส รูปภาพปัจจุบันสามารถขึ้นอยู่กับการวนซ้ำก่อนหน้า (เช่นตัวอย่างของฉันซึ่งมีการเพิ่มจุดใหม่ที่ด้านบนของก่อนหน้า) ถ้านั่นคือสิ่งที่คุณหมายถึง
grovesNL

ฉันไม่เห็นว่าการปีนเขาแบบสุ่มเพิ่มอะไร ... เมื่อพิจารณาว่าคุณสามารถคาดเดาได้ แต่คุณต้องการและทิ้งพวกเขาหากพวกเขาไม่ดีนั่นเป็นสิ่งที่มีประสิทธิภาพเช่นเดียวกับที่ต้องผ่านการคาดเดาจำนวนมากในขณะที่ตรวจสอบและ เลือกหนึ่งที่ดีที่สุด
Sp3000

@ Sp3000: ประเด็นคือคุณไม่รู้จักการเคลื่อนไหว "ดีที่สุด" จนกว่าจะมีการเคลื่อนไหวที่อาจเกิดขึ้น ณ จุดนั้นคุณสามารถเลือกที่จะยอมรับได้หากมันเหมาะสมกับเกณฑ์การยอมรับของคุณเอง (เช่นมันอยู่ใกล้พอ ") เกณฑ์การยอมรับไม่ควรมีความรู้เกี่ยวกับการเคลื่อนไหวที่เป็นไปได้ทั้งหมด (ฉันอาจต้องชี้แจงเรื่องนี้เพิ่มเติม) โดยพื้นฐานแล้วคุณไม่สามารถระบุการเคลื่อนไหว "ดีที่สุด" ล่วงหน้าได้ แต่คุณควรปรับปรุงภาพแบบค่อยเป็นค่อยไป
grovesNL

ด้วยรูปร่างของแปรงนี้เป็นไปไม่ได้ที่จะทาสีพิกเซลมุม (จากตัวอย่างที่คุณได้รับความประทับใจนี้) หรือวางแปรงไว้นอกขอบเขตภาพบางส่วนได้หรือไม่?
Oliphaunt

ความกังวลของ Sp3000 อาจรวมถึงอัลกอริทึมที่กำหนดไว้ดังต่อไปนี้: สำหรับแต่ละพิกเซลให้ลองทุกสี ไม่มีความสุ่มและเหมือนอย่างอื่นมาก แต่ดูเหมือนว่าจะพอดีกับกฎ
Oliphaunt

คำตอบ:


35

JavaScript

วิธีการแก้ปัญหานี้ใช้องค์ประกอบผ้าใบ HTML5 เพื่อดึงข้อมูลภาพ แต่ไม่จำเป็นต้องใช้ HTML นั่นหมายความว่าสามารถใช้งานได้ในคอนโซลของคุณ มันเข้าถึงภาพจานสีเป็นอาร์เรย์; ฉันเก็บทุกสีจากภาพพาเล็ตในอาเรย์) มันส่งออกไปยังคอนโซล (หลังจากเสร็จสิ้น) และยังเก็บผลลัพธ์ในตัวแปร

รุ่นปรับปรุงมากที่สุดของรหัสที่อยู่ในซอ ซอยังใช้อัลกอริทึมที่ดีกว่าเพื่อลดสัญญาณรบกวนในภาพ การปรับปรุงอัลกอริทึมส่วนใหญ่จะแก้ไขฟังก์ชั่น (สูงสุดถึงนาที) ซึ่งทำให้เลือกสีผกผัน

รหัสในรูปของไอคอน MS Paint! (โค้ดที่จัดรูปแบบในซอหรือสแต็กโค้ด)

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

การใช้งาน:

Paint('DATA URI');

ซอ

ซอนั้นใช้crossorigin.meดังนั้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับการแบ่งปันข้ามแหล่งทรัพยากร

ฉันได้ปรับปรุงซอเพื่อให้คุณสามารถปรับค่าบางอย่างเพื่อสร้างภาพวาดที่ดูดีที่สุด สีของรูปภาพบางภาพอาจถูกปิดเพื่อหลีกเลี่ยงปัญหานี้ให้ปรับ accept_rate เพื่อปรับอัลกอริทึม จำนวนที่ต่ำกว่าหมายถึงการไล่ระดับสีที่ดีกว่าจำนวนที่สูงขึ้นจะส่งผลให้สีคมชัดขึ้น


นี่คือซอในฐานะ Stack-Snippet (ไม่อัพเดทในกรณีที่ซอไม่ทำงาน):


เพื่อเป็นการระลึกถึงดาวพลูโตของ New Horizon ฉันได้ใส่รูปพลูโต:

เป็นต้นฉบับ วาด

เป็นต้นฉบับ วาด

สำหรับต่อไปนี้ฉันได้ตั้งค่าเพื่อให้คล้ายกับต้นฉบับมากที่สุด:

ฉันรันสิ่งนี้ด้วยวอลล์เปเปอร์เริ่มต้นของ OS X Yosemite หลังจากปล่อยให้มันรันไปสักพักผลลัพธ์ก็น่าทึ่งอย่างแน่นอน ไฟล์ต้นฉบับมีขนาดใหญ่มาก (26 MB) ดังนั้นฉันจึงปรับขนาดและบีบอัดไฟล์:

ป้อนคำอธิบายรูปภาพที่นี่

คืนเต็มไปด้วยดวงดาว (ฉันใช้ภาพความละเอียดสูงกว่าเพื่อผลลัพธ์ที่ดีกว่า)

ภาพที่ฉันพบใน google: ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่


12

JavaScript + HTML

สุ่ม:

จุดสุ่ม

จัดแนวแบบสุ่ม:

แบ่งผ้าใบออกเป็นสี่เหลี่ยมจตุรัส 4x4 และเลือกจุดสุ่มภายในหนึ่งในสี่เหลี่ยม ออฟเซ็ตจะย้ายกริดดังนั้นคุณสามารถเติมช่องว่างเล็ก ๆ ได้

ห่วง:

สร้างกริดและลูปผ่านจุดทั้งหมด ออฟเซ็ตย้ายกริด การเว้นวรรคกำหนดขนาดของแต่ละเซลล์ (พวกเขาจะเริ่มทับซ้อนกัน)

แตกต่างของสี:

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

ป้อนคำอธิบายรูปภาพที่นี่
RGB: ป้อนคำอธิบายรูปภาพที่นี่
HSL: ป้อนคำอธิบายรูปภาพที่นี่
HSV: ป้อนคำอธิบายรูปภาพที่นี่


เด็ดมาก "เรียกใช้ข้อมูลโค้ด" แบ่งสำหรับฉันเมื่อมันพยายามที่จะตั้งค่าdocument.cookie(หลังจาก 1,000 ซ้ำ) เพราะเอกสารเป็น sandboxed คุกกี้จำเป็นหรือไม่
grovesNL

ไม่ครั้งหนึ่งฉันเคยรันโปรแกรมเป็นเวลาสองสามชั่วโมง แต่แล้วเบราว์เซอร์ของฉันก็ล้มเหลว ดังนั้นฉันจึงอบคุกกี้เป็นข้อมูลสำรอง แต่ฉันจะลบออกเพราะดูเหมือนว่าการแลกเปลี่ยนสแต็คไม่ชอบคุกกี้
Grant Davis

1
มองไปที่รหัสของคุณผมคิดว่ามันอาจจะได้รับประโยชน์จากการเร่งความเร็วเดียวกันที่ผมแนะนำในคำตอบ wolfhammer ของยกเว้นนำไปใช้แทนdoThing loopคุณอาจพบว่าการเพิ่มความเร็วนั้นคุ้มกับบรรทัดพิเศษ ...
trichoplax

1
@trichoplax ขอบคุณมากไม่เพียง แต่การแก้ไขของคุณเพิ่มความเร็วของโปรแกรมของฉันในขณะที่แก้ไขฉันพบและแก้ไขข้อผิดพลาดทางคณิตศาสตร์ที่ฉันทำและโปรแกรมของฉันไม่สร้างจุดสีดำเล็ก ๆ เหล่านั้นอีกต่อไป
Grant Davis

เยี่ยมมาก! ภาพออกใหม่ดูดีขึ้นมาก
trichoplax

8

C # (การดำเนินการอ้างอิง)

นี่คือรหัสที่ใช้ในการสร้างภาพในคำถาม ฉันคิดว่ามันจะมีประโยชน์ที่จะให้บางคนอ้างอิงสำหรับการจัดระเบียบอัลกอริทึมของพวกเขา พิกัดและสีแบบสุ่มสมบูรณ์จะถูกเลือกในแต่ละการเคลื่อนไหว มันทำงานได้อย่างน่าประหลาดใจเมื่อพิจารณาถึงข้อ จำกัด ที่กำหนดโดยขนาดแปรง / เกณฑ์การยอมรับ

ผมใช้อัลกอริทึม CIEDE2000 สำหรับการวัดแตกต่างของสีจากแหล่งที่มาเปิดห้องสมุดColorMine นี่ควรให้การจับคู่สีใกล้ชิดขึ้น (จากมุมมองของมนุษย์) แต่มันก็ไม่ได้เป็นความแตกต่างที่เห็นได้ชัดเจนเมื่อใช้กับจานสีนี้

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

จากนั้นคุณสามารถสร้างชุดรูปภาพ (เช่นวิดีโอของฉัน) โดยการเรียกอินสแตนซ์ในลักษณะที่คล้ายกับรหัสด้านล่าง (ปรับแต่งตามจำนวนการวนซ้ำ / เฟรม / ชื่อที่ต้องการ) อาร์กิวเมนต์แรกคือพา ธ ไฟล์ไปยังอิมเมจต้นทางอาร์กิวเมนต์ที่สองคือพา ธ ไฟล์ไปที่พาเล็ต (ลิงก์ในคำถาม) และอาร์กิวเมนต์ที่สามคือพา ธ ไฟล์สำหรับอิมเมจเอาต์พุต

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++;
            }
        }
    }
}

ฉันค้นหาภาพวาดบนผืนผ้าใบที่มีสีสันออนไลน์และพบภาพด้านล่างซึ่งดูเหมือนจะเป็นภาพทดสอบที่ดี (ซับซ้อน) ลิขสิทธิ์ทั้งหมดเป็นของเจ้าของที่เกี่ยวข้อง

ป้อนคำอธิบายรูปภาพที่นี่ป้อนคำอธิบายรูปภาพที่นี่ แหล่ง

ป้อนคำอธิบายรูปภาพที่นี่ป้อนคำอธิบายรูปภาพที่นี่

แหล่ง

ป้อนคำอธิบายรูปภาพที่นี่ป้อนคำอธิบายรูปภาพที่นี่

แหล่ง


นี่คือวิธีที่ฉันรู้ว่าฉันไม่รู้อะไรเกี่ยวกับอะไรเลย วิธีแก้ปัญหาที่ดี
แบรนดอน

6

ผ้าใบจาวาสคริปต์

ปรับปรุง

ข้อเสนอแนะที่ยอดเยี่ยมในความคิดเห็น ตอนนี้เร็วขึ้นและไม่ทำให้ UI ช้าลง!

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 ฉันมีปัญหาข้ามไซต์กับการโหลดภาพ ฉันจะดูว่าฉันสามารถคิดอะไรบางอย่างออกมา
wolfhammer

1
@trichoplax ไม่มีความมืดไม่ได้ตั้งใจ มันเป็นข้อผิดพลาดที่มีความโปร่งใสในภาพที่สร้างขึ้น โค้ดเปรียบเทียบที่คิดว่าโปร่งใสควรเป็นสีดำ
wolfhammer

@trichoplax ฉันได้เปลี่ยนเป็นเพียงการเปรียบเทียบสีแบบสุ่ม
wolfhammer

1
ฉันคัดลอกรหัสของคุณลงใน jsfiddle แล้วลองทำการทดสอบ มันทำให้การลู่เข้าค่อนข้างเร็วขึ้น คุณอาจต้องการลอง ... สิ่งที่ฉันทำคือล้อมรอบเนื้อหาของฟังก์ชั่นลูปด้วยสำหรับการวนซ้ำเพื่อทำซ้ำเนื้อหา 1000 ครั้ง ซึ่งหมายความว่าเหตุการณ์เมาส์และแป้นพิมพ์จะถูกตรวจสอบทุก ๆ 1,000 ครั้งเท่านั้นแทนที่จะทำซ้ำทุกครั้ง การวนซ้ำของคุณเร็วพอที่การวนซ้ำ 1,000 ครั้งยังคงเป็นการตอบสนองต่อเมาส์และแป้นพิมพ์และประหยัดเวลาในการรอคอยสำหรับการบรรจบกัน :)
trichoplax

1
@tricholplax ว้าวคำแนะนำเหล่านั้นทำให้สิ่งต่าง ๆ เร็วขึ้นมาก ฉันคิดว่าจำเป็นต้องใช้ s / = 4 ฉันไม่ได้ภาพเคลื่อนไหวสีสวย ๆ
wolfhammer

3

มาติกา

มันไม่ได้เร็วขนาดนั้นทั้งหมด แต่อย่างน้อยมันก็ทำให้ได้ภาพที่น่าจดจำดังนั้นฉันจึงมีความสุข

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

เอาท์พุท:

อินพุต เอาท์พุต

อินพุต เอาท์พุต

ผลลัพธ์อาจจะดีขึ้นเล็กน้อยเมื่อมีการทำซ้ำมากขึ้นและยังมีอีกมากที่ฉันสามารถพยายามเร่ง / ปรับปรุงการบรรจบกัน แต่ตอนนี้มันดูดีพอ


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 ภาพ% [] , ความกว้าง% , ความสูง% , ขั้นตอน% OUT เอาท์พุท% []

  • อิมเมจ% - 2D จำนวนเต็มอาร์เรย์ [y, x] พร้อมข้อมูลภาพ (รูปแบบ ARGB 32 บิต (ไม่สนใจ alpha))
  • width% - ความกว้างของภาพ
  • height% - ความสูงของรูปภาพ
  • ขั้นตอน% - จำนวนการวนซ้ำ
  • output% - อาเรย์เอาท์พุทเช่นเดียวกับภาพ%

ป้อนคำอธิบายรูปภาพที่นี่


คุณสามารถเพิ่มตัวอย่างได้ไหม?
drham

ใช่ฉันจะเพิ่มบางอย่างเร็ว ๆ นี้ (มันเป็นงานที่ต้องใช้การถ่ายโอนภาพมากดังนั้นฉันจะต้องถ่ายภาพหน้าจอก่อน)
12Me21
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.