สร้างภาพซ้ำโดยใช้บรรทัด


31

เขียนโปรแกรมที่ถ่ายด้วยภาพ RGB สีจริงI , จำนวนเส้นสูงสุดในการวาดLและความยาวต่ำสุดmและความยาวMสูงสุดของแต่ละบรรทัด เอาท์พุทภาพOที่รูปลักษณ์มากที่สุดเท่าที่เป็นไปได้เช่นผมและวาดโดยใช้Lหรือเส้นตรงน้อยลงซึ่งทั้งหมดนี้มีความยาวระหว่างยุคลิดและM

แต่ละบรรทัดจะต้องเป็นหนึ่งสีทึบมีจุดปลายทั้งสองอยู่ในขอบเขตของOและวาดโดยใช้อัลกอริทึมเส้นของ Bresenham (ซึ่งห้องสมุดกราฟิกส่วนใหญ่จะทำเพื่อคุณแล้ว) แต่ละบรรทัดมีความหนาเพียง 1 พิกเซล

ทุกบรรทัดแม้แต่ความยาว 0 ควรมีอย่างน้อยหนึ่งพิกเซล เส้นอาจถูกวาดทับกัน

ก่อนที่จะวาดเส้นใด ๆ คุณอาจเริ่มต้นพื้นหลังของOเป็นสีทึบใด ๆ (ที่อาจขึ้นอยู่กับฉัน )

รายละเอียด

  • Oควรจะมีขนาดเดียวกันกับผม
  • Lจะเป็นจำนวนเต็มที่ไม่ใช่ค่าลบเสมอ มันอาจจะเป็นมากกว่าพื้นที่ของฉัน
  • ม.และเอ็มจะไม่ติดลบตัวเลขทศนิยมกับM > =เมตร ระยะห่างระหว่างสองพิกเซลคือระยะ Euclidean ระหว่างจุดศูนย์กลาง หากระยะทางนี้น้อยกว่าmหรือมากกว่าMดังนั้นจะไม่อนุญาตให้มีบรรทัดระหว่างพิกเซลเหล่านั้น
  • เส้นไม่ควรถูกลดรอยหยัก
  • ความทึบและอัลฟาไม่ควรใช้
  • โปรแกรมของคุณไม่ควรใช้เวลามากกว่าหนึ่งชั่วโมงในการทำงานบนคอมพิวเตอร์ที่ทันสมัยในภาพที่มีขนาดน้อยกว่าหนึ่งล้านพิกเซลและLน้อยกว่า 10,000

ทดสอบภาพ

แน่นอนว่าคุณควรแสดงภาพที่ถูกต้องหรือน่าสนใจที่สุดให้กับเรา (ซึ่งฉันคาดว่าจะเกิดขึ้นเมื่อLอยู่ระหว่าง 5% ถึง 25% ของจำนวนพิกเซลในIและmและMประมาณหนึ่งในสิบของขนาดทแยงมุม)

นี่คือภาพทดสอบบางส่วน (คลิกเพื่อดูภาพต้นฉบับ) คุณสามารถโพสต์ของคุณเอง

Mona Lisa น้ำตก Nighthawks สตาร์รี่ไนท์ สะพานโกลเดนเกต

ภาพที่เรียบง่าย:

บันไดเพนโรสแถบ Mobius โค้งของฮิลแบร์ต

นี่คือการประกวดความนิยม ผลงานที่ได้คะแนนโหวตสูงสุดชนะ

หมายเหตุ

  • มันอาจจะมีประโยชน์ถ้าให้Lมาจากเปอร์เซ็นต์ของพิกเซลทั้งหมดในฉันและค่าสัมบูรณ์ เช่น>>> imageliner I=img.png L=50% m=10 M=20จะเป็นเช่นเดียวกับ>>> imageliner I=img.png L=32 m=10 M=20ถ้าimg.pngเป็นภาพขนาด 8 x 8 พิกเซล สิ่งที่คล้ายกันสามารถทำได้สำหรับMและM สิ่งนี้ไม่จำเป็น
  • ตั้งแต่สายไม่สามารถออกไปจากขอบเขตเส้นที่ยาวที่สุดที่เป็นไปได้จะมีความยาวเส้นทแยงมุมของฉัน การมีMสูงกว่านี้ไม่ควรทำลายอะไรเลย
  • โดยธรรมชาติถ้าmคือ 0 และLมากกว่าหรือเท่ากับจำนวนพิกเซลในI , Oอาจเหมือนกับฉันโดยมีความยาว 0 "เส้น" ในแต่ละตำแหน่งของพิกเซล พฤติกรรมนี้ไม่จำเป็น
  • การสร้างรูปร่างของฉันนั้นสำคัญกว่าการสร้างสี คุณอาจต้องการที่จะมองเข้าไปในการตรวจจับขอบ

เพื่อชี้แจง: อนุญาตให้ใช้ไลบรารีอย่างSimpleCVหรือไม่ และคำตอบอาจมีตัวเลือกสำหรับ I, L, m และ M รวมถึง m = 0 และ L = พื้นที่?
เหตุผล

@epicwisdom ใช่อนุญาตให้ใช้ไลบรารีทั้งหมด (ยกเว้นสิ่งที่มีอยู่แล้วโดยเฉพาะ) รู้สึกอิสระที่จะใช้ประเด็นสำคัญการตรวจจับขอบอะไรก็ตาม อัลกอริทึมของคุณควรใช้กับตัวเลือกที่ถูกต้องของI , L , m , M , รวมm = 0 และL = พื้นที่ (แม้ว่าแน่นอนอัลกอริทึมของคุณอาจดูดีขึ้นสำหรับการปรับพารามิเตอร์โดยเฉพาะ)
งานอดิเรกของ Calvin

ยกตัวอย่างเช่นอัลกอริทึมไลบรารีเฉพาะนี้จะถือว่าเป็นคำตอบที่ไม่ถูกต้องหรือไม่
เหตุผล

@epicwisdom จริง ๆ แล้วฉันจะอนุญาตสิ่งนั้นและสิ่งอื่นที่คล้ายคลึงกัน ดูเหมือนว่ามันจะยังคงใช้การปรับแต่งที่ฉลาดเพื่อให้ภาพออกมาจากเส้นที่ให้คุณ
งานอดิเรกของ Calvin

1
เส้นต้องมีความหนา 1 หรือไม่?
aditsu

คำตอบ:


21

C ++ - บรรทัดที่ค่อนข้างสุ่มและบางเส้น

เริ่มจากสุ่มบางบรรทัด

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

สิ่งที่ฉันสังเกตเห็นและมีอิทธิพลต่อการใช้งาน:

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

ผมได้ทดลองกับตัวเลขบางและเลือกL=0.3*pixel_count(I)และซ้ายและm=10 M=50มันจะให้ผลลัพธ์ที่ดีเริ่มต้นที่ประมาณ0.25ไป0.26สำหรับจำนวนของสาย แต่ผมเลือก 0.3 จะมีห้องพักมากขึ้นสำหรับรายละเอียดที่ถูกต้อง

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

ลบความไม่คู่ควร

ขั้นตอนต่อไปนั้นค่อนข้างแพง (สำหรับ 235k บรรทัดมันใช้เวลาประมาณหนึ่งชั่วโมง แต่มันควรจะดีใน "เวลาหนึ่งชั่วโมงสำหรับ 10k บรรทัดต่อ 1 ล้านพิกเซล") แต่ก็น่าแปลกใจเช่นกัน ฉันผ่านเส้นที่ทาสีไว้ก่อนหน้านี้ทั้งหมดและลบเส้นที่ไม่ทำให้ภาพดีขึ้น นี่ทำให้ฉันทำงานครั้งนี้ด้วยเพียง 97347 บรรทัดที่สร้างภาพต่อไปนี้:

คุณอาจต้องดาวน์โหลดและเปรียบเทียบมันในโปรแกรมดูภาพที่เหมาะสมเพื่อให้เห็นความแตกต่างมากที่สุด

และเริ่มต้นใหม่อีกครั้ง

ตอนนี้ฉันมีเส้นจำนวนมากที่ฉันสามารถทาสีอีกครั้งเพื่อให้มีจำนวนรวม 235929 อีกครั้ง ไม่มากที่จะพูดดังนั้นนี่คือภาพ:

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

การวิเคราะห์สั้น ๆ

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

ภาพเคลื่อนไหว

และเนื่องจากเราทุกคนชื่นชอบอนิเมชั่นนี่คือภาพเคลื่อนไหว gifs ของกระบวนการทั้งหมดสำหรับภาพขนาดเล็กของประตูทอง โปรดทราบว่ามีความแตกต่างอย่างมีนัยสำคัญเนื่องจากรูปแบบ gif (และเนื่องจากผู้สร้างรูปแบบไฟล์ภาพเคลื่อนไหวสีจริงและผู้ผลิตเบราว์เซอร์กำลังต่อสู้กับอัตตาของพวกเขาไม่มีรูปแบบมาตรฐานสำหรับภาพเคลื่อนไหวสีจริงมิฉะนั้นฉันอาจเพิ่ม. mng หรือคล้ายกัน )

บางมากขึ้น

นี่เป็นผลลัพธ์ของรูปภาพอื่น ๆ ตามที่ขอ (คุณอาจต้องเปิดในแท็บใหม่เพื่อไม่ลดขนาด)

ความคิดในอนาคต

การเล่นโดยใช้รหัสสามารถสร้างความแตกต่างที่น่าดึงดูดใจ

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

รหัส

นี่เป็นเพียงฟังก์ชั่นที่มีประโยชน์หลักสองประการโค้ดทั้งหมดไม่พอดีที่นี่และสามารถดูได้ที่http://ideone.com/Z2P6Ls

bmpเรียนrawและraw_lineฟังก์ชั่นทำพิกเซลเข้าถึงและเส้นตามลำดับในวัตถุที่สามารถนำมาเขียนเพื่อ bmp รูปแบบการแสดง (มันเป็นเพียงแค่สับบางนอนอยู่รอบ ๆ และผมคิดว่านี้จะทำให้ค่อนข้างเป็นอิสระจากห้องสมุดใด ๆ )

รูปแบบไฟล์อินพุตคือ PPM

std::pair<bmp,std::vector<line>>  paint_useful( const bmp& orig, bmp& clone, std::vector<line>& retlines, bmp& layer, const std::string& outprefix, size_t x, size_t y )
{
        const size_t pixels = (x*y);
        const size_t lines = 0.3*pixels;
//      const size_t lines = 10000;

//      const size_t start_accurate_color = lines/4;

        std::random_device rnd;

        std::uniform_int_distribution<size_t> distx(0,x-1);
        std::uniform_int_distribution<size_t> disty(0,y-1);
        std::uniform_int_distribution<size_t> col(-15,15);
        std::uniform_int_distribution<size_t> acol(0,255);

        const ssize_t m = 1*1;
        const ssize_t M = 50*50;

        retlines.reserve( lines );

        for (size_t i = retlines.size(); i < lines; ++i)
        {
                size_t x0;
                size_t x1;

                size_t y0;
                size_t y1;

                size_t dist = 0;
                do
                {
                        x0 = distx(rnd);
                        x1 = distx(rnd);

                        y0 = disty(rnd);
                        y1 = disty(rnd);

                        dist = distance(x0,x1,y0,y1);
                }
                while( dist > M || dist < m );

                std::vector<std::pair<int32_t,int32_t>> points = clone.raw_line_pixels(x0,y0,x1,y1);

                ssize_t r = 0;
                ssize_t g = 0;
                ssize_t b = 0;

                for (size_t i = 0; i < points.size(); ++i)
                {
                        r += orig.raw(points[i].first,points[i].second).r;
                        g += orig.raw(points[i].first,points[i].second).g;
                        b += orig.raw(points[i].first,points[i].second).b;
                }

                r += col(rnd);
                g += col(rnd);
                b += col(rnd);

                r /= points.size();
                g /= points.size();
                b /= points.size();

                r %= 255;
                g %= 255;
                b %= 255;

                r = std::max(ssize_t(0),r);
                g = std::max(ssize_t(0),g);
                b = std::max(ssize_t(0),b);

//              r = acol(rnd);
//              g = acol(rnd);
//              b = acol(rnd);

//              if( i > start_accurate_color )
                {
                        ssize_t dp = 0; // accumulated distance of new color to original
                        ssize_t dn = 0; // accumulated distance of current reproduced to original
                        for (size_t i = 0; i < points.size(); ++i)
                        {
                                dp += rgb_distance(
                                                                                orig.raw(points[i].first,points[i].second).r,r,
                                                                                orig.raw(points[i].first,points[i].second).g,g,
                                                                                orig.raw(points[i].first,points[i].second).b,b
                                                                        );

                                dn += rgb_distance(
                                                                                clone.raw(points[i].first,points[i].second).r,orig.raw(points[i].first,points[i].second).r,
                                                                                clone.raw(points[i].first,points[i].second).g,orig.raw(points[i].first,points[i].second).g,
                                                                                clone.raw(points[i].first,points[i].second).b,orig.raw(points[i].first,points[i].second).b
                                                                        );

                        }

                        if( dp > dn ) // the distance to original is bigger, use the new one
                        {
                                --i;
                                continue;
                        }
                        // also abandon if already too bad
//                      if( dp > 100000 )
//                      {
//                              --i;
//                              continue;
//                      }
                }

                layer.raw_line_add(x0,y0,x1,y1,{1u,1u,1u});
                clone.raw_line(x0,y0,x1,y1,{(uint32_t)r,(uint32_t)g,(uint32_t)b});
                retlines.push_back({ (int)x0,(int)y0,(int)x1,(int)y1,(int)r,(int)g,(int)b});

                static time_t last = 0;
                time_t now = time(0);
                if( i % (lines/100) == 0 )
                {
                        std::ostringstream fn;
                        fn << outprefix + "perc_" << std::setw(3) << std::setfill('0') << (i/(lines/100)) << ".bmp"; 
                        clone.write(fn.str());
                        bmp lc(layer);
                        lc.max_contrast_all();
                        lc.write(outprefix + "layer_" + fn.str());
                }

                if( (now-last) > 10 )
                {
                        last = now;
                        static int st = 0;
                        std::ostringstream fn;
                        fn << outprefix + "inter_" << std::setw(8) << std::setfill('0') << i << ".bmp";
                        clone.write(fn.str());

                        ++st;
                }
        }
        clone.write(outprefix + "clone.bmp");
        return { clone, retlines };
}


void erase_bad( std::vector<line>& lines, const bmp& orig )
{
        ssize_t current_score = evaluate(lines,orig);

        std::vector<line> newlines(lines);

        uint32_t deactivated = 0;
        std::cout << "current_score = " << current_score << "\n";
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                newlines[i].active = false;
                ssize_t score = evaluate(newlines,orig);
                if( score > current_score )
                {
                        newlines[i].active = true;
                }
                else
                {
                        current_score = score;
                        ++deactivated;
                }
                if( i % 1000 == 0 )
                {
                        std::ostringstream fn;
                        fn << "erase_" << std::setw(6) << std::setfill('0') << i << ".bmp";
                        bmp tmp(orig);
                        paint(newlines,tmp);
                        tmp.write(fn.str());
                        paint_layers(newlines,tmp);
                        tmp.max_contrast_all();
                        tmp.write("layers_" + fn.str());
                        std::cout << "\r i = " << i << std::flush;
                }
        }
        std::cout << "\n";
        std::cout << "current_score = " << current_score << "\n";
        std::cout << "deactivated = " << deactivated << "\n";


        bmp tmp(orig);

        paint(newlines,tmp);
        tmp.write("newlines.bmp");
        lines.clear();
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                if( newlines[i].is_active() )
                {
                        lines.push_back(newlines[i]);
                }
        }
}

+1 ดีมากจริงๆ คุณมีผลลัพธ์สำหรับภาพทดสอบอื่น ๆ หรือไม่?
นาธาเนียล

1
@Nathaniel: ฉันได้เพิ่มบางอย่าง ภาพที่ "ไม่ธรรมดา" นั้นไม่ได้เกิดขึ้นเนื่องจากการเล่นเกมนั้นเกือบจะสมบูรณ์แบบ
PlasmaHH

17

Java - เส้นสุ่ม

โซลูชันขั้นพื้นฐานที่ดึงเส้นสุ่มและคำนวณสีเฉลี่ยของภาพต้นฉบับ สีพื้นหลังถูกตั้งค่าเป็นสีเฉลี่ยของแหล่งที่มา

L = 5,000, m = 10, M = 50

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

L = 10000, m = 10, M = 50

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

แก้ไข

ฉันได้เพิ่มอัลกอริทึมทางพันธุกรรมที่จัดการประชากรของเส้น ในแต่ละรุ่นเราเก็บเฉพาะบรรทัดที่ดีที่สุด 50% ปล่อยรุ่นอื่นและสร้างรายการใหม่แบบสุ่ม เกณฑ์สำหรับการรักษาเส้นคือ:

  • ระยะห่างของสีภาพต้นฉบับนั้นเล็ก
  • จำนวนทางแยกที่มีเส้นอื่น ๆ (ยิ่งน้อยยิ่งดี)
  • ความยาว (ยิ่งยาวยิ่งดี)
  • มุมของพวกเขากับเพื่อนบ้านที่ใกล้ที่สุด

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

รุ่นแรก (5,000 บรรทัด)

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

รุ่นที่สิบ (5,000 บรรทัด)

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

เล่นกับพารามิเตอร์

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

package line;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import snake.Image;

public class Lines {

    private final static int NB_LINES = 5000;
    private final static int MIN_LENGTH = 10;
    private final static int MAX_LENGTH = 50;

    public static void main(String[] args) throws IOException {     
        BufferedImage src = ImageIO.read(Image.class.getClassLoader().getResourceAsStream("joconde.png"));
        BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);


        int [] bgColor = {0, 0, 0};
        int avgRed = 0, avgGreen = 0, avgBlue = 0, count = 0;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                int colsrc = src.getRGB(x, y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
        }

        bgColor[0] = avgBlue/count; bgColor[1] = avgGreen/count; bgColor[2] = avgRed/count;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                dest.getRaster().setPixel(x, y, bgColor);
            }
        }
        List<List<Point>> lines = new ArrayList<List<Point>>();
        Random rand = new Random();
        for (int i = 0; i < NB_LINES; i++) {
            int length = rand.nextInt(MAX_LENGTH - MIN_LENGTH) + MIN_LENGTH;
            double ang = rand.nextDouble() * Math.PI;
            int lx = (int)(Math.cos(ang) * length); // can be negative or positive
            int ly = (int)(Math.sin(ang) * length); // positive only
            int sx = rand.nextInt(dest.getWidth() -1 - Math.abs(lx));
            int sy = rand.nextInt(dest.getHeight() - 1- Math.abs(ly));
            List<Point> line;
            if (lx > 0) {
                line = line(sx, sy, sx+lx, sy+ly);
            } else {
                line = line(sx+Math.abs(lx), sy, sx, sy+ly);
            }
            lines.add(line);    
        }

        // render the picture
        int [] color = {0, 0, 0};
        for (List<Point> line : lines) {

            avgRed = 0; avgGreen = 0; avgBlue = 0;
            count = 0;
            for (Point p : line) {
                int colsrc = src.getRGB(p.x, p.y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
            avgRed /= count; avgGreen /= count; avgBlue /= count;
            color[0] = avgBlue; color[1] = avgGreen; color[2] = avgRed;
            for (Point p : line) {
                dest.getRaster().setPixel(p.x, p.y, color);
            }

        }
        ImageIO.write(dest, "png", new File("a0.png"));

    }

    private static List<Point> line(int x0, int y0, int x1, int y1) {
        List<Point> points = new ArrayList<Point>();
        int deltax = x1 - x0;
        int deltay = y1 - y0;
        int tmp;
        double error = 0;       
        double deltaerr = 0;
        if (Math.abs(deltax) >= Math.abs(deltay)) {
            if (x0 > x1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltay) / deltax); 
            int y = y0;
            for (int x = x0; x <= x1; x++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (y0 < y1) y++; else y--;
                    error -= 1.0;
                }
            }
        } else {
            if (y0 > y1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltax) / deltay);   // Assume deltay != 0 (line is not horizontal),
            int x = x0;
            for (int y = y0; y <= y1; y++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (x0 < x1) x++; else x--;
                    error -= 1.0;
                }
            }
        }
        return points;
    }
}

ในที่สุดบางคนก็ตอบว่า: ฉันอยากเห็นตัวอย่างเพิ่มเติม
งานอดิเรกของ Calvin

@ Calvin Sure ตอนนี้ฉันกำลังทำงานเพื่อปรับปรุงอัลกอริทึมโดยการรักษาประชากรของเส้นและกำจัดเช่นแย่ลง 20% และสร้างใหม่ใหม่ (อัลกอริทึมทางพันธุกรรมบางชนิด)
Arnaud

ฉันมีบางอย่างเช่นนั้นในใจ แต่ไม่มีเวลาเขียน มองไปข้างหน้าเพื่อแอลพันธุกรรม ผลลัพธ์ :)
aditsu

บางทีคุณต้องการลบเกณฑ์มุมที่เล็กกว่านี้ไหม ทำไมคุณถึงใส่มัน? ภาพต้นฉบับดูดีแม้ว่าเส้นจะไม่มีมุมตัดกันเล็ก ๆ
justhalf

@justhalf เรียบร้อยแล้ว ฉันได้เพิ่มเกณฑ์มุมเพื่อพยายามจำลองแปรงจิตรกร
Arnaud

9

C - เส้นตรง

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

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define SIGN(x) ((x > 0) ? 1 : (x < 0) ? -1 : 0)
#define MIN(x, y) ((x > y) ? y : x)
#define MAX(x, y) ((x > y) ? x : y)

typedef struct {
    size_t width;
    size_t height;

    unsigned char *r;
    unsigned char *g;
    unsigned char *b;
} image;

typedef struct {
    unsigned char r;
    unsigned char g;
    unsigned char b;
} color;

void init_image(image *data, size_t width, size_t height) {
    data->width = width;
    data->height = height;
    data->r = malloc(sizeof(data->r) * data->width * data->height);
    data->g = malloc(sizeof(data->g) * data->width * data->height);
    data->b = malloc(sizeof(data->b) * data->width * data->height);
}

#define BUFFER_LEN 1024
int load_image(const char *filename, image* data) {
    FILE *f = fopen(filename, "r");
    char buffer[BUFFER_LEN];          /* read buffer */
    size_t max_value;
    size_t i;
    fgets(buffer, BUFFER_LEN, f);
    if (strncmp(buffer, "P3", 2) != 0) {
        printf("File begins with %s instead of P3\n", buffer);
        return 0;
    }

    fscanf(f, "%u", &data->width);
    fscanf(f, "%u", &data->height);
    fscanf(f, "%u", &max_value);
    assert(max_value==255);

    init_image(data, data->width, data->height);

    for (i = 0; i < data->width * data->height; i++) {
        fscanf(f, "%hhu", &(data->r[i]));
        fscanf(f, "%hhu", &(data->g[i]));
        fscanf(f, "%hhu", &(data->b[i]));
    }
    fclose(f);

    printf("Read %zux%zu pixels from %s.\n", data->width, data->height, filename);
}

int write_image(const char *filename, image *data) {
    FILE *f = fopen(filename, "w");
    size_t i;
    fprintf(f, "P3\n%zu %zu\n255\n", data->width, data->height);
    for (i = 0; i < data->width * data->height; i++) {
        fprintf(f, "%hhu %hhu %hhu ", data->r[i], data->g[i], data->b[i]);
    }
    fclose(f);
}

unsigned char average(unsigned char *data, size_t data_len) {
    size_t i;
    size_t j;
    size_t hist[256];

    for (i = 0; i < 256; i++) hist[i] = 0;
    for (i = 0; i < data_len; i++) hist[data[i]]++;
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist[i];
        if (j >= data_len / 2) return i;
    }
    return 255;
}

void set_pixel(image *data, size_t x, size_t y, unsigned char r, unsigned char g, unsigned char b) {
    data->r[x + data->width * y] = r;
    data->g[x + data->width * y] = g;
    data->b[x + data->width * y] = b;
}

color get_pixel(image *data, size_t x, size_t y) {
    color ret;
    ret.r = data->r[x + data->width * y];
    ret.g = data->g[x + data->width * y];
    ret.b = data->b[x + data->width * y];
    return ret;
}

void fill(image *data, unsigned char r, unsigned char g, unsigned char b) {
    size_t i;
    for (i = 0; i < data->width * data->height; i++) {
        data->r[i] = r;
        data->g[i] = g;
        data->b[i] = b;
    }
}

void line(image *data, size_t x1, size_t y1, size_t x2, size_t y2, unsigned char r, unsigned char g, unsigned char b) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    set_pixel(data, x, y, r, g, b);

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        set_pixel(data, x, y, r, g, b);
    }
}

color average_line(image *data, size_t x1, size_t y1, size_t x2, size_t y2) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;
    color ret;
    color px;
    size_t i;
    size_t j;
    size_t hist_r[256];
    size_t hist_g[256];
    size_t hist_b[256];
    size_t data_len = 0;

    for (i = 0; i < 256; i++) {
        hist_r[i] = 0;
        hist_g[i] = 0;
        hist_b[i] = 0;
    }

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    px = get_pixel(data, x, y);
    hist_r[px.r]++;
    hist_g[px.g]++;
    hist_b[px.b]++;
    data_len++;

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        px = get_pixel(data, x, y);
        hist_r[px.r]++;
        hist_g[px.g]++;
        hist_b[px.b]++;
        data_len++;
    }

    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_r[i];
        if (j >= data_len / 2) {
            ret.r = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_g[i];
        if (j >= data_len / 2) {
            ret.g = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_b[i];
        if (j >= data_len / 2) {
            ret.b = i;
            break;
        }
    }
    return ret;
}


void lines(image *source, image *dest, size_t L, float m, float M) {
    size_t i, j;
    float dx;
    float mx, my;
    float mm = MAX(MIN(source->width * source->height / L, M), m);
    unsigned char av_r = average(source->r, source->width * source->height);
    unsigned char av_g = average(source->g, source->width * source->height);
    unsigned char av_b = average(source->b, source->width * source->height);
    fill(dest, av_r, av_g, av_b);
    dx = (float)source->width / L;
    mx = 0;
    my = mm / 2;
    for (i = 0; i < L; i++) {
        color avg;
        mx += dx;
        my += (source->height - mm) / 8;
        if (my + mm / 2 > source->height) {
            my = mm / 2 + ((size_t)(my + mm / 2) % (size_t)(source->height - mm));
        }
        avg = average_line(source, mx, my - mm / 2, mx, my + mm / 2);
        line(dest, mx, my - mm / 2, mx, my + mm / 2, avg.r, avg.g, avg.b);
    }
}

int main(int argc, char *argv[]) {
    image source;
    image dest;
    size_t L;
    float m;
    float M;

    load_image(argv[1], &source);
    L = atol(argv[2]);
    m = atof(argv[3]);
    M = atof(argv[4]);

    init_image(&dest, source.width, source.height);
    lines(&source, &dest, L, m, M);


    write_image(argv[5], &dest);
}

L = 5,000, m = 10, M = 50

L = 5,000, m = 10, M = 50

L = 5,000, m = 10, M = 50

L = 5,000, m = 10, M = 50

L = 100000, m = 10, M = 50

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


6

Python 3 อ้างอิงจาก "บรรทัดที่ค่อนข้างสุ่มและบางส่วน" รวมถึงการตรวจจับขอบ sobel

ในทางทฤษฎีรหัสสามารถทำงานได้ตลอดไป (ดังนั้นฉันสามารถเรียกใช้มันค้างคืนเพื่อความสนุกสนาน) แต่มันบันทึกความคืบหน้าดังนั้นภาพทั้งหมดจะถูกนำมาจากเครื่องหมาย 1-10 นาที

มันจะอ่านภาพก่อนแล้วจึงใช้การตรวจจับขอบ sobel เพื่อค้นหามุมของขอบทั้งหมดเพื่อให้แน่ใจว่าเส้นจะไม่บุกรุกกับสีอื่น เมื่อตั้งค่าความยาวสุ่มภายใน (ความยาวสูงสุด, ความยาวสูงสุด) แล้วจะทดสอบเพื่อดูว่ามีส่วนต่อภาพรวมหรือไม่ ในขณะที่เส้นที่เล็กกว่าดีกว่าฉันตั้งความยาวบรรทัดจาก 10-50

from random import randint, uniform
import json
from PIL import Image, ImageDraw, ImageFilter
import math
k=(-1,0,1,-2,0,2,-1,0,1)
k1=(-1,-2,-1,0,0,0,1,2,1)
population=[]
lengthmin=10
lengthmax=50
number_lines=10**8
im=Image.open('0.png')
[x1,y1]=im.size
dx=0
class drawer():
    def __init__(self,genome,score,filename):
        self.genome=genome
        self.score=score
        self.filename=filename
    def initpoint(self,g1):
        g2=self.genome
        im=Image.open('0.png')
        im1=im.filter(ImageFilter.Kernel((3,3),k,1,128))
        im2=im.filter(ImageFilter.Kernel((3,3),k1,1,128))
        im1=im1.filter(ImageFilter.GaussianBlur(radius=4))
        im2=im2.filter(ImageFilter.GaussianBlur(radius=4))
        for x in range(0,number_lines):
            if(x%10**4==0):
                print(x*100/number_lines)
                self.save()
                g1.save('1.png')
            (x,y)=(randint(0,x1-1),randint(0,y1-1))
            w=im1.getpixel((x,y))[0]-128
            z=im2.getpixel((x,y))[0]-128
            w=int(w)
            z=int(z)
            W=(w**2+z**2)**0.5
            if(W!=0):
                w=(w/W)*randint(lengthmin,lengthmax)
                z=(z/W)*randint(lengthmin,lengthmax)
                (w,z)=(z,w)
                (a,b)=(x+w,y+z)
                a=int(a)
                b=int(b)
                x=int(x)
                y=int(y)
                if(a>=x1):
                    a=x1-1
                if(b>=y1):
                    b=y1-1
                if(a<0):
                    a=0
                if(b<0):
                    b=0
                if(x>=x1):
                    x=x1-1
                if(y>=y1):
                    y=y1-1
                if(x<0):
                    x=0
                if(y<0):
                    y=0
                C=[0,0,0]
                D=0
                E=0
                F=0
                G=0
                W=((x-a)**2+(y-b)**2)**0.5
                if(W!=0):
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        C[0]+=im.getpixel((c,d))[0]
                        C[1]+=im.getpixel((c,d))[1]
                        C[2]+=im.getpixel((c,d))[2]
                    C[0]/=W
                    C[1]/=W
                    C[2]/=W
                    C[0]=int(C[0])
                    C[1]=int(C[1])
                    C[2]=int(C[2])
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        E=0
                        D=0
                        D+=(g1.getpixel((c,d))[0]-im.getpixel((c,d))[0])**2
                        D+=(g1.getpixel((c,d))[1]-im.getpixel((c,d))[1])**2
                        D+=(g1.getpixel((c,d))[2]-im.getpixel((c,d))[2])**2
                        F+=D**0.5
                        E+=(im.getpixel((c,d))[0]-C[0])**2
                        E+=(im.getpixel((c,d))[1]-C[1])**2
                        E+=(im.getpixel((c,d))[2]-C[2])**2
                        G+=E**0.5
                    #print((G/W,F/W))
                    if(G<F):
                        for Z in range(0,int(W)):
                            w=(Z/W)
                            (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                            c=int(c)
                            d=int(d)
                            g1.putpixel((c,d),(int(C[0]),int(C[1]),int(C[2])))
                        g2.append((x,y,a,b,int(C[0]%256),int(C[1]%256),int(C[2]%256)))
        return(g1)
    def import_file(self):
        with open(self.filename, 'r') as infile:
            self.genome=json.loads(infile.read())
        print(len(self.genome))
    def save(self):
        with open(self.filename, 'w') as outfile:
            data = json.dumps(self.genome)
            outfile.write(data)
population.append(drawer([],0,'0.txt'))
G=0
g1=Image.new('RGB',(x1,y1),'black')
g1=population[0].initpoint(g1)
g1.save('1.png')

โกธิคอเมริกัน

Escher

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