ভোরোনাই মানচিত্র হিসাবে একটি চিত্র আঁকুন


170

আমার চ্যালেঞ্জ ধারণাটি সঠিক দিকে ঠেলে দেওয়ার জন্য ক্যালভিনের শখের ক্রেডিট।

বিমানের পয়েন্টগুলির একটি সেট বিবেচনা করুন, যা আমরা সাইটগুলি কল করব এবং প্রতিটি সাইটের সাথে একটি রঙ যুক্ত করব। এখন আপনি নিকটতম সাইটের রঙের সাথে প্রতিটি পয়েন্ট রঙ করে পুরো প্লেনটি আঁকতে পারেন। একে ভোরোনাই মানচিত্র (বা ভোরোনাই চিত্র ) বলা হয়। নীতিগতভাবে, ভোরোনাই মানচিত্র যে কোনও দূরত্বের মেট্রিকের জন্য সংজ্ঞায়িত করা যেতে পারে, তবে আমরা সাধারণ ইউক্যালিডিয়ান দূরত্বটি কেবল ব্যবহার করব r = √(x² + y²)( দ্রষ্টব্য: এই চ্যালেঞ্জের সাথে প্রতিযোগিতা করার জন্য আপনাকে কীভাবে এইগুলির একটি গণনা এবং রেন্ডার করতে হবে তা অগত্যা আপনাকে জেনে রাখবেন না))

এখানে 100 টি সাইটের একটি উদাহরণ রয়েছে:

এখানে চিত্র বর্ণনা লিখুন

আপনি যদি কোনও ঘরের দিকে তাকান, তবে সেই ঘরের মধ্যে থাকা সমস্ত পয়েন্ট অন্য কোনও সাইটের তুলনায় সংশ্লিষ্ট সাইটের নিকটে রয়েছে।

আপনার কাজটি হ'ল এই জাতীয় ভোরোনাই মানচিত্র সহ কোনও প্রদত্ত চিত্র আনুমানিক। আপনাকে চিত্রটি কোনও সুবিধাজনক রাস্টার গ্রাফিক্স ফর্ম্যাট এবং সেইসাথে একটি পূর্ণসংখ্যার এন দেওয়া হয়েছে । তারপরে আপনার এন সাইটগুলি এবং প্রতিটি সাইটের জন্য একটি রঙ তৈরি করা উচিত , যেমন এই সাইটের উপর ভিত্তি করে ভোরোনাই মানচিত্রটি যতটা সম্ভব নিবিড়ভাবে ইনপুট চিত্রের সাথে সাদৃশ্যপূর্ণ।

আপনার আউটপুট থেকে ভোরোনাই মানচিত্রটি রেন্ডার করতে আপনি এই চ্যালেঞ্জের নীচে স্ট্যাক স্নিপেট ব্যবহার করতে পারেন, বা আপনি যদি চান তবে আপনি নিজেই এটি সরবরাহ করতে পারেন।

আপনি সাইটগুলির একটি সেট (যদি আপনার প্রয়োজন হয়) থেকে ভোরোনাই মানচিত্রের গণনা করতে অন্তর্নির্মিত বা তৃতীয় পক্ষের ফাংশন ব্যবহার করতে পারেন।

এটি একটি জনপ্রিয়তার প্রতিযোগিতা, সুতরাং সর্বাধিক নেট ভোটের সাথে উত্তর জিতল। ভোটারদের দ্বারা উত্তর বিচার করতে উত্সাহিত করা হয়

  • আসল চিত্রগুলি এবং তাদের রঙগুলি কতটা আনুমানিক হয়।
  • বিভিন্ন ধরণের চিত্রগুলিতে অ্যালগরিদম কতটা ভাল কাজ করে।
  • অ্যালগরিদম ছোট এন এর জন্য কতটা ভাল কাজ করে ।
  • আলগোরিদিম অভিযোজিতভাবে গোষ্ঠীগুলিতে চিত্রের যে অঞ্চলে আরও বিশদ প্রয়োজন সেগুলি নির্দেশ করে।

চিত্র পরীক্ষা করুন

আপনার অ্যালগরিদমটি পরীক্ষা করতে এখানে কয়েকটি চিত্র দেওয়া হয়েছে (আমাদের কিছু সাধারণ সন্দেহভাজন, কিছু নতুন)। বড় সংস্করণ জন্য ছবি ক্লিক করুন।

দুর্দান্ত ওয়েভ অসামাজিক ব্যক্তি সৈকত কর্নেল শনি বাদামি ভালুক Yoshi একজাতীয় বড় বানর কাঁকড়া নীহারিকা জিওবিটস কিড জলপ্রপাত চিত্কার

প্রথম সারির সৈকতটি অলিভিয়া বেল আঁকেন এবং তার অনুমতি সহ অন্তর্ভুক্ত করেছিলেন।

আপনি যদি অতিরিক্ত চ্যালেঞ্জ চান, তবে সাদা পটভূমিতে যোশিকে চেষ্টা করুন এবং তার পেটের লাইনটি ঠিক পান

আপনি এই ইমগুর গ্যালারীটিতে এই পরীক্ষাগুলির সমস্তটি সন্ধান করতে পারেন যেখানে আপনি সেগুলিকে একটি জিপ ফাইল হিসাবে ডাউনলোড করতে পারেন। অ্যালবামটিতে অন্য একটি পরীক্ষা হিসাবে এলোমেলো ভোরোনাই চিত্র রয়েছে। রেফারেন্সের জন্য, এটি তৈরি করা ডেটা এখানে

দয়া করে বিভিন্ন চিত্র এবং এন এর উদাহরণস্বরূপ চিত্রগুলি অন্তর্ভুক্ত করুন , উদাহরণস্বরূপ 100, 300, 1000, 3000 (পাশাপাশি কিছু কিছু নির্দিষ্ট ঘরের নির্দিষ্টকরণের পেস্টবিন)। আপনি যেমন উপযুক্ত দেখেন তেমন কক্ষগুলির মধ্যে কালো প্রান্তগুলি ব্যবহার করতে বা বাদ দিতে পারেন (এটি অন্যের চেয়ে কিছু চিত্রের চেয়ে ভাল লাগতে পারে)। যদিও সাইটগুলি অন্তর্ভুক্ত করবেন না (কোনও পৃথক উদাহরণ ব্যতীত সম্ভবত আপনি যদি আপনার সাইটের স্থান নির্ধারণ কীভাবে কাজ করে তা ব্যাখ্যা করতে চান)।

আপনি যদি বিপুল সংখ্যক ফলাফল দেখাতে চান তবে উত্তরের আকারটি যুক্তিসঙ্গত রাখতে আপনি imgur.com এ একটি গ্যালারী তৈরি করতে পারেন । বিকল্পভাবে, আপনার পোস্টে থাম্বনেইল রাখুন এবং তাদের বড় চিত্রগুলিতে লিঙ্ক তৈরি করুন, যেমন আমি আমার রেফারেন্স উত্তরে করেছিsImgur.com লিঙ্কে (যেমন I3XrT.png-> I3XrTs.png) ফাইলের নামের সাথে সংযুক্ত করে আপনি ছোট থাম্বনেলগুলি পেতে পারেন । এছাড়াও, যদি আপনি খুব ভাল কিছু খুঁজে পান তবে অন্য পরীক্ষার চিত্রগুলি নির্দ্বিধায় ব্যবহার করুন।

পেশকারী

আপনার ফলাফলগুলি রেন্ডার করতে আপনার আউটপুটটিকে নিম্নলিখিত স্ট্যাক স্নিপেটে আটকান। যথাযথ তালিকার বিন্যাস অপ্রাসঙ্গিক, যতক্ষণ না প্রতিটি কক্ষটি ক্রমে 5 টি ভাসমান পয়েন্ট সংখ্যা দ্বারা সুনির্দিষ্ট হয় x y r g b, যেখানে xএবং yকোষের সাইটের স্থানাঙ্ক হয় r g bএবং এটি পরিসরে লাল, সবুজ এবং নীল রঙের চ্যানেল 0 ≤ r, g, b ≤ 1

স্নিপেটটি ঘরের প্রান্তগুলির একটি লাইন প্রস্থ নির্দিষ্ট করার জন্য এবং সেল সাইটগুলি প্রদর্শিত হবে কিনা তা (বেশিরভাগ ডিবাগিংয়ের উদ্দেশ্যে) পরে বিকল্পগুলি সরবরাহ করে। তবে নোট করুন যে আউটপুটটি কেবলমাত্র তখনই সরবরাহ করা হয় যখন ঘর নির্দিষ্টকরণগুলি পরিবর্তন হয় - সুতরাং আপনি যদি অন্য কয়েকটি বিকল্প সংশোধন করেন তবে কোষে বা কোনও কিছুর জন্য একটি স্থান যুক্ত করুন।

সত্যই সুন্দর জেএস ভোরোনাই লাইব্রেরিটি লেখার জন্য রেমন্ড হিলকে প্রচুর কৃতিত্ব ।

সম্পর্কিত চ্যালেঞ্জ


5
@ ফ্রোজয়েডেপায়াস আপনি যে ভোট পেয়েছেন তা দেখে। ;) এটি একটি জনপ্রিয়তা প্রতিযোগিতা। এটি করার সর্বোত্তম উপায়টি অগত্যা নয় । ধারণাটি হ'ল আপনি এটি করার পাশাপাশি আপনার চেষ্টা করার চেষ্টা করেছেন এবং ভোটগুলি প্রতিফলিত হবে যে লোকেরা আপনাকে ভাল কাজ করেছেন তাতে সম্মত হয় কিনা। স্বীকৃতভাবে এগুলিতে একটি নির্দিষ্ট পরিমাণ সাবজেক্টিভিটি রয়েছে। সংশ্লিষ্ট চ্যালেঞ্জ আমি লিঙ্ক, বা কটাক্ষপাত আছে এই এক । আপনি দেখতে পাবেন যে সাধারণত বিভিন্ন ধরণের পন্থা থাকে তবে ভোটিং সিস্টেমটি আরও ভাল সমাধানগুলি শীর্ষে উঠে যায় এবং কোনও বিজয়ীর সিদ্ধান্ত নিতে সহায়তা করে।
মার্টিন ইন্ডার

3
অলিভিয়া তার সমুদ্র সৈকতের এতদূর অনুমানের অনুমোদন দেয়।
অ্যালেক্স এ।

3
@AlexA। ডিভন এতক্ষণে জমা দেওয়া তার মুখের কিছু অনুমানের অনুমোদন দেয়। তিনি কোনও এন = 100 সংস্করণের কোনও বড় অনুরাগী নন;)
জিওবিটস

1
@ জিওবিটস: তিনি যখন বড় হবেন তখন তিনি বুঝতে পারবেন।
অ্যালেক্স এ।

1
সেন্ট্রোডিয়াল ভোরোনাই-ভিত্তিক স্টিপলিং কৌশল সম্পর্কে একটি পৃষ্ঠা এখানে । অনুপ্রেরণার একটি ভাল উত্স (সম্পর্কিত মাস্টার থিসিসে অ্যালগরিদমের সম্ভাব্য উন্নতির একটি সুন্দর আলোচনা রয়েছে)।
চাকরী

উত্তর:


112

পাইথন + স্কিপি + সাইকিট-ইমেজ , ওয়েট পোইসন ডিস্কের নমুনা

আমার সমাধান বরং জটিল। আমি শব্দটি মুছে ফেলতে এবং প্রতিটি পয়েন্টটি কীভাবে 'মজাদার' (স্থানীয় এনট্রপি এবং এজ সনাক্তকরণের সংমিশ্রণটি ব্যবহার করে) ম্যাপিংয়ের জন্য চিত্রটিতে কিছু প্রাকপ্রসেসিং করি:

তারপরে আমি পুইসন ডিস্কের সাথে মোড় নিয়ে নমুনা ব্যবহার করে নমুনা পয়েন্টগুলি বেছে নিই : বৃত্তটির দূরত্বটি আমরা আগে নির্ধারিত ওজন দ্বারা নির্ধারিত হয়।

তারপরে একবার আমার স্যাম্পলিং পয়েন্টগুলি উপস্থিত হয়ে আমি ভেরোনাই বিভাগগুলিতে চিত্রটি ভাগ করে নেব এবং প্রতিটি সেগমেন্টের অভ্যন্তরে রঙের মানগুলির L * a * b * গড় প্রতিটি বিভাগে অর্পণ করব।

আমার প্রচুর হিউরিস্টিকস রয়েছে এবং নমুনা পয়েন্টের সংখ্যাটি কাছাকাছি রয়েছে কিনা তা নিশ্চিত করতে আমারও কিছুটা গণিত করতে হবে N। আমি সামান্যN ওভারশুট করার মাধ্যমে ঠিক পাই , এবং তারপরে একটি হিউরিস্টিকের সাথে কিছু পয়েন্ট ফেলে আছি drop

রানটাইমের ক্ষেত্রে, এই ফিল্টারটি সস্তা নয় , তবে নীচের কোনও চিত্র বানাতে 5 সেকেন্ডের বেশি সময় নেয়নি।

আরও ঝামেলা ছাড়া:

import math
import random
import collections
import os
import sys
import functools
import operator as op
import numpy as np
import warnings

from scipy.spatial import cKDTree as KDTree
from skimage.filters.rank import entropy
from skimage.morphology import disk, dilation
from skimage.util import img_as_ubyte
from skimage.io import imread, imsave
from skimage.color import rgb2gray, rgb2lab, lab2rgb
from skimage.filters import sobel, gaussian_filter
from skimage.restoration import denoise_bilateral
from skimage.transform import downscale_local_mean


# Returns a random real number in half-open range [0, x).
def rand(x):
    r = x
    while r == x:
        r = random.uniform(0, x)
    return r


def poisson_disc(img, n, k=30):
    h, w = img.shape[:2]

    nimg = denoise_bilateral(img, sigma_range=0.15, sigma_spatial=15)
    img_gray = rgb2gray(nimg)
    img_lab = rgb2lab(nimg)

    entropy_weight = 2**(entropy(img_as_ubyte(img_gray), disk(15)))
    entropy_weight /= np.amax(entropy_weight)
    entropy_weight = gaussian_filter(dilation(entropy_weight, disk(15)), 5)

    color = [sobel(img_lab[:, :, channel])**2 for channel in range(1, 3)]
    edge_weight = functools.reduce(op.add, color) ** (1/2) / 75
    edge_weight = dilation(edge_weight, disk(5))

    weight = (0.3*entropy_weight + 0.7*edge_weight)
    weight /= np.mean(weight)
    weight = weight

    max_dist = min(h, w) / 4
    avg_dist = math.sqrt(w * h / (n * math.pi * 0.5) ** (1.05))
    min_dist = avg_dist / 4

    dists = np.clip(avg_dist / weight, min_dist, max_dist)

    def gen_rand_point_around(point):
        radius = random.uniform(dists[point], max_dist)
        angle = rand(2 * math.pi)
        offset = np.array([radius * math.sin(angle), radius * math.cos(angle)])
        return tuple(point + offset)

    def has_neighbours(point):
        point_dist = dists[point]
        distances, idxs = tree.query(point,
                                    len(sample_points) + 1,
                                    distance_upper_bound=max_dist)

        if len(distances) == 0:
            return True

        for dist, idx in zip(distances, idxs):
            if np.isinf(dist):
                break

            if dist < point_dist and dist < dists[tuple(tree.data[idx])]:
                return True

        return False

    # Generate first point randomly.
    first_point = (rand(h), rand(w))
    to_process = [first_point]
    sample_points = [first_point]
    tree = KDTree(sample_points)

    while to_process:
        # Pop a random point.
        point = to_process.pop(random.randrange(len(to_process)))

        for _ in range(k):
            new_point = gen_rand_point_around(point)

            if (0 <= new_point[0] < h and 0 <= new_point[1] < w
                    and not has_neighbours(new_point)):
                to_process.append(new_point)
                sample_points.append(new_point)
                tree = KDTree(sample_points)
                if len(sample_points) % 1000 == 0:
                    print("Generated {} points.".format(len(sample_points)))

    print("Generated {} points.".format(len(sample_points)))

    return sample_points


def sample_colors(img, sample_points, n):
    h, w = img.shape[:2]

    print("Sampling colors...")
    tree = KDTree(np.array(sample_points))
    color_samples = collections.defaultdict(list)
    img_lab = rgb2lab(img)
    xx, yy = np.meshgrid(np.arange(h), np.arange(w))
    pixel_coords = np.c_[xx.ravel(), yy.ravel()]
    nearest = tree.query(pixel_coords)[1]

    i = 0
    for pixel_coord in pixel_coords:
        color_samples[tuple(tree.data[nearest[i]])].append(
            img_lab[tuple(pixel_coord)])
        i += 1

    print("Computing color means...")
    samples = []
    for point, colors in color_samples.items():
        avg_color = np.sum(colors, axis=0) / len(colors)
        samples.append(np.append(point, avg_color))

    if len(samples) > n:
        print("Downsampling {} to {} points...".format(len(samples), n))

    while len(samples) > n:
        tree = KDTree(np.array(samples))
        dists, neighbours = tree.query(np.array(samples), 2)
        dists = dists[:, 1]
        worst_idx = min(range(len(samples)), key=lambda i: dists[i])
        samples[neighbours[worst_idx][1]] += samples[neighbours[worst_idx][0]]
        samples[neighbours[worst_idx][1]] /= 2
        samples.pop(neighbours[worst_idx][0])

    color_samples = []
    for sample in samples:
        color = lab2rgb([[sample[2:]]])[0][0]
        color_samples.append(tuple(sample[:2][::-1]) + tuple(color))

    return color_samples


def render(img, color_samples):
    print("Rendering...")
    h, w = [2*x for x in img.shape[:2]]
    xx, yy = np.meshgrid(np.arange(h), np.arange(w))
    pixel_coords = np.c_[xx.ravel(), yy.ravel()]

    colors = np.empty([h, w, 3])
    coords = []
    for color_sample in color_samples:
        coord = tuple(x*2 for x in color_sample[:2][::-1])
        colors[coord] = color_sample[2:]
        coords.append(coord)

    tree = KDTree(coords)
    idxs = tree.query(pixel_coords)[1]
    data = colors[tuple(tree.data[idxs].astype(int).T)].reshape((w, h, 3))
    data = np.transpose(data, (1, 0, 2))

    return downscale_local_mean(data, (2, 2, 1))


if __name__ == "__main__":
    warnings.simplefilter("ignore")

    img = imread(sys.argv[1])[:, :, :3]

    print("Calibrating...")
    mult = 1.02 * 500 / len(poisson_disc(img, 500))

    for n in (100, 300, 1000, 3000):
        print("Sampling {} for size {}.".format(sys.argv[1], n))

        sample_points = poisson_disc(img, mult * n)
        samples = sample_colors(img, sample_points, n)
        base = os.path.basename(sys.argv[1])
        with open("{}-{}.txt".format(os.path.splitext(base)[0], n), "w") as f:
            for sample in samples:
                f.write(" ".join("{:.3f}".format(x) for x in sample) + "\n")

        imsave("autorenders/{}-{}.png".format(os.path.splitext(base)[0], n),
            render(img, samples))

        print("Done!")

চিত্র

যথাযথভাবে N100, 300, 1000 এবং 3000:

অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ
অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ


2
আমি এটি পছন্দ করি; এটি দেখতে কিছুটা ধূমপান করা কাচের মতো।
বব দ্য আশ্চর্যজনক

3
আমি এটির সাথে কিছুটা গোলযোগ পেয়েছি এবং আপনি ভাল ফলাফল পেয়েছেন, বিশেষত নিম্ন ত্রিভুজ চিত্রগুলির জন্য, আপনি যদি denoise_tv_bregman এর সাথে denoise_bilatteral প্রতিস্থাপন করেন। এটি এর denoising আরও প্যাচ উত্পন্ন, যা সাহায্য করে।
এলক্লেইন

@ LKlevin আপনি কোন ওজন ব্যবহার করেছেন?
orlp

আমি ওজন হিসাবে 1.0 ব্যবহার করেছি।
এলক্লেইন

65

সি ++

আমার পদ্ধতিটি বেশ ধীর, তবে আমি যে ফলাফলগুলি দিয়েছি তার গুণগত মান নিয়ে খুব খুশি, বিশেষত প্রান্তগুলি সংরক্ষণের ক্ষেত্রে। উদাহরণস্বরূপ, এখানে প্রতি 1000 টি সাইট সহ যোশি এবং কর্নেল বক্স রয়েছে:

দুটি প্রধান অংশ যা এটি টিক দেয়। প্রথমটি, ফাংশনটিতে অষ্টিতevaluate() প্রার্থী সাইটের অবস্থানগুলির একটি সেট নেয়, তাদের উপর সর্বোত্তম রঙ নির্ধারণ করে এবং লক্ষ্য চিত্রের বিপরীতে রেন্ডারড ভোরোনাই টেসেললেসনের পিএসএনআরের জন্য একটি স্কোর প্রদান করে। প্রতিটি সাইটের রঙগুলি সাইটের চারপাশে ঘর দ্বারা আচ্ছাদিত লক্ষ্য চিত্র পিক্সেলগুলির গড় দ্বারা নির্ধারিত হয়। আমি ভেলফোর্ডের অ্যালগরিদম ব্যবহার করি প্রতিটি কক্ষের জন্য সর্বোত্তম রঙ এবং ফলস্বরূপ, এমএসই এবং পিএসএনআরের মধ্যে সম্পর্ককে কাজে লাগিয়ে চিত্রের উপরে কেবল একটি পাস ব্যবহার করে ফলাফল পিএসএনআর both রঙের বিষয়ে বিশেষভাবে বিবেচনা না করেই এটি সাইটের অবস্থানগুলির সর্বোত্তম সেট সন্ধান করতে সমস্যাটিকে হ্রাস করে।

এরপরে দ্বিতীয় অংশটি মূর্ত অবস্থায় main()এই সেটটি সন্ধান করার চেষ্টা করে। এটি এলোমেলোভাবে পয়েন্টগুলির একটি সেট চয়ন করে শুরু হয়। তারপরে, প্রতিটি পদক্ষেপে এটি একটি পয়েন্ট সরিয়ে দেয় (রাউন্ড-রবিন) এবং এটিকে প্রতিস্থাপনের জন্য এলোমেলো প্রার্থী পয়েন্টগুলির একটি সেট পরীক্ষা করে। গুচ্ছের সর্বাধিক পিএসএনআর যা দেয় তা গ্রহণ এবং রাখা হয়। কার্যকরভাবে, এর ফলে সাইটটি একটি নতুন স্থানে লাফিয়ে ওঠে এবং সাধারণত চিত্রটিকে কিছুটা হলেও উন্নত করে। মনে রাখবেন যে অ্যালগোরিদম ইচ্ছাকৃতভাবে প্রার্থী হিসাবে আসল অবস্থানটি ধরে রাখে না । কখনও কখনও এর অর্থ হ'ল জাম্প সামগ্রিক চিত্রের গুণমানকে কমিয়ে দেয়। এটি হতে দেওয়া স্থানীয় ম্যাক্সিমায় আটকে যাওয়া এড়াতে সহায়তা করে। এটি একটি থামার মানদণ্ড দেয়; এখনও অবধি পাওয়া সাইটের সেরা সেটটিতে কোনও উন্নতি না করেই কয়েকটি নির্দিষ্ট পদক্ষেপের পরে প্রোগ্রামটি সমাপ্ত হয়।

নোট করুন যে এই বাস্তবায়নটি মোটামুটি বেসিক এবং সহজেই সাইটের সংখ্যা বাড়ার সাথে সাথে সিপিইউ-কোর সময়টিতে খুব সহজেই সময় নিতে পারে। এটি প্রতিটি পরীক্ষার্থীর জন্য সম্পূর্ণ ভোরোনাই মানচিত্রের পুনঃনির্মাণ করে এবং ব্রেস্ট ফোর্স প্রতিটি পিক্সেলের জন্য সমস্ত সাইটের দূরত্ব পরীক্ষা করে। যেহেতু প্রতিটি ক্রিয়াকলাপে একবারে একটি পয়েন্ট সরানো এবং অন্যটি যুক্ত করা থাকে, তাই প্রতিটি পদক্ষেপে চিত্রটিতে আসল পরিবর্তনগুলি মোটামুটি স্থানীয় হতে চলেছে। ভোরোনাই চিত্রটি দক্ষতার সাথে বাড়ানোর জন্য অ্যালগরিদম রয়েছে এবং আমি বিশ্বাস করি তারা এই অ্যালগরিদমকে একটি দুর্দান্ত গতিবেগ দেবে। এই প্রতিযোগিতার জন্য, যাইহোক, আমি জিনিসগুলি সহজ এবং নিষ্ঠুরূপে রাখতে বেছে নিয়েছি।

কোড

#include <cstdlib>
#include <cmath>
#include <string>
#include <vector>
#include <fstream>
#include <istream>
#include <ostream>
#include <iostream>
#include <algorithm>
#include <random>

static auto const decimation = 2;
static auto const candidates = 96;
static auto const termination = 200;

using namespace std;

struct rgb {float red, green, blue;};
struct img {int width, height; vector<rgb> pixels;};
struct site {float x, y; rgb color;};

img read(string const &name) {
    ifstream file{name, ios::in | ios::binary};
    auto result = img{0, 0, {}};
    if (file.get() != 'P' || file.get() != '6')
        return result;
    auto skip = [&](){
        while (file.peek() < '0' || '9' < file.peek())
            if (file.get() == '#')
                while (file.peek() != '\r' && file.peek() != '\n')
                    file.get();
    };
     auto maximum = 0;
     skip(); file >> result.width;
     skip(); file >> result.height;
     skip(); file >> maximum;
     file.get();
     for (auto pixel = 0; pixel < result.width * result.height; ++pixel) {
         auto red = file.get() * 1.0f / maximum;
         auto green = file.get() * 1.0f / maximum;
         auto blue = file.get() * 1.0f / maximum;
         result.pixels.emplace_back(rgb{red, green, blue});
     }
     return result;
 }

 float evaluate(img const &target, vector<site> &sites) {
     auto counts = vector<int>(sites.size());
     auto variance = vector<rgb>(sites.size());
     for (auto &site : sites)
         site.color = rgb{0.0f, 0.0f, 0.0f};
     for (auto y = 0; y < target.height; y += decimation)
         for (auto x = 0; x < target.width; x += decimation) {
             auto best = 0;
             auto closest = 1.0e30f;
             for (auto index = 0; index < sites.size(); ++index) {
                 float distance = ((x - sites[index].x) * (x - sites[index].x) +
                                   (y - sites[index].y) * (y - sites[index].y));
                 if (distance < closest) {
                     best = index;
                     closest = distance;
                 }
             }
             ++counts[best];
             auto &pixel = target.pixels[y * target.width + x];
             auto &color = sites[best].color;
             rgb delta = {pixel.red - color.red,
                          pixel.green - color.green,
                          pixel.blue - color.blue};
             color.red += delta.red / counts[best];
             color.green += delta.green / counts[best];
             color.blue += delta.blue / counts[best];
             variance[best].red += delta.red * (pixel.red - color.red);
             variance[best].green += delta.green * (pixel.green - color.green);
             variance[best].blue += delta.blue * (pixel.blue - color.blue);
         }
     auto error = 0.0f;
     auto count = 0;
     for (auto index = 0; index < sites.size(); ++index) {
         if (!counts[index]) {
             auto x = min(max(static_cast<int>(sites[index].x), 0), target.width - 1);
             auto y = min(max(static_cast<int>(sites[index].y), 0), target.height - 1);
             sites[index].color = target.pixels[y * target.width + x];
         }
         count += counts[index];
         error += variance[index].red + variance[index].green + variance[index].blue;
     }
     return 10.0f * log10f(count * 3 / error);
 }

 void write(string const &name, int const width, int const height, vector<site> const &sites) {
     ofstream file{name, ios::out};
     file << width << " " << height << endl;
     for (auto const &site : sites)
         file << site.x << " " << site.y << " "
              << site.color.red << " "<< site.color.green << " "<< site.color.blue << endl;
 }

 int main(int argc, char **argv) {
     auto rng = mt19937{random_device{}()};
     auto uniform = uniform_real_distribution<float>{0.0f, 1.0f};
     auto target = read(argv[1]);
     auto sites = vector<site>{};
     for (auto point = atoi(argv[2]); point; --point)
         sites.emplace_back(site{
             target.width * uniform(rng),
             target.height * uniform(rng)});
     auto greatest = 0.0f;
     auto remaining = termination;
     for (auto step = 0; remaining; ++step, --remaining) {
         auto best_candidate = sites;
         auto best_psnr = 0.0f;
         #pragma omp parallel for
         for (auto candidate = 0; candidate < candidates; ++candidate) {
             auto trial = sites;
             #pragma omp critical
             {
                 trial[step % sites.size()].x = target.width * (uniform(rng) * 1.2f - 0.1f);
                 trial[step % sites.size()].y = target.height * (uniform(rng) * 1.2f - 0.1f);
             }
             auto psnr = evaluate(target, trial);
             #pragma omp critical
             if (psnr > best_psnr) {
                 best_candidate = trial;
                 best_psnr = psnr;
             }
         }
         sites = best_candidate;
         if (best_psnr > greatest) {
             greatest = best_psnr;
             remaining = termination;
             write(argv[3], target.width, target.height, sites);
         }
         cout << "Step " << step << "/" << remaining
              << ", PSNR = " << best_psnr << endl;
     }
     return 0;
 }

চলমান

প্রোগ্রামটি স্ব-অন্তর্ভুক্ত এবং স্ট্যান্ডার্ড লাইব্রেরির বাইরে কোনও বাহ্যিক নির্ভরতা নেই তবে এর জন্য বাইনারি পিপিএম ফর্ম্যাটে ইমেজগুলি থাকা দরকার । চিত্রগুলি পিপিএম-তে রূপান্তর করতে আমি ইমেজম্যাগিক ব্যবহার করি , যদিও জিআইএমপি এবং বেশ কয়েকটি অন্যান্য প্রোগ্রাম এটিও করতে পারে।

এটি সংকলন করতে প্রোগ্রামটি সংরক্ষণ করুন voronoi.cppএবং তারপরে চালান:

g++ -std=c++11 -fopenmp -O3 -o voronoi voronoi.cpp

আমি আশা করি এটি সম্ভবত উইন্ডোজে ভিজ্যুয়াল স্টুডিওর সাম্প্রতিক সংস্করণগুলির সাথে কাজ করবে, যদিও আমি এটি চেষ্টা করে নি। আপনি নিশ্চিত করতে চাইবেন যে আপনি সি ++ 11 বা আরও ভাল এবং আপনি যদি ওপেনএমপি সক্ষম করেন তবে সংকলন করছেন। ওপেনএমপি কঠোরভাবে প্রয়োজনীয় নয়, তবে মৃত্যুদন্ড কার্যকর করার সময়টিকে আরও সহনীয় করে তুলতে এটি অনেকখানি সহায়তা করে।

এটি চালানোর জন্য, এর মতো কিছু করুন:

./voronoi cornell.ppm 1000 cornell-1000.txt

পরবর্তী ফাইলটি যেমন যায় তেমন সাইটের ডেটা দিয়ে আপডেট করা হবে। প্রথম লাইনে চিত্রটির প্রস্থ এবং উচ্চতা থাকবে, তারপরে সমস্যার বর্ণনায় জাভাস্ক্রিপ্ট রেন্ডারারে অনুলিপি এবং আটকানোর জন্য উপযুক্ত x, y, r, g, b এর রেখা থাকবে।

প্রোগ্রামের শীর্ষে থাকা তিনটি ধ্রুবক আপনাকে গতি বনাম মানের জন্য এটি টিউন করতে দেয়। decimationফ্যাক্টর যখন রঙ এবং PSNR জন্য সাইট একটি সেট মূল্যায়নের লক্ষ্য ইমেজ coarsens। এটি যত বেশি হবে তত দ্রুত প্রোগ্রামটি চলবে। এটিকে 1 এ সেট করা পুরো রেজোলিউশন চিত্রটি ব্যবহার করে। candidatesধ্রুব নিয়ন্ত্রণগুলি কত প্রার্থী প্রতিটি পদক্ষেপ উপর পরীক্ষা করা হবে। উচ্চতরটিতে আরোহণের জন্য একটি ভাল স্পট খুঁজে পাওয়ার আরও ভাল সুযোগ দেয় তবে প্রোগ্রামটি ধীর করে দেয়। শেষ অবধি, terminationপ্রোগ্রামটি ছাড়ার আগে প্রোগ্রামটির আউটপুট উন্নতি না করে কত পদক্ষেপে যেতে পারে। এটি বাড়ানো ভাল ফলাফল দিতে পারে তবে এটিকে সামান্য বেশি সময় দিতে পারে।

চিত্র

N = 100, 300, 1000, এবং 3000:


1
এটি আইএমও জিতেছে - আমার চেয়ে অনেক ভাল।
orlp

1
@ অর্প - ধন্যবাদ! যদিও ন্যায়সঙ্গত হওয়ার জন্য, আপনি আপনার তাড়াতাড়ি পোস্ট করেছেন এবং এটি অনেক বেশি দ্রুত চলে। গতি গণনা!
বুজুম

1
ঠিক আছে, খনিটি আসলে কোনও ভারোণোই মানচিত্রের উত্তর নয় :) এটি একটি সত্যই নমুনা বয়নকারী অ্যালগরিদম, তবে নমুনা পয়েন্টগুলিকে ভোরোনাই সাইটগুলিতে রূপান্তর করা স্পষ্টত অনুকূল নয়।
orlp

55

আইডিএল, অভিযোজিত সংশোধন

এই পদ্ধতিটি জ্যোতির্বিদ্যা সংক্রান্ত সিমুলেশনগুলি এবং উপ- বিভাগের পৃষ্ঠ থেকে অভিযোজিত জাল সংশোধন দ্বারা অনুপ্রাণিত হয় । এটি এমন এক ধরণের কাজ যা আইডিএল নিজেকে অহংকার করে, যা আপনি ব্যবহার করতে সক্ষম হয়েছি এমন বিশাল সংখ্যক বিল্টিন ফাংশন দ্বারা আপনি বলতে সক্ষম হবেন। : ডি

আমি ব্ল্যাক-ব্যাকগ্রাউন্ডের যোশি পরীক্ষার চিত্রটির জন্য কিছু মধ্যস্থতাকে আউটপুট দিয়েছি n = 1000

প্রথমে আমরা চিত্রটিতে (ব্যবহার করে ct_luminance) একটি আলোকিত গ্রাইস্কেল সঞ্চালন করি এবং ভাল প্রান্ত সনাক্তকরণের জন্য একটি প্রিভিট ফিল্টার ( prewitt, উইকিপিডিয়া দেখুন ) প্রয়োগ করি :

অ আ ক খ অ আ ক খ

তারপরে আসল গ্রান্ট-ওয়ার্ক আসে: আমরা চিত্রটি 4-এ বিভক্ত করি এবং ফিল্টারযুক্ত চিত্রের প্রতিটি কোয়াড্র্যান্টের বৈচিত্রটি পরিমাপ করি। আমাদের বিভাজনটি মহকুমার আকারের দ্বারা ওজনিত হয় (যা এই প্রথম ধাপে সমান), যাতে উচ্চতর বৈচিত্র্যের সাথে "দৃ "়" অঞ্চলগুলি আরও ছোট এবং ছোট এবং আরও ছোট হয় না। তারপরে, আমরা আরও বিশদ সহ মহকুমাগুলিকে টার্গেট করতে ওজনযুক্ত প্রকরণটি ব্যবহার করি এবং যতক্ষণ না আমরা আমাদের লক্ষ্য সংখ্যার সাইটগুলিতে আঘাত করি (প্রতিটি মহকুমায় ঠিক একটি সাইট রয়েছে) ততক্ষণ পর্যন্ত প্রতিটি বিশদ সমৃদ্ধ বিভাগকে 4 টি অতিরিক্ত হিসাবে বিভক্ত করা হয়। যেহেতু আমরা প্রতিবার পুনরাবৃত্তি করি তখন আমরা 3 টি সাইট যুক্ত করি তাই আমরা সাইটগুলি শেষ করি n - 2 <= N <= n

আমি এই চিত্রটির জন্য একটি মহকুমা প্রক্রিয়া তৈরি করেছি, যা আমি এম্বেড করতে পারি না, তবে এটি এখানে ; প্রতিটি অনুচ্ছেদে রঙটি ভারযুক্ত বৈকল্পিক দ্বারা নির্ধারিত হয়। (আমি সাদা ব্যাকগ্রাউন্ডের যোশি জন্য একই ধরণের ভিডিও তৈরি করেছি, তুলনা করার জন্য, রঙের টেবিলটি বিপরীত হয়েছে যাতে এটি কালো রঙের পরিবর্তে সাদা দিকে যায়; এটি এখানে ।) মহকুমার চূড়ান্ত পণ্যটি এরকম দেখাচ্ছে:

অ আ ক খ

আমাদের মহকুমার তালিকাটি একবার হলে আমরা প্রতিটি মহকুমার মধ্য দিয়ে যাই। চূড়ান্ত সাইটের অবস্থানটি হ'ল প্রিভিট চিত্রের সর্বনিম্নের অবস্থান, অর্থাৎ সর্বনিম্ন "ধারালো" পিক্সেল এবং বিভাগটির রঙ p পিক্সেলের রঙ; এখানে চিহ্নিত চিত্র সহ মূল চিত্রটি এখানে রয়েছে:

অ আ ক খ

তারপরে, আমরা triangulateসাইটগুলি ডেলাউন ট্রাইঙ্গুলেশন গণনা করার জন্য বিল্ট-ইন voronoiব্যবহার করি এবং প্রতিটি ভোরোনাই বহুভুজের অনুভূমিকাকে সংজ্ঞায়িত করার জন্য বিল্ট-ইন ব্যবহার করি, প্রতিটি বহুভুজকে তার নিজের রঙে চিত্রের বাফারে আঁকার আগে। অবশেষে, আমরা চিত্র বাফারের একটি স্ন্যাপশট সংরক্ষণ করি।

অ আ ক খ

কোড:

function subdivide, image, bounds, vars
  ;subdivide a section into 4, and return the 4 subdivisions and the variance of each
  division = list()
  vars = list()
  nx = bounds[2] - bounds[0]
  ny = bounds[3] - bounds[1]
  for i=0,1 do begin
    for j=0,1 do begin
      x = i * nx/2 + bounds[0]
      y = j * ny/2 + bounds[1]
      sub = image[x:x+nx/2-(~(nx mod 2)),y:y+ny/2-(~(ny mod 2))]
      division.add, [x,y,x+nx/2-(~(nx mod 2)),y+ny/2-(~(ny mod 2))]
      vars.add, variance(sub) * n_elements(sub)
    endfor
  endfor
  return, division
end

pro voro_map, n, image, outfile
  sz = size(image, /dim)
  ;first, convert image to greyscale, and then use a Prewitt filter to pick out edges
  edges = prewitt(reform(ct_luminance(image[0,*,*], image[1,*,*], image[2,*,*])))
  ;next, iteratively subdivide the image into sections, using variance to pick
  ;the next subdivision target (variance -> detail) until we've hit N subdivisions
  subdivisions = subdivide(edges, [0,0,sz[1],sz[2]], variances)
  while subdivisions.count() lt (n - 2) do begin
    !null = max(variances.toarray(),target)
    oldsub = subdivisions.remove(target)
    newsub = subdivide(edges, oldsub, vars)
    if subdivisions.count(newsub[0]) gt 0 or subdivisions.count(newsub[1]) gt 0 or subdivisions.count(newsub[2]) gt 0 or subdivisions.count(newsub[3]) gt 0 then stop
    subdivisions += newsub
    variances.remove, target
    variances += vars
  endwhile
  ;now we find the minimum edge value of each subdivision (we want to pick representative 
  ;colors, not edge colors) and use that as the site (with associated color)
  sites = fltarr(2,n)
  colors = lonarr(n)
  foreach sub, subdivisions, i do begin
    slice = edges[sub[0]:sub[2],sub[1]:sub[3]]
    !null = min(slice,target)
    sxy = array_indices(slice, target) + sub[0:1]
    sites[*,i] = sxy
    colors[i] = cgcolor24(image[0:2,sxy[0],sxy[1]])
  endforeach
  ;finally, generate the voronoi map
  old = !d.NAME
  set_plot, 'Z'
  device, set_resolution=sz[1:2], decomposed=1, set_pixel_depth=24
  triangulate, sites[0,*], sites[1,*], tr, connectivity=C
  for i=0,n-1 do begin
    if C[i] eq C[i+1] then continue
    voronoi, sites[0,*], sites[1,*], i, C, xp, yp
    cgpolygon, xp, yp, color=colors[i], /fill, /device
  endfor
  !null = cgsnapshot(file=outfile, /nodialog)
  set_plot, old
end

pro wrapper
  cd, '~/voronoi'
  fs = file_search()
  foreach f,fs do begin
    base = strsplit(f,'.',/extract)
    if base[1] eq 'png' then im = read_png(f) else read_jpeg, f, im
    voro_map,100, im, base[0]+'100.png'
    voro_map,500, im, base[0]+'500.png'
    voro_map,1000,im, base[0]+'1000.png'
  endforeach
end

এর মাধ্যমে কল করুন voro_map, n, image, output_filename। আমি wrapperপাশাপাশি একটি পদ্ধতি অন্তর্ভুক্ত করেছি, যা প্রতিটি পরীক্ষার চিত্রের মধ্য দিয়ে যায় এবং 100, 500 এবং 1000 সাইট নিয়ে চলে।

আউটপুট এখানে সংগ্রহ করা হয়েছে এবং এখানে কিছু থাম্বনেইল রয়েছে:

n = 100

অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ

n = 500

অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ

n = 1000

অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ অ আ ক খ


9
আমি এই সত্যটি পছন্দ করি যে এই সমাধানটি আরও জটিল ক্ষেত্রগুলিতে আরও বেশি পয়েন্ট রাখে, যা আমি মনে করি অভিপ্রায় এবং এ মুহুর্তে এটি অন্যদের থেকে পৃথক করে।
অ্যালেক্সান্ডার-ব্রেট

হ্যাঁ, বিশদ-গোষ্ঠীযুক্ত পয়েন্টগুলির
ধারণাটিই

3
খুব ঝরঝরে ব্যাখ্যা, এবং চিত্রগুলি চিত্তাকর্ষক! আমার একটি প্রশ্ন আছে - দেখে মনে হচ্ছে যোশি সাদা পটভূমিতে থাকাকালীন আপনি অনেক আলাদা চিত্র পেয়েছেন, যেখানে আমাদের কয়েকটি বিজোড় আকার রয়েছে। কি কারণ হতে পারে?
ব্রেইনস্টিল 17'15

2
@ ব্রায়ান স্টিল আমি কল্পনা করি যে রূপরেখাগুলি উচ্চ বৈকল্পিক অঞ্চল হিসাবে বেছে নেওয়া হয়েছে এবং অকারণে মনোনিবেশ করা হয়েছে এবং তারপরে অন্যান্য সত্যিকারের উচ্চ-বিস্তৃত ক্ষেত্রগুলি এর কারণে কম পয়েন্ট নির্ধারিত হয়েছে।
ডপপেলগ্রিনিয়ার

@ ব্রেনস্টিল আমার মনে হয় ডপপেল সঠিক - কালো সীমানা এবং সাদা পটভূমির মধ্যে একটি শক্ত প্রান্ত রয়েছে, যা অ্যালগরিদমে অনেক বিশদ জানতে চায়। আমি নিশ্চিত নই যে এটি এমন কিছু যা আমি ঠিক করতে পারি (বা, আরও গুরুত্বপূর্ণভাবে হওয়া উচিত ) ...
সির্পারসিভাল

47

পাইথন 3 + পিআইএল + সাইপি, ফাজী কে-মানে

from collections import defaultdict
import itertools
import random
import time

from PIL import Image
import numpy as np
from scipy.spatial import KDTree, Delaunay

INFILE = "planet.jpg"
OUTFILE = "voronoi.txt"
N = 3000

DEBUG = True # Outputs extra images to see what's happening
FEATURE_FILE = "features.png"
SAMPLE_FILE = "samples.png"
SAMPLE_POINTS = 20000
ITERATIONS = 10
CLOSE_COLOR_THRESHOLD = 15

"""
Color conversion functions
"""

start_time = time.time()

# http://www.easyrgb.com/?X=MATH
def rgb2xyz(rgb):
  r, g, b = rgb
  r /= 255
  g /= 255
  b /= 255

  r = ((r + 0.055)/1.055)**2.4 if r > 0.04045 else r/12.92
  g = ((g + 0.055)/1.055)**2.4 if g > 0.04045 else g/12.92
  b = ((b + 0.055)/1.055)**2.4 if b > 0.04045 else b/12.92

  r *= 100
  g *= 100
  b *= 100

  x = r*0.4124 + g*0.3576 + b*0.1805
  y = r*0.2126 + g*0.7152 + b*0.0722
  z = r*0.0193 + g*0.1192 + b*0.9505

  return (x, y, z)

def xyz2lab(xyz):
  x, y, z = xyz
  x /= 95.047
  y /= 100
  z /= 108.883

  x = x**(1/3) if x > 0.008856 else 7.787*x + 16/116
  y = y**(1/3) if y > 0.008856 else 7.787*y + 16/116
  z = z**(1/3) if z > 0.008856 else 7.787*z + 16/116

  L = 116*y - 16
  a = 500*(x - y)
  b = 200*(y - z)

  return (L, a, b)

def rgb2lab(rgb):
  return xyz2lab(rgb2xyz(rgb))

def lab2xyz(lab):
  L, a, b = lab
  y = (L + 16)/116
  x = a/500 + y
  z = y - b/200

  y = y**3 if y**3 > 0.008856 else (y - 16/116)/7.787
  x = x**3 if x**3 > 0.008856 else (x - 16/116)/7.787
  z = z**3 if z**3 > 0.008856 else (z - 16/116)/7.787

  x *= 95.047
  y *= 100
  z *= 108.883

  return (x, y, z)

def xyz2rgb(xyz):
  x, y, z = xyz
  x /= 100
  y /= 100
  z /= 100

  r = x* 3.2406 + y*-1.5372 + z*-0.4986
  g = x*-0.9689 + y* 1.8758 + z* 0.0415
  b = x* 0.0557 + y*-0.2040 + z* 1.0570

  r = 1.055 * (r**(1/2.4)) - 0.055 if r > 0.0031308 else 12.92*r
  g = 1.055 * (g**(1/2.4)) - 0.055 if g > 0.0031308 else 12.92*g
  b = 1.055 * (b**(1/2.4)) - 0.055 if b > 0.0031308 else 12.92*b

  r *= 255
  g *= 255
  b *= 255

  return (r, g, b)

def lab2rgb(lab):
  return xyz2rgb(lab2xyz(lab))

"""
Step 1: Read image and convert to CIELAB
"""

im = Image.open(INFILE)
im = im.convert("RGB")
width, height = prev_size = im.size

pixlab_map = {}

for x in range(width):
    for y in range(height):
        pixlab_map[(x, y)] = rgb2lab(im.getpixel((x, y)))

print("Step 1: Image read and converted")

"""
Step 2: Get feature points
"""

def euclidean(point1, point2):
    return sum((x-y)**2 for x,y in zip(point1, point2))**.5


def neighbours(pixel):
    x, y = pixel
    results = []

    for dx, dy in itertools.product([-1, 0, 1], repeat=2):
        neighbour = (pixel[0] + dx, pixel[1] + dy)

        if (neighbour != pixel and 0 <= neighbour[0] < width
            and 0 <= neighbour[1] < height):
            results.append(neighbour)

    return results

def mse(colors, base):
    return sum(euclidean(x, base)**2 for x in colors)/len(colors)

features = []

for x in range(width):
    for y in range(height):
        pixel = (x, y)
        col = pixlab_map[pixel]
        features.append((mse([pixlab_map[n] for n in neighbours(pixel)], col),
                         random.random(),
                         pixel))

features.sort()
features_copy = [x[2] for x in features]

if DEBUG:
    test_im = Image.new("RGB", im.size)

    for i in range(len(features)):
        pixel = features[i][1]
        test_im.putpixel(pixel, (int(255*i/len(features)),)*3)

    test_im.save(FEATURE_FILE)

print("Step 2a: Edge detection-ish complete")

def random_index(list_):
    r = random.expovariate(2)

    while r > 1:
         r = random.expovariate(2)

    return int((1 - r) * len(list_))

sample_points = set()

while features and len(sample_points) < SAMPLE_POINTS:
    index = random_index(features)
    point = features[index][2]
    sample_points.add(point)
    del features[index]

print("Step 2b: {} feature samples generated".format(len(sample_points)))

if DEBUG:
    test_im = Image.new("RGB", im.size)

    for pixel in sample_points:
        test_im.putpixel(pixel, (255, 255, 255))

    test_im.save(SAMPLE_FILE)

"""
Step 3: Fuzzy k-means
"""

def euclidean(point1, point2):
    return sum((x-y)**2 for x,y in zip(point1, point2))**.5

def get_centroid(points):
    return tuple(sum(coord)/len(points) for coord in zip(*points))

def mean_cell_color(cell):
    return get_centroid([pixlab_map[pixel] for pixel in cell])

def median_cell_color(cell):
    # Pick start point out of mean and up to 10 pixels in cell
    mean_col = get_centroid([pixlab_map[pixel] for pixel in cell])
    start_choices = [pixlab_map[pixel] for pixel in cell]

    if len(start_choices) > 10:
        start_choices = random.sample(start_choices, 10)

    start_choices.append(mean_col)

    best_dist = None
    col = None

    for c in start_choices:
        dist = sum(euclidean(c, pixlab_map[pixel])
                       for pixel in cell)

        if col is None or dist < best_dist:
            col = c
            best_dist = dist

    # Approximate median by hill climbing
    last = None

    while last is None or euclidean(col, last) < 1e-6:
        last = col

        best_dist = None
        best_col = None

        for deviation in itertools.product([-1, 0, 1], repeat=3):
            new_col = tuple(x+y for x,y in zip(col, deviation))
            dist = sum(euclidean(new_col, pixlab_map[pixel])
                       for pixel in cell)

            if best_dist is None or dist < best_dist:
                best_col = new_col

        col = best_col

    return col

def random_point():
    index = random_index(features_copy)
    point = features_copy[index]

    dx = random.random() * 10 - 5
    dy = random.random() * 10 - 5

    return (point[0] + dx, point[1] + dy)

centroids = np.asarray([random_point() for _ in range(N)])
variance = {i:float("inf") for i in range(N)}
cluster_colors = {i:(0, 0, 0) for i in range(N)}

# Initial iteration
tree = KDTree(centroids)
clusters = defaultdict(set)

for point in sample_points:
    nearest = tree.query(point)[1]
    clusters[nearest].add(point)

# Cluster!
for iter_num in range(ITERATIONS):
    if DEBUG:
        test_im = Image.new("RGB", im.size)

        for n, pixels in clusters.items():
            color = 0xFFFFFF * (n/N)
            color = (int(color//256//256%256), int(color//256%256), int(color%256))

            for p in pixels:
                test_im.putpixel(p, color)

        test_im.save(SAMPLE_FILE)

    for cluster_num in clusters:
        if clusters[cluster_num]:
            cols = [pixlab_map[x] for x in clusters[cluster_num]]

            cluster_colors[cluster_num] = mean_cell_color(clusters[cluster_num])
            variance[cluster_num] = mse(cols, cluster_colors[cluster_num])

        else:
            cluster_colors[cluster_num] = (0, 0, 0)
            variance[cluster_num] = float("inf")

    print("Clustering (iteration {})".format(iter_num))

    # Remove useless/high variance
    if iter_num < ITERATIONS - 1:
        delaunay = Delaunay(np.asarray(centroids))
        neighbours = defaultdict(set)

        for simplex in delaunay.simplices:
            n1, n2, n3 = simplex

            neighbours[n1] |= {n2, n3}
            neighbours[n2] |= {n1, n3}
            neighbours[n3] |= {n1, n2}

        for num, centroid in enumerate(centroids):
            col = cluster_colors[num]

            like_neighbours = True

            nns = set() # neighbours + neighbours of neighbours

            for n in neighbours[num]:
                nns |= {n} | neighbours[n] - {num}

            nn_far = sum(euclidean(col, cluster_colors[nn]) > CLOSE_COLOR_THRESHOLD
                         for nn in nns)

            if nns and nn_far / len(nns) < 1/5:
                sample_points -= clusters[num]

                for _ in clusters[num]:
                    if features and len(sample_points) < SAMPLE_POINTS:
                        index = random_index(features)
                        point = features[index][3]
                        sample_points.add(point)
                        del features[index]

                clusters[num] = set()

    new_centroids = []

    for i in range(N):
        if clusters[i]:
            new_centroids.append(get_centroid(clusters[i]))
        else:
            new_centroids.append(random_point())

    centroids = np.asarray(new_centroids)
    tree = KDTree(centroids)

    clusters = defaultdict(set)

    for point in sample_points:
        nearest = tree.query(point, k=6)[1]
        col = pixlab_map[point]

        for n in nearest:
            if n < N and euclidean(col, cluster_colors[n])**2 <= variance[n]:
                clusters[n].add(point)
                break

        else:
            clusters[nearest[0]].add(point)

print("Step 3: Fuzzy k-means complete")

"""
Step 4: Output
"""

for i in range(N):
    if clusters[i]:
        centroids[i] = get_centroid(clusters[i])

centroids = np.asarray(centroids)
tree = KDTree(centroids)
color_clusters = defaultdict(set)

# Throw back on some sample points to get the colors right
all_points = [(x, y) for x in range(width) for y in range(height)]

for pixel in random.sample(all_points, int(min(width*height, 5 * SAMPLE_POINTS))):
    nearest = tree.query(pixel)[1]
    color_clusters[nearest].add(pixel)

with open(OUTFILE, "w") as outfile:
    for i in range(N):
        if clusters[i]:
            centroid = tuple(centroids[i])          
            col = tuple(x/255 for x in lab2rgb(median_cell_color(color_clusters[i] or clusters[i])))
            print(" ".join(map(str, centroid + col)), file=outfile)

print("Done! Time taken:", time.time() - start_time)

অ্যালগরিদম

মূল ধারণাটি হ'ল কে-মানে প্রাকৃতিকভাবে পার্টিশনটিকে ভোরোনাই কোষে ভাগ করে দেয়, কারণ পয়েন্টগুলি নিকটতম সেন্ট্রয়েডের সাথে আবদ্ধ থাকে। যাইহোক, আমাদের কোনওভাবে বাধা হিসাবে রঙগুলিতে যুক্ত করা দরকার।

প্রথমে আমরা প্রতিটি পিক্সেলকে আরও ভাল রঙের ম্যানিপুলেশনের জন্য ল্যাব রঙের স্পেসে রূপান্তর করি ।

তারপরে আমরা এক ধরণের "দরিদ্র ব্যক্তির প্রান্ত সনাক্তকরণ" করি। প্রতিটি পিক্সেলের জন্য, আমরা এর অর্থোগোনাল এবং তির্যক প্রতিবেশীদের দিকে নজর রাখি, এবং রঙের গড়-বর্গক্ষেত্রের পার্থক্য গণনা করি। এরপরে আমরা এই পিক্সেলগুলিকে এই পার্থক্য অনুসারে তালিকার সামনের অংশে প্রতিবেশীদের সাথে সর্বাধিক সাদৃশ্যযুক্ত করে এবং পিছনে তাদের প্রতিবেশীদের তুলনায় পিক্সেলগুলি (যেমন, প্রান্ত বিন্দু হওয়ার সম্ভাবনা বেশি)। এখানে গ্রহের উদাহরণ রয়েছে, যেখানে পিক্সেল যত উজ্জ্বল, তার প্রতিবেশীদের চেয়ে তত বেশি ভিন্ন:

এখানে চিত্র বর্ণনা লিখুন

(উপরের রেন্ডার আউটপুটটিতে গ্রিডের মতো স্পষ্ট নিদর্শন রয়েছে @

এর পরে, আমরা ক্লাস্টার করতে পয়েন্টের একটি বৃহত সংখ্যার নমুনা করতে এই পিক্সেল ক্রমটি ব্যবহার করি। আমরা একটি ঘনিষ্ঠ বিতরণ ব্যবহার করি, যা আরও প্রান্তের মতো এবং "আকর্ষণীয়" পয়েন্টগুলিকে অগ্রাধিকার দেয়।

এখানে চিত্র বর্ণনা লিখুন

ক্লাস্টারিংয়ের জন্য, আমরা প্রথমে Nসেন্ট্রয়েডগুলি বেছে নিই , এলোমেলোভাবে উপরের মত একই ঘনিষ্ঠ বিতরণ ব্যবহার করে বেছে নেওয়া হয়েছে। একটি প্রাথমিক পুনরাবৃত্তি সম্পাদন করা হয়, এবং ফলস্বরূপ প্রতিটি ক্লাস্টারের জন্য আমরা একটি গড় রঙ এবং রঙের বৈকল্পিক প্রান্তিকাকে অর্পণ করি। তারপরে বেশ কয়েকটি পুনরাবৃত্তির জন্য, আমরা:

  • সেন্ট্রয়েডগুলির ডেলাউনে ট্র্যাঙ্গুলেশন তৈরি করুন, যাতে আমরা সহজেই সেন্ট্রয়েডগুলিতে প্রতিবেশীদের জিজ্ঞাসা করতে পারি।
  • সেন্ট্রয়েডগুলি অপসারণ করতে ত্রিভুজ ব্যবহার করুন যা তাদের প্রতিবেশী এবং প্রতিবেশীর প্রতিবেশীদের বেশিরভাগ (>> 4/5) রঙের নিকটে রয়েছে color যে কোনও সম্পর্কিত নমুনা পয়েন্টগুলিও সরানো হয় এবং নতুন প্রতিস্থাপন সেন্ট্রয়েড এবং নমুনা পয়েন্টগুলি যুক্ত করা হয়। এই পদক্ষেপটি অ্যালগরিদমকে আরও ক্লাস্টার স্থাপন করতে বাধ্য করার চেষ্টা করে যেখানে বিশদ প্রয়োজন।
  • নতুন সেন্ট্রয়েডগুলির জন্য একটি কেডি-ট্রি তৈরি করুন, যাতে আমরা সহজেই যে কোনও নমুনা বিন্দুতে নিকটতম সেন্ট্রয়েডগুলিকে জিজ্ঞাসা করতে পারি।
  • প্রতিটি নমুনা বিন্দু 6 নিকটতম সেন্ট্রয়েডগুলির মধ্যে 6 টির (নিখুঁতভাবে বেছে নেওয়া 6) নির্দিষ্ট করার জন্য গাছটি ব্যবহার করুন। সেন্ট্রয়েড কেবলমাত্র একটি নমুনা পয়েন্ট গ্রহণ করবে যদি পয়েন্টটির রঙ সেন্ট্রয়েডের বর্ণের বৈকল্পিক প্রান্তিকের মধ্যে থাকে। আমরা প্রতিটি গ্রহণযোগ্য সেন্ট্রয়েডকে প্রতিটি নমুনা বিন্দু নির্ধারণ করার চেষ্টা করি, তবে যদি এটি সম্ভব না হয় তবে আমরা কেবল এটি নিকটতম সেন্ট্রয়েডে নির্ধারণ করি। ক্লাস্টারদের ওভারল্যাপ করা সম্ভব হওয়ায় অ্যালগরিদমের "ধোঁয়া" এই পদক্ষেপটি থেকে আসে।
  • সেন্ট্রয়েডগুলি পুনরুদ্ধার করুন।

এখানে চিত্র বর্ণনা লিখুন

(পূর্ণ আকারের জন্য ক্লিক করুন)

পরিশেষে, আমরা অভিন্ন বিতরণ ব্যবহার করে এবার প্রচুর পয়েন্টের নমুনা নিই। অন্য কেডি-ট্রি ব্যবহার করে, আমরা প্রতিটি বিন্দুটিকে নিকটতম সেন্ট্রয়েডে ক্লাস্টার গঠন করে নির্ধারণ করি। তারপরে আমরা আমাদের চূড়ান্ত ঘরের রঙগুলি প্রদান করে, একটি ক্লিষ্টারিং অ্যালগরিদম ব্যবহার করে প্রতিটি ক্লাস্টারের মাঝারি রঙটি আনুমানিক করি (@ ফিনোটপি এবং @ মার্টিনব্যাটনারকে এই পদক্ষেপের জন্য ধন্যবাদ)।

এখানে চিত্র বর্ণনা লিখুন

নোট

স্নিপেট ( OUTFILE) এর জন্য একটি পাঠ্য ফাইল আউটপুট ছাড়াও , যদি প্রোগ্রামে DEBUGসেট করা থাকে Trueতবে উপরের চিত্রগুলি আউটপুট এবং ওভাররাইটও করতে পারে। অ্যালগরিদম প্রতিটি চিত্রের জন্য কয়েক মিনিট সময় নেয়, তাই এটি অগ্রগতি পরীক্ষা করার একটি ভাল উপায় যা চলমান সময়ে কোনও ভয়াবহ পরিমাণ যোগ করে না।

নমুনা আউটপুট

এন = 32:

এখানে চিত্র বর্ণনা লিখুন

এন = 100:

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন

এন = 1000:

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন

এন = 3000:

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন


1
আমি সত্যিই পছন্দ করি যে আপনার অন-হোয়াইট যোশিস কীভাবে পরিণত হয়েছিল।
সর্বাধিক

26

গণিত, র্যান্ডম সেল

এটিই বেসলাইন সমাধান, সুতরাং আমি আপনাকে যে ন্যূনতম জিজ্ঞাসা করছি তার একটি ধারণা পাবেন। fileএবং এন- তে ফাইলের নাম (স্থানীয়ভাবে বা URL হিসাবে) দেওয়া n, নিম্নলিখিত কোডটি কেবল এন এলোমেলো পিক্সেল খুঁজে বের করে এবং সেই পিক্সেলগুলিতে পাওয়া রঙগুলি ব্যবহার করে। এটি সত্যিই নিষ্পাপ এবং অবিশ্বাস্যরূপে ভাল কাজ করে না, তবে আমি চাই আপনি ছেলেরা এটি সর্বোপরি পরাজিত করুন। :)

data = ImageData@Import@file;
dims = Dimensions[data][[1 ;; 2]]
{Reverse@#, data[[##]][[1 ;; 3]] & @@ Floor[1 + #]} &[dims #] & /@ 
 RandomReal[1, {n, 2}]

এন = 100 এর জন্য সমস্ত পরীক্ষার চিত্র এখানে রয়েছে (সমস্ত চিত্র বৃহত্তর সংস্করণে লিঙ্ক হয়েছে):

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন

আপনি দেখতে পাচ্ছেন যে এগুলি মূলত অকেজো। তাদের কিছু শৈল্পিক মূল্য থাকতে পারে, একটি ভাববাদী উপায়ে, মূল চিত্রগুলি সবেমাত্র স্বীকৃত।

জন্য এন = 500 , পরিস্থিতি একটু উন্নত হয়, কিন্তু এখনও খুব অদ্ভুত শিল্পকর্ম হয়, চিত্র ধুয়ে তাকান, এবং কোষ অনেকটা বিস্তারিত ছাড়া অঞ্চলে নষ্ট করা হয়:

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন

তোমার পালা!


আমি ভাল কোডার নই তবে আমার godশ্বর এই চিত্রগুলি দেখতে সুন্দর দেখাচ্ছে। অসাধারণ ধারণা!
ফারাজ মাসরুর

কোন কারণ Dimensions@ImageDataএবং না ImageDimensions? আপনি ImageDataব্যবহার করে ধীরে ধীরে এড়াতে পারবেন PixelValue
2012campion

টুইট আমি পরে এটি ঠিক করতে এবং উদাহরণস্বরূপ চিত্রগুলি প্রস্তাবিত এন-মানগুলিতেও পরিবর্তন করতে পারি।
মার্টিন এন্ডার

23

ম্যাথামেটিকাল

আমরা সবাই জানি মার্টিন ম্যাথমেটিকাকে পছন্দ করে তাই ম্যাথামেটিকাকে দিয়ে চেষ্টা করে দেখি।

আমার অ্যালগরিদম প্রাথমিক ভেরোনাই চিত্রটি তৈরি করতে চিত্র প্রান্ত থেকে এলোমেলো পয়েন্ট ব্যবহার করে। ডায়াগ্রামটি তখন একটি সাধারণ গড় ফিল্টার সহ জালটির পুনরাবৃত্তীয় সমন্বয় দ্বারা প্রেট্টিফাইড হয়। এটি উচ্চতর বিপরীতে অঞ্চলের নিকটে উচ্চ কক্ষের ঘনত্ব সহ চিত্রগুলি দেয় এবং ক্রেজি অ্যাঙ্গেল ছাড়াই দৃশ্যত আনন্দদায়ক কোষগুলি দেয়।

নিম্নলিখিত চিত্রগুলি কার্যত প্রক্রিয়াটির একটি উদাহরণ দেখায়। মজাটি কিছুটা ম্যাথমেটিকাস খারাপ অ্যান্টিয়ালাইজিং দ্বারা নষ্ট হয়ে যায় তবে আমরা ভেক্টর গ্রাফিক্স পাই, এটি অবশ্যই মূল্যবান হতে হবে।

এই অ্যালগরিদমটি এলোমেলোভাবে নমুনা ছাড়াই এখানেVoronoiMesh ডকুমেন্টেশনে পাওয়া যাবে

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন

চিত্র পরীক্ষা করুন (100,300,1000,3000)

কোড

VoronoiImage[img_, nSeeds_, iterations_] := Module[{
    i = img,
    edges = EdgeDetect@img,
    voronoiRegion = Transpose[{{0, 0}, ImageDimensions[img]}],
    seeds, voronoiInitial, voronoiRelaxed
    },
   seeds = RandomChoice[ImageValuePositions[edges, White], nSeeds];
   voronoiInitial = VoronoiMesh[seeds, voronoiRegion];
   voronoiRelaxed = 
    Nest[VoronoiMesh[Mean @@@ MeshPrimitives[#, 2], voronoiRegion] &, 
     voronoiInitial, iterations];
   Graphics[Table[{RGBColor[ImageValue[img, Mean @@ mp]], mp}, 
     {mp,MeshPrimitives[voronoiRelaxed, 2]}]]
   ];

প্রথম পোস্টের জন্য দুর্দান্ত কাজ! :) আপনি 32 টি কোষ সহ ভোরোনাই পরীক্ষার চিত্র চেষ্টা করতে চাইতে পারেন (এটি চিত্রের মধ্যেই সেল সংখ্যা)।
মার্টিন ইন্ডার

ধন্যবাদ! আমার ধারণা আমার অ্যালগরিদম এই উদাহরণটিতে ভয়াবহভাবে সঞ্চালন করবে। বীজগুলি কোষের প্রান্তগুলিতে আরম্ভ হবে এবং পুনরাবৃত্তি এটি আরও ভাল করে তুলবে না;)
পাঞ্জাবি

মূল চিত্রটিতে রূপান্তরিত করতে ধীর গতির সত্ত্বেও, আমি দেখতে পাচ্ছি যে আপনার অ্যালগোরিদম খুব শৈল্পিক ফলাফল পেয়েছে! (জর্জেস সেউরাট কাজ করে এমন একটি উন্নত সংস্করণের মত)। দারূন কাজ!
নেজোদ

আপনি নিজের চূড়ান্ত লাইনগুলিতে পরিবর্তন করে কাঁচের মতো বর্ণের অন্তরবিচ্ছিন্ন বহুভুজ রঙগুলি পেতে পারেনGraphics@Table[ Append[mp, VertexColors -> RGBColor /@ ImageValue[img, First[mp]]], {mp, MeshPrimitives[voronoiRelaxed, 2]}]
হিস্টোগ্রামগুলি

13

পাইথন + সায়পি + এমসি

আমি যে অ্যালগরিদম ব্যবহার করেছি তা নিম্নলিখিত:

  1. ছোট আকারের চিত্রগুলিকে আকার দিন (~ 150 পিক্সেল)
  2. সর্বাধিক চ্যানেল মানগুলির একটি অসম্পূর্ণ-মুখোশযুক্ত চিত্র তৈরি করুন (এটি সাদা অঞ্চলগুলিকে খুব দৃ pick়ভাবে তুলতে সহায়তা করে না)।
  3. পরম মান নিন।
  4. এই চিত্রের সাথে আনুপাতিক সম্ভাবনার একটি এলোমেলো পয়েন্ট চয়ন করুন। এটি বিরতি উভয় পক্ষের পয়েন্ট চয়ন করে।
  5. একটি ব্যয় ক্রিয়াকলাপ কম করার জন্য নির্বাচিত পয়েন্টগুলি পরিমার্জন করুন। চ্যানেলগুলিতে স্কোয়ার বিচ্যুতির পরিমাণের সর্বাধিক ক্রিয়াকলাপটি (আবার শক্ত রঙগুলিতে পক্ষপাতদুষ্ট সহায়তা করে কেবল শক্ত সাদা নয়)। আমি মার্কিভ চেইন মন্টি কার্লোকে অপ্টিমাইজার হিসাবে ইম্যাসি মডিউল (অত্যন্ত প্রস্তাবিত) দিয়ে অপব্যবহার করেছি। এন চেইন পুনরাবৃত্তির পরে যখন কোনও নতুন উন্নতি পাওয়া যায় না তখন পদ্ধতিটি ব্যর্থ হয়।

অ্যালগরিদম খুব ভাল কাজ করে বলে মনে হচ্ছে। দুর্ভাগ্যক্রমে এটি কেবল সংক্ষিপ্ত চিত্রগুলিতে চালিত হতে পারে। ভোরোনাই পয়েন্টগুলি নিতে এবং এগুলি বড় চিত্রগুলিতে প্রয়োগ করার মতো সময় আমার হাতে নেই। তারা এই সময়ে সংশোধন করা যেতে পারে। আরও ভাল মিনিমা পাওয়ার জন্য আমি আরও দীর্ঘকাল ধরে এমসিএমসি চালাতে পারতাম। অ্যালগরিদমের দুর্বল বিন্দু এটি বরং ব্যয়বহুল। আমার কাছে 1000 পয়েন্ট ছাড়িয়ে যাওয়ার সময় নেই এবং 1000 পয়েন্টের চিত্রের মধ্যে একটি দম্পতি আসলে এখনও পরিমার্জনযোগ্য।

(ডান ক্লিক করুন এবং একটি বড় সংস্করণ পেতে চিত্র দেখুন)

100, 300 এবং 1000 পয়েন্টের জন্য থাম্বনেইল

বড় সংস্করণে লিঙ্কগুলি হ'ল http://imgur.com/a/2IXDT#9 (100 পয়েন্ট), http://imgur.com/a/bBQ7q (300 পয়েন্ট) এবং http://imgur.com/a/rr8wJ (1000 পয়েন্ট)

#!/usr/bin/env python

import glob
import os

import scipy.misc
import scipy.spatial
import scipy.signal
import numpy as N
import numpy.random as NR
import emcee

def compute_image(pars, rimg, gimg, bimg):
    npts = len(pars) // 2
    x = pars[:npts]
    y = pars[npts:npts*2]
    yw, xw = rimg.shape

    # exit if points are too far away from image, to stop MCMC
    # wandering off
    if(N.any(x > 1.2*xw) or N.any(x < -0.2*xw) or
       N.any(y > 1.2*yw) or N.any(y < -0.2*yw)):
        return None

    # compute tesselation
    xy = N.column_stack( (x, y) )
    tree = scipy.spatial.cKDTree(xy)

    ypts, xpts = N.indices((yw, xw))
    queryxy = N.column_stack((N.ravel(xpts), N.ravel(ypts)))

    dist, idx = tree.query(queryxy)

    idx = idx.reshape(yw, xw)
    ridx = N.ravel(idx)

    # tesselate image
    div = 1./N.clip(N.bincount(ridx), 1, 1e99)
    rav = N.bincount(ridx, weights=N.ravel(rimg)) * div
    gav = N.bincount(ridx, weights=N.ravel(gimg)) * div
    bav = N.bincount(ridx, weights=N.ravel(bimg)) * div

    rout = rav[idx]
    gout = gav[idx]
    bout = bav[idx]
    return rout, gout, bout

def compute_fit(pars, img_r, img_g, img_b):
    """Return fit statistic for parameters."""
    # get model
    retn = compute_image(pars, img_r, img_g, img_b)
    if retn is None:
        return -1e99
    model_r, model_g, model_b = retn

    # maximum squared deviation from one of the chanels
    fit = max( ((img_r-model_r)**2).sum(),
               ((img_g-model_g)**2).sum(),
               ((img_b-model_b)**2).sum() )

    # return fake log probability
    return -fit

def convgauss(img, sigma):
    """Convolve image with a Gaussian."""
    size = 3*sigma
    kern = N.fromfunction(
        lambda y, x: N.exp( -((x-size/2)**2+(y-size/2)**2)/2./sigma ),
        (size, size))
    kern /= kern.sum()
    out = scipy.signal.convolve2d(img.astype(N.float64), kern, mode='same')
    return out

def process_image(infilename, outroot, npts):
    img = scipy.misc.imread(infilename)
    img_r = img[:,:,0]
    img_g = img[:,:,1]
    img_b = img[:,:,2]

    # scale down size
    maxdim = max(img_r.shape)
    scale = int(maxdim / 150)
    img_r = img_r[::scale, ::scale]
    img_g = img_g[::scale, ::scale]
    img_b = img_b[::scale, ::scale]

    # make unsharp-masked image of input
    img_tot = N.max((img_r, img_g, img_b), axis=0)
    img1 = convgauss(img_tot, 2)
    img2 = convgauss(img_tot, 32)
    diff = N.abs(img1 - img2)
    diff = diff/diff.max()
    diffi = (diff*255).astype(N.int)
    scipy.misc.imsave(outroot + '_unsharp.png', diffi)

    # create random points with a probability distribution given by
    # the unsharp-masked image
    yw, xw = img_r.shape
    xpars = []
    ypars = []
    while len(xpars) < npts:
        ypar = NR.randint(int(yw*0.02),int(yw*0.98))
        xpar = NR.randint(int(xw*0.02),int(xw*0.98))
        if diff[ypar, xpar] > NR.rand():
            xpars.append(xpar)
            ypars.append(ypar)

    # initial parameters to model
    allpar = N.concatenate( (xpars, ypars) )

    # set up MCMC sampler with parameters close to each other
    nwalkers = npts*5  # needs to be at least 2*number of parameters+2
    pos0 = []
    for i in xrange(nwalkers):
        pos0.append(NR.normal(0,1,allpar.shape)+allpar)

    sampler = emcee.EnsembleSampler(
        nwalkers, len(allpar), compute_fit,
        args=[img_r, img_g, img_b],
        threads=4)

    # sample until we don't find a better fit
    lastmax = -N.inf
    ct = 0
    ct_nobetter = 0
    for result in sampler.sample(pos0, iterations=10000, storechain=False):
        print ct
        pos, lnprob = result[:2]
        maxidx = N.argmax(lnprob)

        if lnprob[maxidx] > lastmax:
            # write image
            lastmax = lnprob[maxidx]
            mimg = compute_image(pos[maxidx], img_r, img_g, img_b)
            out = N.dstack(mimg).astype(N.int32)
            out = N.clip(out, 0, 255)
            scipy.misc.imsave(outroot + '_binned.png', out)

            # save parameters
            N.savetxt(outroot + '_param.dat', scale*pos[maxidx])

            ct_nobetter = 0
            print(lastmax)

        ct += 1
        ct_nobetter += 1
        if ct_nobetter == 60:
            break

def main():
    for npts in 100, 300, 1000:
        for infile in sorted(glob.glob(os.path.join('images', '*'))):
            print infile
            outroot = '%s/%s_%i' % (
                'outdir',
                os.path.splitext(os.path.basename(infile))[0], npts)

            # race condition!
            lock = outroot + '.lock'
            if os.path.exists(lock):
                continue
            with open(lock, 'w') as f:
                pass

            process_image(infile, outroot, npts)

if __name__ == '__main__':
    main()

নিরক্ষিত-মুখোশযুক্ত চিত্রগুলি নীচের মত দেখাচ্ছে। এলোমেলো পয়েন্টগুলি চিত্র থেকে বেছে নেওয়া হয় যদি কোনও এলোমেলো সংখ্যা চিত্রের মানের চেয়ে কম হয় (1 হিসাবে ধরা হয়):

আনশার্পড মুখোশযুক্ত শনি চিত্র

আমি আরও বেশি সময় পেলে আমি আরও বড় ছবি এবং ভোরোনাই পয়েন্ট পোস্ট করব।

সম্পাদনা করুন: আপনি যদি ওয়াকারের সংখ্যা 100 * এনপিটিএসে বাড়িয়ে দেন তবে সমস্ত চ্যানেলের বিচ্যুতির স্কোয়ারগুলির কয়েকটি হিসাবে ব্যয়ের ফাংশনটি পরিবর্তন করুন এবং দীর্ঘ সময়ের জন্য অপেক্ষা করুন (লুপটি ভেঙে যাওয়ার জন্য পুনরাবৃত্তির সংখ্যা বাড়িয়ে তুলুন) 200), মাত্র 100 পয়েন্ট দিয়ে কিছু ভাল চিত্র তৈরি করা সম্ভব:

চিত্র 11, 100 পয়েন্ট চিত্র 2, 100 পয়েন্ট চিত্র 4, 100 পয়েন্ট চিত্র 10, 100 পয়েন্ট


3

পয়েন্ট ওজন মানচিত্র হিসাবে ইমেজ শক্তি ব্যবহার

আমার এই চ্যালেঞ্জের দিকে আমার দৃষ্টিভঙ্গিতে, আমি একটি নির্দিষ্ট চিত্রের অঞ্চলের "প্রাসঙ্গিকতা" মানচিত্রের একটি সম্ভাবনাটি যে একটি নির্দিষ্ট বিন্দুটি ভোরোনাই সেন্ট্রয়েড হিসাবে বেছে নেওয়া হবে তার দিকে চেয়েছিল। যাইহোক, আমি এখনও এলোমেলোভাবে ইমেজ পয়েন্টগুলি বেছে নিয়ে ভোরোনাই মোসেকিংয়ের শৈল্পিক অনুভূতিটি সংরক্ষণ করতে চেয়েছিলাম। অতিরিক্ত হিসাবে, আমি বড় চিত্রগুলিতে পরিচালনা করতে চেয়েছিলাম, তাই ডাউন স্যাম্পলিংয়ের প্রক্রিয়ায় আমি কোনও কিছুই হারাব না। আমার অ্যালগরিদম মোটামুটি এরকম:

  1. প্রতিটি চিত্রের জন্য একটি তীক্ষ্ণতা মানচিত্র তৈরি করুন। তীক্ষ্ণতা মানচিত্রটি স্বাভাবিক চিত্রের শক্তি দ্বারা নির্ধারিত হয় (বা চিত্রের উচ্চ ফ্রিকোয়েন্সি সংকেতের বর্গ)। একটি উদাহরণ এরকম দেখাচ্ছে:

তীক্ষ্ণতার মানচিত্র

  1. তীক্ষ্ণতা মানচিত্রে পয়েন্টগুলি থেকে 70 শতাংশ এবং অন্যান্য সমস্ত পয়েন্ট থেকে 30 শতাংশ গ্রহণ করে চিত্রটি থেকে প্রচুর পয়েন্ট তৈরি করুন। এর অর্থ এই যে পয়েন্টগুলি চিত্রের উচ্চ-বিশদ অংশগুলি থেকে আরও ঘনভাবে নমুনা করা হয়।
  2. রঙ!

ফলাফল

N = 100, 500, 1000, 3000

চিত্র 1, এন = 100 চিত্র 1, এন = 500 চিত্র 1, এন = 1000 চিত্র 1, এন = 3000

চিত্র 2, এন = 100 চিত্র 2, এন = 500 চিত্র 2, এন = 1000 চিত্র 2, এন = 3000

চিত্র 3, এন = 100 চিত্র 3, এন = 500 চিত্র 3, এন = 1000 চিত্র 3, এন = 3000

চিত্র 4, এন = 100 চিত্র 4, এন = 500 চিত্র 4, এন = 1000 চিত্র 4, এন = 3000

চিত্র 5, এন = 100 চিত্র 5, এন = 500 চিত্র 5, এন = 1000 চিত্র 5, এন = 3000

চিত্র 6, এন = 100 চিত্র 6, এন = 500 চিত্র 6, এন = 1000 চিত্র 6, এন = 3000

চিত্র 7, এন = 100 চিত্র 7, এন = 500 চিত্র 7, এন = 1000 চিত্র 7, এন = 3000

চিত্র 8, এন = 100 চিত্র 8, এন = 500 চিত্র 8, এন = 1000 চিত্র 8, এন = 3000

চিত্র 9, এন = 100 চিত্র 9, এন = 500 চিত্র 9, এন = 1000 চিত্র 9, এন = 3000

চিত্র 10, এন = 100 চিত্র 10, এন = 500 চিত্র 10, এন = 1000 চিত্র 10, এন = 3000

চিত্র 11, এন = 100 চিত্র 11, এন = 500 চিত্র 11, এন = 1000 চিত্র 11, এন = 3000

চিত্র 12, এন = 100 চিত্র 12, এন = 500 চিত্র 12, এন = 1000 চিত্র 12, এন = 3000

চিত্র 13, এন = 100 চিত্র 13, এন = 500 চিত্র 13, এন = 1000 চিত্র 13, এন = 3000

চিত্র 14, এন = 100 চিত্র 14, এন = 500 চিত্র 14, এন = 1000 চিত্র 14, এন = 3000


14
আপনি কি এতে কিছু মনে করবেন) এটি উত্পন্ন করতে উত্স কোড সহ, এবং খ) প্রতিটি থাম্বনেইলকে পূর্ণ আকারের চিত্রের সাথে লিঙ্ক করবেন?
মার্টিন ইন্ডার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.