सिर्फ एक बंद वक्र के साथ एक छवि Redraw


74

Vi.sualize.us से प्रेरित है

लक्ष्य

इनपुट एक ग्रेस्केल इमेज है और आउटपुट एक ब्लैक एंड व्हाइट इमेज है। आउटपुट छवि में केवल एक बंद वक्र (लूप) होता है जिसे स्वयं के साथ अंतर करने या स्वयं को छूने की अनुमति नहीं है। लाइन की चौड़ाई पूरी छवि में स्थिर रहेगी। यहाँ चुनौती ऐसा करने के लिए एक एल्गोरिथ्म ढूंढ रही है। आउटपुट को केवल इनपुट छवि का प्रतिनिधित्व करना है, लेकिन किसी भी कलात्मक स्वतंत्रता के साथ। संकल्प इतना महत्वपूर्ण नहीं है लेकिन पहलू अनुपात उसी के बारे में रहना चाहिए।

उदाहरण

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

अधिक परीक्षण छवियों

loch ness आकाश खुरचनेवाला आइंस्टाइन चेकर


2
आप सापेक्ष प्रस्तावों पर कुछ प्रतिबंध लगाना चाह सकते हैं । अन्यथा कोई भी रिज़ॉल्यूशन को काफी बढ़ा सकता है (32 या कुछ का कारक), और फिर प्रत्येक पिक्सेल को उचित औसत तीव्रता के 32x32 ब्लॉक के साथ बदलें। ब्लॉक को सभी कनेक्ट करने के लिए यह काफी आसान होना चाहिए और वे उन्हें इस तरह से व्यवस्थित करते हैं कि सब कुछ एक ही लूप से जुड़ता है।
मार्टिन एंडर

1
यदि लाइन स्वयं को नहीं छू सकती है, तो कोई अंधेरे क्षेत्र नहीं, गहरे रंग का शेड 50% ग्रे होगा
edc65

1
@ मर्टिन The width of the line shall be constant throughout the whole image.लेकिन फिर भी एक उपयोगी संकेत
edc65

2
@ edc65 हां निरंतर, लेकिन आप अभी भी इसे एक पिक्सेल (लगातार) की तुलना में व्यापक बना सकते हैं जिस स्थिति में आपके पास एक पिक्सेल से अलग लाइन के दो भाग हो सकते हैं और फिर वह क्षेत्र 50% औसत तीव्रता से अधिक गहरा होगा।
मार्टिन एंडर

2
@githubphagocyte मुख्य रूप से छवि काले और सफेद रंग में होनी चाहिए, लेकिन इससे कोई फर्क नहीं पड़ता कि इसमें एंटी एलियासिंग प्रभाव है। और आपको तिरछे स्पर्श करने वाले पिक्सेल की इस स्थिति से बचने की कोशिश करनी चाहिए, लेकिन फिर से, यदि ऐसा होता है कि छवि में केवल कुछ ही बार यह ठीक होगा, जब तक आप इसे व्यवस्थित रूप से उपयोग नहीं करते हैं। इनपुट के लिए धन्यवाद। @ edc65: हां मुझे इस बात की जानकारी है, लक्ष्य यह है कि दर्शक अब भी छवि पर एक अलग लाइन (जब ज़ूम इन कर रहा हो) की पहचान कर सकता है।
निर्दोष

जवाबों:


34

जावा: डॉट मैट्रिक्स स्टाइल

चूंकि किसी ने भी इस सवाल का जवाब नहीं दिया है, इसलिए मैं इसे एक शॉट दूंगा। पहले मैं हिल्बर्ट घटता के साथ एक कैनवास भरना चाहता था, लेकिन अंत में मैंने एक सरल दृष्टिकोण चुना है:

डॉट मैट्रिक्स स्टाइल मोना लिसा

यहाँ कोड है:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class LineArt extends JPanel {
    private BufferedImage ref;
    //Images are stored in integers:
    int[] images = new int[] {31, 475, 14683, 469339};
    int[] brightness = new int[] {200,170,120,0};

    public static void main(String[] args) throws Exception {
        new LineArt(args[0]);
    }

    public LineArt(String filename) throws Exception {
        ref = ImageIO.read(new File(filename));
        JFrame frame = new JFrame();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(ref.getWidth()*5, ref.getHeight()*5);
        this.setPreferredSize(new Dimension((ref.getWidth()*5)+20, (ref.getHeight()*5)+20));
        frame.add(new JScrollPane(this));
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, getWidth(), getHeight());
        g2d.translate(10, 10);
        g2d.setColor(Color.BLACK);
        g2d.drawLine(0, 0, 4, 0);
        g2d.drawLine(0, 0, 0, ref.getHeight()*5);

        for(int y = 0; y<ref.getHeight();y++) {
            for(int x = 1; x<ref.getWidth()-1;x++) {
                int light = new Color(ref.getRGB(x, y)).getRed();
                int offset = 0;
                while(brightness[offset]>light) offset++;
                for(int i = 0; i<25;i++) {
                    if((images[offset]&1<<i)>0) {
                        g2d.drawRect((x*5)+i%5, (y*5)+(i/5), 0,0);
                    }
                }
            }
            g2d.drawLine(2, (y*5), 4, (y*5));
            g2d.drawLine((ref.getWidth()*5)-5, (y*5), (ref.getWidth()*5)-1, (y*5));
            if(y%2==0) {
                g2d.drawLine((ref.getWidth()*5)-1, (y*5), (ref.getWidth()*5)-1, (y*5)+4);
            } else {
                g2d.drawLine(2, (y*5), 2, (y*5)+4);
            }
        }
        if(ref.getHeight()%2==0) {
            g2d.drawLine(0, ref.getHeight()*5, 2, ref.getHeight()*5);
        } else {
            g2d.drawLine(0, ref.getHeight()*5, (ref.getWidth()*5)-1, ref.getHeight()*5);
        }
    }
}

अद्यतन : अब यह एक चक्र बनाता है, न कि केवल एक पंक्ति


2
बहुत अच्छा और सरल समाधान, मैंने इसे हल करने के तरीके की कल्पना नहीं की थी लेकिन यह बहुत अच्छा लग रहा है!
त्रुटी

@DenDenDo ने कर्व को छोटा करते हुए फ्लो एनीमेशन को प्लॉट करने का सुझाव दिया। यदि आप सही क्रम में उपयोग किए गए सभी कॉनर पॉइंट्स के निर्देशांक के साथ एक टेक्स्टफाइल (सीएसवी या जो भी आप चाहते हैं) प्रदान कर सकते हैं तो यह बहुत अच्छा होगा। मैंने एनीमेशन की गणना के लिए एक मैटलैब स्क्रिप्ट बनाई - लेकिन निश्चित रूप से आप इसे स्वयं करने के लिए भी स्वतंत्र हैं =)
त्रुटी

35

पायथन: हिल्बर्ट वक्र ( 373 361)

मैंने छवि की तीव्रता के आधार पर चर ग्रैन्युलैरिटी के साथ हिल्बर्ट वक्र बनाने का निर्णय लिया:

import pylab as pl
from scipy.misc import imresize, imfilter
import turtle

# load image
img = pl.flipud(pl.imread("face.png"))

# setup turtle
levels = 8
size = 2**levels
turtle.setup(img.shape[1] * 4.2, img.shape[0] * 4.2)
turtle.setworldcoordinates(0, 0, size, -size)
turtle.tracer(1000, 0)

# resize and blur image
img = imfilter(imresize(img, (size, size)), 'blur')

# define recursive hilbert curve
def hilbert(level, angle = 90):
    if level == 0:
        return
    if level == 1 and img[-turtle.pos()[1], turtle.pos()[0]] > 128:
        turtle.forward(2**level - 1)
    else:
        turtle.right(angle)
        hilbert(level - 1, -angle)
        turtle.forward(1)
        turtle.left(angle)
        hilbert(level - 1, angle)
        turtle.forward(1)
        hilbert(level - 1, angle)
        turtle.left(angle)
        turtle.forward(1)
        hilbert(level - 1, -angle)
        turtle.right(angle)

# draw hilbert curve
hilbert(levels)
turtle.update()

वास्तव में मैंने विस्तार के विभिन्न स्तरों पर निर्णय लेने की योजना बनाई, जैसे "यह स्थान बहुत उज्ज्वल है, मैं पुनरावृत्ति को रोकूंगा और अगले ब्लॉक पर जाऊंगा!"। लेकिन स्थानीय स्तर पर बड़े आंदोलनों के लिए छवि की तीव्रता का मूल्यांकन बहुत गलत है और बदसूरत दिखता है। इसलिए मैंने केवल यह तय करने के साथ कि क्या स्तर 1 को छोड़ना है या किसी अन्य हिल्बर्ट लूप को आकर्षित करना है।

यहाँ पहली परीक्षण छवि पर परिणाम है:

नतीजा

@Githubphagocyte के लिए धन्यवाद प्रतिपादन बहुत तेज है (उपयोग करते हुए turtle.tracer)। इस प्रकार मुझे परिणाम के लिए पूरी रात इंतजार नहीं करना पड़ता है और अपने योग्य बिस्तर पर जा सकता हूं। :)


कुछ कोड गोल्फ

@flawr: "लघु कार्यक्रम"? आपने गोल्फ संस्करण नहीं देखा है! ;)

तो बस मज़े के लिए:

from pylab import*;from scipy.misc import*;from turtle import*
i=imread("f.p")[::-1];s=256;h=i.shape;i=imfilter(imresize(i,(s,s)),'blur')
setup(h[1]*4.2,h[0]*4.2);setworldcoordinates(0,0,s,-s);f=forward;r=right
def h(l,a=90):
 x,y=pos()
 if l==1and i[-y,x]>128:f(2**l-1)
 else:
  if l:l-=1;r(a);h(l,-a);f(1);r(-a);h(l,a);f(1);h(l,a);r(-a);f(1);h(l,-a);r(a)
h(8)

( 373 361 अक्षर। लेकिन यह हमेशा के लिए के बाद से मैं हटाने ले जाएगा turte.tracer(...)आदेश!)


दोष द्वारा एनीमेशन

दोष: मेरा एल्गोरिथ्म @DenDenDo ने मुझे बताया था कि थोड़ा संशोधित है: मुझे हर पुनरावृत्ति में कुछ बिंदुओं को हटाना पड़ा क्योंकि अभिसरण काफी धीमा हो जाएगा। इसलिए वक्र खुद को प्रतिच्छेदन करेगा।

यहाँ छवि विवरण दर्ज करें


1
अच्छी तरह से किया! अगर आप तेज दौड़ना चाहते हैं, तो screen.tracer(0)इसके बजाय प्रयास करें turtle.speed(0)। आपको शुरुआत में स्क्रीन को इंस्टेंट करने की आवश्यकता हो सकती है, लेकिन अगर यह स्क्रीन का एकमात्र उदाहरण है, तो आपके सभी कछुए अपने आप ही इसे सौंप देंगे। फिर screen.update()परिणाम प्रदर्शित करने के लिए अंत में। जब मैंने पहली बार यह खोज की तो मैं गति के अंतर से आश्चर्यचकित था ...
trichoplax

मैं वास्तव में आश्चर्यचकित था कि आप इसे इतने कम कार्यक्रम में कर पाए! लेकिन वैसे भी, बधाई! भग्न ftw =)
निर्दोष

@DenDenDo ने कर्व को छोटा करते हुए फ्लो एनीमेशन को प्लॉट करने का सुझाव दिया। यदि आप सही क्रम में उपयोग किए गए सभी कॉनर पॉइंट्स के निर्देशांक के साथ एक टेक्स्टफाइल (सीएसवी या जो भी आप चाहते हैं) प्रदान कर सकते हैं तो यह बहुत अच्छा होगा। मैंने एनीमेशन की गणना के लिए एक मैटलैब स्क्रिप्ट बनाई - लेकिन निश्चित रूप से आप इसे स्वयं करने के लिए भी स्वतंत्र हैं =)
त्रुटी

@flawr: यहाँ हम चलते हैं।
फाल्को

तो यहाँ कोड है: pastebin.com/wTcwb0nm
निर्दोष

32

पायथन 3.4 - ट्रैवलिंग सेल्समैन समस्या

कार्यक्रम मूल से एक बिंबित छवि बनाता है:

यहाँ छवि विवरण दर्ज करें यहाँ छवि विवरण दर्ज करें

प्रत्येक ब्लैक पिक्सेल के लिए एक बिंदु बेतरतीब ढंग से पिक्सेल केंद्र के पास उत्पन्न होता है और इन बिंदुओं को एक ट्रैवलिंग सेल्समैन समस्या के रूप में माना जाता है । कार्यक्रम नियमित अंतराल पर एक एसवीजी छवि वाली एक HTML फ़ाइल बचाता है क्योंकि यह पथ की लंबाई को कम करने का प्रयास करता है। पथ आत्म-प्रतिच्छेद करना शुरू कर देता है और धीरे-धीरे कई घंटों में कम हो जाता है। अंतत: पथ अब स्वयं प्रतिच्छेदन नहीं है:

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

'''
Traveling Salesman image approximation.
'''

import os.path

from PIL import Image   # This uses Pillow, the PIL fork for Python 3.4
                        # https://pypi.python.org/pypi/Pillow

from random import random, sample, randrange, shuffle
from time import perf_counter


def make_line_picture(image_filename):
    '''Save SVG image of closed curve approximating input image.'''
    input_image_path = os.path.abspath(image_filename)
    image = Image.open(input_image_path)
    width, height = image.size
    scale = 1024 / width
    head, tail = os.path.split(input_image_path)
    output_tail = 'TSP_' + os.path.splitext(tail)[0] + '.html'
    output_filename = os.path.join(head, output_tail)
    points = generate_points(image)
    population = len(points)
    save_dither(points, image)
    grid_cells = [set() for i in range(width * height)]
    line_cells = [set() for i in range(population)]
    print('Initialising acceleration grid')
    for i in range(population):
        recalculate_cells(i, width, points, grid_cells, line_cells)
    while True:
        save_svg(output_filename, width, height, points, scale)
        improve_TSP_solution(points, width, grid_cells, line_cells)


def save_dither(points, image):
    '''Save a copy of the dithered image generated for approximation.'''
    image = image.copy()
    pixels = list(image.getdata())
    pixels = [255] * len(pixels)
    width, height = image.size
    for p in points:
        x = int(p[0])
        y = int(p[1])
        pixels[x+y*width] = 0
    image.putdata(pixels)
    image.save('dither_test.png', 'PNG')


def generate_points(image):
    '''Return a list of points approximating the image.

    All points are offset by small random amounts to prevent parallel lines.'''
    width, height = image.size
    image = image.convert('L')
    pixels = image.getdata()
    points = []
    gap = 1
    r = random
    for y in range(2*gap, height - 2*gap, gap):
        for x in range(2*gap, width - 2*gap, gap):
            if (r()+r()+r()+r()+r()+r())/6 < 1 - pixels[x + y*width]/255:
                        points.append((x + r()*0.5 - 0.25,
                                       y + r()*0.5 - 0.25))
    shuffle(points)
    print('Total number of points', len(points))
    print('Total length', current_total_length(points))
    return points


def current_total_length(points):
    '''Return the total length of the current closed curve approximation.'''
    population = len(points)
    return sum(distance(points[i], points[(i+1)%population])
               for i in range(population))


def recalculate_cells(i, width, points, grid_cells, line_cells):
    '''Recalculate the grid acceleration cells for the line from point i.'''
    for j in line_cells[i]:
        try:
            grid_cells[j].remove(i)
        except KeyError:
            print('grid_cells[j]',grid_cells[j])
            print('i',i)
    line_cells[i] = set()
    add_cells_along_line(i, width, points, grid_cells, line_cells)
    for j in line_cells[i]:
        grid_cells[j].add(i)


def add_cells_along_line(i, width, points, grid_cells, line_cells):
    '''Add each grid cell that lies on the line from point i.'''
    population = len(points)
    start_coords = points[i]
    start_x, start_y = start_coords
    end_coords = points[(i+1) % population]
    end_x, end_y = end_coords
    gradient = (end_y - start_y) / (end_x - start_x)
    y_intercept = start_y - gradient * start_x
    total_distance = distance(start_coords, end_coords)
    x_direction = end_x - start_x
    y_direction = end_y - start_y
    x, y = start_x, start_y
    grid_x, grid_y = int(x), int(y)
    grid_index = grid_x + grid_y * width
    line_cells[i].add(grid_index)
    while True:
        if x_direction > 0:
            x_line = int(x + 1)
        else:
            x_line = int(x)
            if x_line == x:
                x_line = x - 1
        if y_direction > 0:
            y_line = int(y + 1)
        else:
            y_line = int(y)
            if y_line == y:
                y_line = y - 1
        x_line_intersection = gradient * x_line + y_intercept
        y_line_intersection = (y_line - y_intercept) / gradient
        x_line_distance = distance(start_coords, (x_line, x_line_intersection))
        y_line_distance = distance(start_coords, (y_line_intersection, y_line))
        if (x_line_distance > total_distance and
            y_line_distance > total_distance):
            break
        if x_line_distance < y_line_distance:
            x = x_line
            y = gradient * x_line + y_intercept
        else:
            y = y_line
            x = (y_line - y_intercept) / gradient
        grid_x = int(x - (x_direction < 0) * (x == int(x)))
        grid_y = int(y - (y_direction < 0) * (y == int(y)))
        grid_index = grid_x + grid_y * width
        line_cells[i].add(grid_index)


def improve_TSP_solution(points, width, grid_cells, line_cells,
                         performance=[0,0,0], total_length=None):
    '''Apply 3 approaches, allocating time to each based on performance.'''
    population = len(points)
    if total_length is None:
        total_length = current_total_length(points)

    print('Swapping pairs of vertices')
    if performance[0] == max(performance):
        time_limit = 300
    else:
        time_limit = 10
    print('    Aiming for {} seconds'.format(time_limit))
    start_time = perf_counter()
    for n in range(1000000):
        swap_two_vertices(points, width, grid_cells, line_cells)
        if perf_counter() - start_time > time_limit:
            break
    time_taken = perf_counter() - start_time
    old_length = total_length
    total_length = current_total_length(points)
    performance[0] = (old_length - total_length) / time_taken
    print('    Time taken', time_taken)
    print('    Total length', total_length)
    print('    Performance', performance[0])

    print('Moving single vertices')
    if performance[1] == max(performance):
        time_limit = 300
    else:
        time_limit = 10
    print('    Aiming for {} seconds'.format(time_limit))
    start_time = perf_counter()
    for n in range(1000000):
        move_a_single_vertex(points, width, grid_cells, line_cells)
        if perf_counter() - start_time > time_limit:
            break
    time_taken = perf_counter() - start_time
    old_length = total_length
    total_length = current_total_length(points)
    performance[1] = (old_length - total_length) / time_taken
    print('    Time taken', time_taken)
    print('    Total length', total_length)
    print('    Performance', performance[1])

    print('Uncrossing lines')
    if performance[2] == max(performance):
        time_limit = 60
    else:
        time_limit = 10
    print('    Aiming for {} seconds'.format(time_limit))
    start_time = perf_counter()
    for n in range(1000000):
        uncross_lines(points, width, grid_cells, line_cells)
        if perf_counter() - start_time > time_limit:
            break
    time_taken = perf_counter() - start_time        
    old_length = total_length
    total_length = current_total_length(points)
    performance[2] = (old_length - total_length) / time_taken
    print('    Time taken', time_taken)
    print('    Total length', total_length)
    print('    Performance', performance[2])


def swap_two_vertices(points, width, grid_cells, line_cells):
    '''Attempt to find a pair of vertices that reduce length when swapped.'''
    population = len(points)
    for n in range(100):
        candidates = sample(range(population), 2)
        befores = [(candidates[i] - 1) % population
                   for i in (0,1)]
        afters = [(candidates[i] + 1) % population for i in (0,1)]
        current_distance = sum((distance(points[befores[i]],
                                         points[candidates[i]]) +
                                distance(points[candidates[i]],
                                         points[afters[i]]))
                               for i in (0,1))
        (points[candidates[0]],
         points[candidates[1]]) = (points[candidates[1]],
                                   points[candidates[0]])
        befores = [(candidates[i] - 1) % population
                   for i in (0,1)]
        afters = [(candidates[i] + 1) % population for i in (0,1)]
        new_distance = sum((distance(points[befores[i]],
                                     points[candidates[i]]) +
                            distance(points[candidates[i]],
                                     points[afters[i]]))
                           for i in (0,1))
        if new_distance > current_distance:
            (points[candidates[0]],
             points[candidates[1]]) = (points[candidates[1]],
                                       points[candidates[0]])
        else:
            modified_points = tuple(set(befores + candidates))
            for k in modified_points:
                recalculate_cells(k, width, points, grid_cells, line_cells)
            return


def move_a_single_vertex(points, width, grid_cells, line_cells):
    '''Attempt to find a vertex that reduces length when moved elsewhere.'''
    for n in range(100):
        population = len(points)
        candidate = randrange(population)
        offset = randrange(2, population - 1)
        new_location = (candidate + offset) % population
        before_candidate = (candidate - 1) % population
        after_candidate = (candidate + 1) % population
        before_new_location = (new_location - 1) % population
        old_distance = (distance(points[before_candidate], points[candidate]) +
                        distance(points[candidate], points[after_candidate]) +
                        distance(points[before_new_location],
                                 points[new_location]))
        new_distance = (distance(points[before_candidate],
                                 points[after_candidate]) +
                        distance(points[before_new_location],
                                 points[candidate]) +
                        distance(points[candidate], points[new_location]))
        if new_distance <= old_distance:
            if new_location < candidate:
                points[:] = (points[:new_location] +
                             points[candidate:candidate + 1] +
                             points[new_location:candidate] +
                             points[candidate + 1:])
                for k in range(candidate - 1, new_location, -1):
                    for m in line_cells[k]:
                        grid_cells[m].remove(k)
                    line_cells[k] = line_cells[k - 1]
                    for m in line_cells[k]:
                        grid_cells[m].add(k)
                for k in ((new_location - 1) % population,
                          new_location, candidate):
                    recalculate_cells(k, width, points, grid_cells, line_cells)
            else:
                points[:] = (points[:candidate] +
                             points[candidate + 1:new_location] +
                             points[candidate:candidate + 1] +
                             points[new_location:])
                for k in range(candidate, new_location - 3):
                    for m in line_cells[k]:
                        grid_cells[m].remove(k)
                    line_cells[k] = line_cells[k + 1]
                    for m in line_cells[k]:
                        grid_cells[m].add(k)
                for k in ((candidate - 1) % population,
                          new_location - 2, new_location - 1):
                    recalculate_cells(k, width, points, grid_cells, line_cells)
            return


def uncross_lines(points, width, grid_cells, line_cells):
    '''Attempt to find lines that are crossed, and reverse path to uncross.'''
    population = len(points)
    for n in range(100):
        i = randrange(population)
        start_1 = points[i]
        end_1 = points[(i + 1) % population]
        if not line_cells[i]:
            recalculate_cells(i, width, points, grid_cells, line_cells)
        for cell in line_cells[i]:
            for j in grid_cells[cell]:
                if i != j and i != (j+1)%population and i != (j-1)%population:
                    start_2 = points[j]
                    end_2 = points[(j + 1) % population]
                    if are_crossed(start_1, end_1, start_2, end_2):
                        if i < j:
                            points[i + 1:j + 1] = reversed(points[i + 1:j + 1])
                            for k in range(i, j + 1):
                                recalculate_cells(k, width, points, grid_cells,
                                                  line_cells)
                        else:
                            points[j + 1:i + 1] = reversed(points[j + 1:i + 1])
                            for k in range(j, i + 1):
                                recalculate_cells(k, width, points, grid_cells,
                                                  line_cells)
                        return


def are_crossed(start_1, end_1, start_2, end_2):
    '''Return True if the two lines intersect.'''
    if end_1[0]-start_1[0] and end_2[0]-start_2[0]:
        gradient_1 = (end_1[1]-start_1[1])/(end_1[0]-start_1[0])
        gradient_2 = (end_2[1]-start_2[1])/(end_2[0]-start_2[0])
        if gradient_1-gradient_2:
            intercept_1 = start_1[1] - gradient_1 * start_1[0]
            intercept_2 = start_2[1] - gradient_2 * start_2[0]        
            x = (intercept_2 - intercept_1) / (gradient_1 - gradient_2)
            if (x-start_1[0]) * (end_1[0]-x) > 0 and (x-start_2[0]) * (end_2[0]-x) > 0:
                return True


def distance(point_1, point_2):
    '''Return the Euclidean distance between the two points.'''
    return sum((point_1[i] - point_2[i]) ** 2 for i in (0, 1)) ** 0.5


def save_svg(filename, width, height, points, scale):
    '''Save a file containing an SVG path of the points.'''
    print('Saving partial solution\n')
    with open(filename, 'w') as file:
        file.write(content(width, height, points, scale))


def content(width, height, points, scale):
    '''Return the full content to be written to the SVG file.'''
    return (header(width, height, scale) +
            specifics(points, scale) +
            footer()
            )


def header(width, height,scale):
    '''Return the text of the SVG header.'''
    return ('<?xml version="1.0"?>\n'
            '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"\n'
            '    "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">\n'
            '\n'
            '<svg width="{0}" height="{1}">\n'
            '<title>Traveling Salesman Problem</title>\n'
            '<desc>An approximate solution to the Traveling Salesman Problem</desc>\n'
            ).format(scale*width, scale*height)


def specifics(points, scale):
    '''Return text for the SVG path command.'''
    population = len(points)
    x1, y1 = points[-1]
    x2, y2 = points[0]
    x_mid, y_mid = (x1 + x2) / 2, (y1 + y2) / 2
    text = '<path d="M{},{} L{},{} '.format(x1, y1, x2, y2)
    for i in range(1, population):
        text += 'L{},{} '.format(*points[i])
    text += '" stroke="black" fill="none" stroke-linecap="round" transform="scale({0},{0})" vector-effect="non-scaling-stroke" stroke-width="3"/>'.format(scale)
    return text


def footer():
    '''Return the closing text of the SVG file.'''
    return '\n</svg>\n'


if __name__ == '__main__':
    import sys
    arguments = sys.argv[1:]
    if arguments:
        make_line_picture(arguments[0])
    else:
        print('Required argument: image file')

कार्यक्रम समाधान में सुधार के लिए 3 अलग-अलग तरीकों का उपयोग करता है, और प्रत्येक के लिए प्रति सेकंड प्रदर्शन को मापता है। प्रत्येक दृष्टिकोण के लिए आवंटित समय को उस समय सबसे अच्छा प्रदर्शन करने के लिए समय का बहुमत देने के लिए समायोजित किया जाता है।

मैंने शुरू में यह अनुमान लगाने की कोशिश की कि प्रत्येक दृष्टिकोण को आवंटित करने के लिए किस समय का अनुपात है, लेकिन यह पता चला है कि प्रक्रिया के दौरान कौन सा दृष्टिकोण सबसे प्रभावी है, इसलिए यह स्वचालित रूप से समायोजित करने के लिए एक बड़ा अंतर रखता है।

तीन सरल दृष्टिकोण हैं:

  1. यादृच्छिक पर दो अंक चुनें और उन्हें स्वैप करें यदि यह कुल लंबाई नहीं बढ़ाता है।
  2. यादृच्छिक बिंदु पर एक बिंदु चुनें और अंकों की सूची के साथ एक यादृच्छिक ऑफसेट करें और इसे बढ़ाएं यदि लंबाई नहीं बढ़ती है।
  3. यादृच्छिक पर एक लाइन चुनें और जांचें कि क्या कोई अन्य रेखा इसे पार करती है, पथ के किसी भी हिस्से को उलट देती है जो क्रॉस का कारण बनता है।

एप्रोच 3 के लिए एक ग्रिड का उपयोग किया जाता है, जो दी गई सेल से गुजरने वाली सभी लाइनों को सूचीबद्ध करता है। चौराहे के लिए पृष्ठ पर हर पंक्ति की जाँच करने के बजाय, केवल ग्रिड सेल के लिए जाँच की जाती है।


मुझे इस पोस्ट को देखने से पहले एक ब्लॉग पोस्ट से यात्रा करने वाले सेल्समैन समस्या का उपयोग करने का विचार मिला, लेकिन जब मैंने यह उत्तर पोस्ट किया तो मैं इसे ट्रैक नहीं कर सका। मेरा मानना ​​है कि चुनौती में छवि एक ट्रैवलिंग सेल्समैन के दृष्टिकोण का उपयोग करके भी बनाई गई थी, जो तेज घुमावों को दूर करने के लिए किसी प्रकार के मार्ग चौरसाई के साथ संयुक्त थी।

मुझे अभी भी विशिष्ट ब्लॉग पोस्ट नहीं मिल रही है, लेकिन मुझे अब उन मूल पत्रों का संदर्भ मिल गया है जिसमें मोना लिसा का इस्तेमाल ट्रैवलिंग सेल्समैन की समस्या को प्रदर्शित करने के लिए किया गया था

यहां टीएसपी कार्यान्वयन एक हाइब्रिड दृष्टिकोण है जिसे मैंने इस चुनौती के लिए मनोरंजन के लिए प्रयोग किया है। जब मैंने इसे पोस्ट किया था तो मैंने लिंक किए गए पेपर नहीं पढ़े थे। मेरा दृष्टिकोण तुलनात्मक रूप से धीमा है। ध्यान दें कि यहां मेरी छवि 10,000 से कम बिंदुओं का उपयोग करती है, और बिना किसी क्रॉसिंग लाइनों के पर्याप्त रूप से परिवर्तित होने में कई घंटे लगते हैं। कागजात के लिंक में उदाहरण छवि 100,000 अंकों का उपयोग करती है ...

दुर्भाग्य से अधिकांश लिंक अब मृत प्रतीत होते हैं, लेकिन क्रेग एस कापलान और रॉबर्ट बॉश 2005 का पेपर "टीएसपी आर्ट" अभी भी काम करता है और विभिन्न दृष्टिकोणों का एक दिलचस्प अवलोकन देता है।


1
वाह, यह वास्तव में अच्छा है =) (यदि आप चाहते हैं कि मैं एक वक्र छोटा करने वाला एनीमेशन करूं, तो बस एक सीएसवी प्रदान करें या बिंदु निर्देशांक की एक सूचीबद्ध सूची के साथ ऐसा ही कुछ करें।)
त्रुटी

@flawr धन्यवाद! बिंदु निर्देशांक की क्रमबद्ध सूची के लिए, यह मोना लिसा चेहरे के लिए लगभग 10,000 अंक है। यह बड़ी छवियों के लिए लगभग 100,000 अंक होगा। इसलिए मैंने यहाँ SVG टेक्स्ट पोस्ट नहीं किया है ... :)
trichoplax

वैसे आप pastebin.com या कुछ इसी तरह का उपयोग कर सकते हैं, लेकिन मैं आपको मजबूर नहीं करना चाहता, यह आपका निर्णय है (मैं पायथन =) में अच्छा नहीं हूं
निर्दोष

@flawr मैं नहीं चाहूंगा कि कार्यक्रम को चलाने के लिए आपको घंटों और घंटों इंतजार करना पड़े। मैं अपने उत्तर के लिए एक फ्लो एनीमेशन नहीं
जोड़ूंगा,

मुझे कभी इस तरह की चीज़ के लिए TSP का विचार नहीं आया! उठो!
सर्गियोल

24

जावा - दोलन

कार्यक्रम एक बंद रास्ता खींचता है और दोलनों को जोड़ता है जिनके आयाम और आवृत्ति छवि चमक पर आधारित होती है। पथ के "कोनों" में यह सुनिश्चित करने के लिए दोलन नहीं होते हैं कि मार्ग स्वयं को नहीं काटता है।

यहाँ छवि विवरण दर्ज करें

package trace;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import snake.Image;

public class Main5 {


    private final static int MULT = 3;
    private final static int ROWS = 80; // must be an even number
    private final static int COLS = 40;

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

        int [] white = {255, 255, 255};
        for (int y = 0; y < dest.getHeight(); y++) {
            for (int x = 0; x < dest.getWidth(); x++) {
                dest.getRaster().setPixel(x, y, white);
            }
        }
        for (int j = 0; j < ROWS; j++) {
            if (j%2 == 0) {
                for (int i = j==0 ? 0 : 1; i < COLS-1; i++) {
                    drawLine(dest, src, (i+.5)*dest.getWidth()/COLS, (j+.5)*dest.getHeight()/ROWS, (i+1.5)*dest.getWidth()/COLS, (j+.5)*dest.getHeight()/ROWS,
                            i > 1 && i < COLS-2);
                }

                drawLine(dest, src, (COLS-.5)*dest.getWidth()/COLS, (j+.5)*dest.getHeight()/ROWS, (COLS-.5)*dest.getWidth()/COLS, (j+1.5)*dest.getHeight()/ROWS, false);
            } else {
                for (int i = COLS-2; i >= (j == ROWS - 1 ? 0 : 1); i--) {
                    drawLine(dest, src, (i+.5)*dest.getWidth()/COLS, (j+.5)*dest.getHeight()/ROWS, (i+1.5)*dest.getWidth()/COLS, (j+.5)*dest.getHeight()/ROWS,
                            i > 1 && i < COLS-2);
                }
                if (j < ROWS-1) {
                    drawLine(dest, src, (1.5)*dest.getWidth()/COLS, (j+.5)*dest.getHeight()/ROWS, (1.5)*dest.getWidth()/COLS, (j+1.5)*dest.getHeight()/ROWS, false);
                }
            }
            if (j < ROWS-1) {
                drawLine(dest, src, 0.5*dest.getWidth()/COLS, (j+.5)*dest.getHeight()/ROWS, 0.5*dest.getWidth()/COLS, (j+1.5)*dest.getHeight()/ROWS, false);
            }
        }
        ImageIO.write(dest, "png", new File("output.png"));
    }

    private static void drawLine(BufferedImage dest, BufferedImage src, double x1, double y1, double x2, double y2, boolean oscillate) {
        int [] black = {0, 0, 0};

        int col = smoothPixel((int)((x1*.5 + x2*.5) / MULT), (int)((y1*.5+y2*.5) / MULT), src);
        int fact = (255 - col) / 32;
        if (fact > 5) fact = 5;
        double dx = y1 - y2;
        double dy = - (x1 - x2);
        double dist = 2 * (Math.abs(x1 - x2) + Math.abs(y1 - y2)) * (fact + 1);
        for (int i = 0; i <= dist; i++) {
            double amp = oscillate ? (1 - Math.cos(fact * i*Math.PI*2/dist)) * 12 : 0;
            double x = (x1 * i + x2 * (dist - i)) / dist;
            double y = (y1 * i + y2 * (dist - i)) / dist;
            x += dx * amp / COLS;
            y += dy * amp / ROWS;
            dest.getRaster().setPixel((int)x, (int)y, black);
        }
    }

    public static int smoothPixel(int x, int y, BufferedImage src) {
        int sum = 0, count = 0;
        for (int j = -2; j <= 2; j++) {
            for (int i = -2; i <= 2; i++) {
                if (x + i >= 0 && x + i < src.getWidth()) {
                    if (y + j >= 0 && y + j < src.getHeight()) {
                        sum += src.getRGB(x + i, y + j) & 255;
                        count++;
                    }
                }
            }
        }
        return sum / count;
    }
}

एक तुलनीय एल्गोरिथ्म के नीचे जो एक सर्पिल पर आधारित है। ( मुझे पता है कि मार्ग बंद नहीं होता है और यह निश्चित रूप से प्रतिच्छेद करता है , मैं इसे केवल कला के लिए पोस्ट करता हूं :-)

यहाँ छवि विवरण दर्ज करें


मुझे विशेष रूप से सर्पिल का दृश्य प्रभाव पसंद है!
विल

मुझे भी, साझा करने के लिए धन्यवाद! (यदि आप चाहते हैं कि आप पथ बिंदुओं की एक आदेशित सूची भी बना सकते हैं और मैं देखूंगा कि क्या मैं इस एक के साथ एक एनीमेशन भी कर सकता हूं))
7:20 बजे त्रुटिपूर्ण

@ जीथुब आपकी रचनात्मक टिप्पणियों के लिए धन्यवाद।
अरनौद

1
मेरे से +1 - यह अब पूरी तरह से नियमों को फिट करता है, और मुझे उन चिकनी बदलावों से प्यार है जो बदलती आवृत्ति देता है।
ट्राइकोप्लाक्स

21

जावा - पुनरावर्ती पथ

मैं एक 2x3 बंद रास्ते से शुरू करता हूं। मैं पथ के प्रत्येक सेल को स्कैन करता हूं और इसे एक नए 3x3 उप-पथ में विभाजित करता हूं। मैं मूल चित्र चुनने के लिए हर बार कोशिश करता हूं कि मूल चित्र "जैसा दिखता है"। मैं उपरोक्त प्रक्रिया को 4 बार दोहराता हूं।

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें

यहाँ कोड है:

package divide;

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

import javax.imageio.ImageIO;

import snake.Image;

public class Divide {

    private final static int MULT = 3;
    private final static int ITERATIONS = 4;

    public static void main(String[] args) throws IOException {
        BufferedImage src = ImageIO.read(Image.class.getClassLoader().getResourceAsStream("input.png"));
        BufferedImage dest = new BufferedImage(src.getWidth() * MULT, src.getHeight() * MULT, BufferedImage.TYPE_INT_RGB);
        for (int y = 0; y < src.getHeight() * MULT; y++) {
            for (int x = 0; x < src.getWidth() * MULT; x++) {
                dest.getRaster().setPixel(x, y, new int [] {255, 255, 255});
            }
        }
        List<String> tab = new ArrayList<String>();
        tab.add("rg");
        tab.add("||"); 
        tab.add("LJ");

        for (int k = 1; k <= ITERATIONS; k++) {
            boolean choose = k>=ITERATIONS-1;
            // multiply size by 3
            tab = iterate(src, tab, choose);
            // fill in the white space - if needed
            expand(src, tab, " r", " L", "r-", "L-", choose);
            expand(src, tab, "g ", "J ", "-g", "-J", choose);
            expand(src, tab, "LJ", "  ", "||", "LJ", choose);
            expand(src, tab, "  ", "rg", "rg", "||", choose);
            expand(src, tab, "L-J", "   ", "| |", "L-J", choose);
            expand(src, tab, "   ", "r-g", "r-g", "| |", choose);
            expand(src, tab, "| |", "| |", "Lg|", "rJ|", choose);
            expand(src, tab, "--", "  ", "gr", "LJ", choose);
            expand(src, tab, "  ", "--", "rg", "JL", choose);
            expand(src, tab, "| ", "| ", "Lg", "rJ", choose);
            expand(src, tab, " |", " |", "rJ", "Lg", choose);

            for (String s : tab) {
                System.out.println(s);
            }
            System.out.println();
        }

        for (int j = 0; j < tab.size(); j++) {
            String line = tab.get(j);
            for (int i = 0; i < line.length(); i++) {
                char c = line.charAt(i);
                int xleft = i * dest.getWidth() / line.length();
                int xright = (i+1) * dest.getWidth() / line.length();
                int ytop = j * dest.getHeight() / tab.size();
                int ybottom = (j+1) * dest.getHeight() / tab.size();
                int x = (xleft + xright) / 2;
                int y = (ytop + ybottom) / 2;
                if (c == '|') {
                    drawLine(dest, x, ytop, x, ybottom);
                }
                if (c == '-') {
                    drawLine(dest, xleft, y, xright, y);
                }
                if (c == 'L') {
                    drawLine(dest, x, y, xright, y);
                    drawLine(dest, x, y, x, ytop);
                }
                if (c == 'J') {
                    drawLine(dest, x, y, xleft, y);
                    drawLine(dest, x, y, x, ytop);
                }
                if (c == 'r') {
                    drawLine(dest, x, y, xright, y);
                    drawLine(dest, x, y, x, ybottom);
                }
                if (c == 'g') {
                    drawLine(dest, x, y, xleft, y);
                    drawLine(dest, x, y, x, ybottom);
                }
            }

        }

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

    }


    private static void drawLine(BufferedImage dest, int x1, int y1, int x2, int y2) {
        int dist = Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2));
        for (int i = 0; i <= dist; i++) {
            int x = (x1*(dist - i) + x2 * i) / dist;
            int y = (y1*(dist - i) + y2 * i) / dist;
            dest.getRaster().setPixel(x, y, new int [] {0, 0, 0});
        }
    }

    private static void expand(BufferedImage src, List<String> tab, String p1, String p2, String r1, String r2, boolean choose) {
        for (int k = 0; k < (choose ? 2 : 1); k++) {
            while (true) {
                boolean again = false;
                for (int j = 0; j < tab.size() - 1; j++) {
                    String line1 = tab.get(j);
                    String line2 = tab.get(j+1);
                    int baseScore = evaluateLine(src, j, tab.size(), line1) + evaluateLine(src, j+1, tab.size(), line2);
                    for (int i = 0; i <= line1.length() - p1.length(); i++) {
                        if (line1.substring(i, i + p1.length()).equals(p1)
                                && line2.substring(i, i + p2.length()).equals(p2)) {
                            String nline1 = line1.substring(0,  i) + r1 + line1.substring(i + p1.length());
                            String nline2 = line2.substring(0,  i) + r2 + line2.substring(i + p2.length());
                            int nScore = evaluateLine(src, j, tab.size(), nline1) + evaluateLine(src, j+1, tab.size(), nline2);
                            if (!choose || nScore > baseScore) {
                                tab.set(j, nline1);
                                tab.set(j+1, nline2);
                                again = true;
                                break;
                            }
                        }
                    }
                    if (again) break;
                }
                if (!again) break;
            }
            String tmp1 = r1;
            String tmp2 = r2;
            r1 = p1;
            r2 = p2;
            p1 = tmp1;
            p2 = tmp2;
        }
    }

    private static int evaluateLine(BufferedImage src, int j, int tabSize, String line) {
        int [] color = {0, 0, 0};
        int score = 0;
        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);
            int x = i*src.getWidth() / line.length();
            int y = j*src.getHeight() / tabSize;
            src.getRaster().getPixel(x, y, color);
            if (c == ' ' && color[0] >= 128) score++;
            if (c != ' ' && color[0] < 128) score++;
        }
        return score;
    }



    private static List<String> iterate(BufferedImage src, List<String> tab, boolean choose) {
        int [] color = {0, 0, 0};
        List<String> tab2 = new ArrayList<String>();
        for (int j = 0; j < tab.size(); j++) {
            String line = tab.get(j);
            String l1 = "", l2 = "", l3 = "";
            for (int i = 0; i < line.length(); i++) {
                char c = line.charAt(i);
                List<String []> candidates = replace(c);
                String [] choice = null;
                if (choose) {

                    int best = 0;
                    for (String [] candidate : candidates) {
                        int bright1 = 0;
                        int bright2 = 0;
                        for (int j1 = 0; j1<3; j1++) {
                            int y = j*3+j1;
                            for (int i1 = 0; i1<3; i1++) {
                                int x = i*3+i1;
                                char c2 = candidate[j1].charAt(i1);
                                src.getRaster().getPixel(x*src.getWidth()/(line.length()*3), y*src.getHeight()/(tab.size()*3), color);
                                if (c2 != ' ') bright1++;
                                if (color[0] > 128) bright2++;
                            }
                        }
                        int score = Math.abs(bright1 - bright2);
                        if (choice == null || score > best) {
                            best = score;
                            choice = candidate;
                        }

                    }
                } else {
                    choice = candidates.get(0);
                }
                //String [] r = candidates.get(rand.nextInt(candidates.size()));
                String [] r = choice;
                l1 += r[0];
                l2 += r[1];
                l3 += r[2];
            }
            tab2.add(l1);
            tab2.add(l2);
            tab2.add(l3);
        }
        return tab2;
    }

    private static List<String []> replace(char c) {
        if (c == 'r') {
            return Arrays.asList(
                    new String[] {
                    "r-g",
                    "| L",
                    "Lg "},
                    new String[] {
                    "   ",
                    " r-",
                    " | "}, 
                    new String[] {
                    "   ",
                    "r--",
                    "Lg "}, 
                    new String[] {
                    " rg",
                    " |L",
                    " | "},
                    new String[] {
                    "   ",
                    "  r",
                    " rJ"});            
        } else if (c == 'g') {
            return Arrays.asList(
                    new String[] {
                    "r-g",
                    "J |",
                    " rJ"},                 
                    new String[] {
                    "   ",
                    "-g ",
                    " | "},
                    new String[] {
                    "   ",
                    "--g",
                    " rJ"},
                    new String[] {
                    "rg ",
                    "J| ",
                    " | "},
                    new String[] {
                    "   ",
                    "g  ",
                    "Lg "});
        } else if (c == 'L') {
            return Arrays.asList(
                    new String[] {
                    "rJ ",
                    "| r",
                    "L-J"},
                    new String[] {
                    " | ",
                    " L-",
                    "   "},
                    new String[] {
                    "rJ ",
                    "L--",
                    "   "},
                    new String[] {
                    " | ",
                    " |r",
                    " LJ"},
                    new String[] {
                    " Lg",
                    "  L",
                    "   "});
        } else if (c == 'J') {
            return Arrays.asList(
                    new String[] {
                    " Lg",
                    "g |",
                    "L-J"},
                    new String[] {
                    " | ",
                    "-J ",
                    "   "},
                    new String[] {
                    " Lg",
                    "--J",
                    "   "},
                    new String[] {
                    " | ",
                    "g| ",
                    "LJ "},
                    new String[] {
                    "rJ ",
                    "J  ",
                    "   "});
        } else if (c == '-') {
            return Arrays.asList(
                    new String[] {
                    " rg",
                    "g|L",
                    "LJ "},
                    new String[] {
                    "rg ",
                    "J|r",
                    " LJ"},
                    new String[] {
                    "   ",
                    "---",
                    "   "},
                    new String[] {
                    "r-g",
                    "J L",
                    "   "},
                    new String[] {
                    "   ",
                    "g r",
                    "L-J"},
                    new String[] {
                    "rg ",
                    "JL-",
                    "   "},
                    new String[] {
                    " rg",
                    "-JL",
                    "   "},                 
                    new String[] {
                    "   ",
                    "gr-",
                    "LJ "},
                    new String[] {
                    "   ",
                    "-gr",
                    " LJ"}                                      
                    );                      
        } else if (c == '|') {
            return Arrays.asList(
                    new String[] {
                    " Lg",
                    "r-J",
                    "Lg "},
                    new String[] {
                    "rJ ",
                    "L-g",
                    " rJ"},
                    new String[] {
                    " | ",
                    " | ",
                    " | "},
                    new String[] {
                    " Lg",
                    "  |",
                    " rJ"},
                    new String[] {
                    "rJ ",
                    "|  ",
                    "Lg "},
                    new String[] {
                    " Lg",
                    " rJ",
                    " | "},
                    new String[] {
                    " | ",
                    " Lg",
                    " rJ"},
                    new String[] {
                    "rJ ",
                    "Lg ",
                    " | "},
                    new String[] {
                    " | ",
                    "rJ ",
                    "Lg "}                  
                    );
        } else {
            List<String []> ret = new ArrayList<String []>();
            ret.add(
                    new String[] {
                    "   ",
                    "   ",
                    "   "});
            return ret;
        }

    }
}

2
यह अब तक के सबसे नवीन समाधानों में से एक जैसा दिखता है! बैटमैन के लिए +1)
दोष

मुझे यह पसंद है।
ट्राइकोप्लाक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.