Voronoi සිතියමක් ලෙස රූපයක් අඳින්න


169

මගේ අභියෝගාත්මක අදහස නිවැරදි දිශාවට යොමු කිරීම සඳහා කැල්වින්ගේ විනෝදාංශ සඳහා ගෞරවය.

තලයේ ඇති කරුණු සමූහයක් සලකා බලන්න, ඒවා අපි අඩවි ලෙස හඳුන්වන්නෙමු , එක් එක් වෙබ් අඩවිය සමඟ වර්ණයක් සම්බන්ධ කරන්නෙමු . දැන් ඔබට ආසන්නතම වෙබ් අඩවියේ වර්ණය සමඟ එක් එක් ලක්ෂ්‍යය වර්ණ ගැන්වීමෙන් මුළු තලයම පින්තාරු කළ හැකිය. මෙය Voronoi සිතියමක් (හෝ Voronoi රූප සටහන ) ලෙස හැඳින්වේ . ප්‍රතිපත්තිමය වශයෙන්, ඕනෑම දුර මෙට්‍රික් සඳහා වොරොනොයි සිතියම් අර්ථ දැක්විය හැකි නමුත් අපි සුපුරුදු යුක්ලීඩියානු දුර භාවිතා කරමු r = √(x² + y²). ( සටහන: මෙම අභියෝගයට තරඟ කිරීම සඳහා මේවායින් එකක් ගණනය කර ඉදිරිපත් කරන්නේ කෙසේදැයි ඔබ දැන සිටිය යුතු නොවේ.)

අඩවි 100 ක් සහිත උදාහරණයක් මෙන්න:

රූප විස්තරය මෙහි ඇතුළත් කරන්න

ඔබ කිසියම් සෛලයක් දෙස බැලුවහොත්, එම කොටුව තුළ ඇති සියලුම ලක්ෂ්‍ය වෙනත් වෙබ් අඩවියකට වඩා අනුරූප වෙබ් අඩවියට සමීප වේ.

ඔබේ කර්තව්‍යය වන්නේ එවැනි වොරොනෝයි සිතියමක් සමඟ දී ඇති රූපයක් ආසන්න වශයෙන් දැක්වීමයි. ඔබට රූපය ඕනෑම පහසු රාස්ටර් ග්‍රැෆික් ආකෘතියකින් මෙන්ම පූර්ණ සංඛ්‍යාවක් එන් . ඉන්පසු ඔබ එන් අඩවි දක්වා නිෂ්පාදනය කළ යුතු අතර, එක් එක් වෙබ් අඩවිය සඳහා වර්ණයක් ලබා දිය යුතුය , එනම් මෙම වෙබ් අඩවි මත පදනම් වූ වොරොනොයි සිතියම ආදාන රූපයට හැකි තරම් සමීපව සමාන වේ.

ඔබේ ප්‍රතිදානයෙන් Voronoi සිතියමක් ඉදිරිපත් කිරීමට ඔබට මෙම අභියෝගයේ පතුලේ ඇති Stack Snippet භාවිතා කළ හැකිය, නැතහොත් ඔබ කැමති නම් එය ඔබටම ඉදිරිපත් කළ හැකිය.

අඩවි සමූහයකින් (ඔබට අවශ්‍ය නම්) Voronoi සිතියමක් ගණනය කිරීම සඳහා ඔබට සාදන ලද හෝ තෙවන පාර්ශවීය කාර්යයන් භාවිතා කළ හැකිය .

මෙය ජනප්‍රියත්ව තරඟයක් බැවින් වැඩිම ශුද්ධ ඡන්ද සංඛ්‍යාවක් සහිත පිළිතුර ජය ගනී. පිළිතුරු විනිශ්චය කිරීමට ඡන්ද දායකයින් දිරිමත් කරනු ලැබේ

  • මුල් රූප සහ ඒවායේ වර්ණ ආසන්න වශයෙන් ගණනය කර ඇත.
  • ඇල්ගොරිතම විවිධ වර්ගයේ රූප මත කෙතරම් හොඳින් ක්‍රියා කරයිද?
  • කොතරම් හොඳින් ඇසුවොත් කුඩා සඳහා ක්රියා එන් .
  • ඇල්ගොරිතම අනුවර්තන පොකුරු රූපයේ කලාපයන්හි වැඩි විස්තර අවශ්‍යද යන්න.

රූප පරීක්ෂා කරන්න

ඔබගේ ඇල්ගොරිතම පරීක්ෂා කිරීම සඳහා පින්තූර කිහිපයක් මෙන්න (අපගේ සුපුරුදු සැකකරුවන්ගෙන් සමහරක්, සමහර නව ඒවා). විශාල අනුවාද සඳහා පින්තූර ක්ලික් කරන්න.

මහා රැල්ල හෙජ්ජෝග් වෙරළ කෝනල් සෙනසුරු දුඹුරු වලසා යොෂි මැන්ඩ්‍රිල් කකුළුවන් නිහාරිකාව Geobits 'Kid දිය ඇල්ල කෑගැසීම

පළමු පේළියේ වෙරළ තීරය ඔලිවියා බෙල් විසින් ඇද ගන්නා ලද අතර ඇයගේ අවසරය ඇතිව එය ඇතුළත් විය.

ඔබට අමතර අභියෝගයක් අවශ්‍ය නම්, සුදු පැහැති පසුබිමක් ඇති යොෂි උත්සාහ කර ඔහුගේ බඩ රේඛාව නිවැරදිව ලබා ගන්න.

ඔබට මෙම පරීක්ෂණ රූප සියල්ලම මෙම imgur ගැලරිය තුළ සොයාගත හැකි අතර එහිදී ඔබට ඒවා සියල්ලම zip ගොනුවක් ලෙස බාගත හැකිය. තවත් පරීක්ෂණයක් ලෙස අහඹු වොරොනෝයි රූප සටහනක් ඇල්බමයේ අඩංගු වේ. යොමු කිරීම සඳහා, එය ජනනය කළ දත්ත මෙන්න .

කරුණාකර විවිධාකාර රූප සඳහා උදාහරණ රූප සටහන් සහ එන් , උදා: 100, 300, 1000, 3000 (මෙන්ම අනුරූපී සෛල පිරිවිතරයන්ට පේස්ට්බින්). ඔබට සුදුසු යැයි පෙනෙන පරිදි සෛල අතර කළු දාර භාවිතා කිරීමට හෝ මඟ හැරීමට ඔබට හැකිය (මෙය සමහර රූපවලට අනෙක් ඒවාට වඩා හොඳින් පෙනේ). කෙසේ වෙතත් අඩවි ඇතුළත් නොකරන්න (වෙනම උදාහරණයක හැර ඔබේ වෙබ් අඩවි ස්ථානගත කිරීම ක්‍රියා කරන ආකාරය පැහැදිලි කිරීමට ඔබට අවශ්‍ය නම්).

ඔබට ප්‍රති results ල විශාල සංඛ්‍යාවක් පෙන්වීමට අවශ්‍ය නම් , පිළිතුරු වල ප්‍රමාණය සාධාරණව තබා ගැනීමට ඔබට 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
rofrogeyedpeas ඔබට ලැබෙන ඡන්ද දෙස බැලීමෙන්. ;) මෙය ජනප්‍රිය තරගයකි. එහි අත්යාවශ්යම නොවේ කරන්න හොඳම ක්රමය. අදහස නම් ඔබට හැකි පමණින් එය කිරීමට උත්සාහ කිරීම සහ ඔබ හොඳ වැඩක් කළ බවට ජනතාව එකඟ වන්නේද යන්න ඡන්දයෙන් පිළිබිඹු වේ. මේවායේ යම් විෂයානුබද්ධතාවයක් ඇති බව පිළිගත යුතුය. මා සම්බන්ධ කළ අභියෝග දෙස බලන්න, නැතහොත් මේ දෙස බලන්න . සාමාන්‍යයෙන් විවිධාකාර ප්‍රවේශයන් ඇති බව ඔබට පෙනෙනු ඇත, නමුත් වඩා හොඳ විසඳුම් ඉහළට ඔසවා ජයග්‍රාහකයෙකු තීරණය කිරීමට ඡන්ද ක්‍රමය උපකාරී වේ.
මාටින් එන්ඩර්

3
මෙතෙක් ඉදිරිපත් කරන ලද වෙරළ තීරයේ දළ විශ්ලේෂණය ඔලිවියා අනුමත කරයි.
ඇලෙක්ස් ඒ.

3
Lex ඇලෙක්සා. මෙතෙක් ඉදිරිපත් කර ඇති ඔහුගේ මුහුණේ සමහර ආසන්න කිරීම් ඩෙවොන් අනුමත කරයි . ඔහු කිසිදු n = 100 අනුවාදයක විශාල රසිකයෙක් නොවේ;)
Geobits

1
E ජියෝබිට්ස්: ඔහු වයසින් මුහුකුරා ගිය විට ඔහුට වැටහෙනු ඇත.
ඇලෙක්ස් ඒ.

1
මෙන්න සෙන්ට්‍රොයිඩ් වොරොනොයි මත පදනම් වූ ස්ටයිප්ලිං තාක්ෂණය පිළිබඳ පිටුවක් . හොඳ දේවානුභාවයෙන් යුත් ප්‍රභවයක් (අදාළ ප්‍රධාන නිබන්ධනය ඇල්ගොරිතම වැඩිදියුණු කළ හැකි ආකාරය පිළිබඳ හොඳ සාකච්ඡාවක් ඇත).
ජොබ්

Answers:


112

Python + scipy + scikit-image , බරින් යුත් Poisson තැටි නියැදීම

මගේ විසඳුම තරමක් සංකීර්ණයි. ශබ්දය ඉවත් කර එක් එක් ලක්ෂ්‍යය කොතරම් සිත්ගන්නාසුළුදැයි සිතියම් ගත කිරීම සඳහා මම රූපයේ පෙර සැකසුම් කිහිපයක් කරමි (දේශීය එන්ට්‍රොපි සහ දාර හඳුනාගැනීමේ සංයෝජනයක් භාවිතා කරමින්):

ඉන්පසු මම පොයිසන් තැටි නියැදියක් භාවිතා කර නියැදි ලකුණු තෝරා ගනිමි : රවුමේ දුර තීරණය වන්නේ අප කලින් තීරණය කළ බර අනුව ය.

නියැදි ලකුණු ලබාගත් පසු මම රූපය වොරොනොයි කොටස් වලට බෙදා වෙන් කර එක් එක් කොටස තුළ ඇති වර්ණ අගයන්හි L * a * b * සාමාන්‍යය එක් එක් කොටස වෙත පවරමි.

මා සතුව හියුරිස්ටික් රාශියක් ඇති අතර නියැදි ලකුණු ගණන ආසන්න බව තහවුරු කර ගැනීම සඳහා මම ටිකක් ගණිතයක් කළ යුතුය N. මම ලබා Novershooting හරියටම තරමක් කර පසුව සැකසීම සඳහා කලපුවේ සමග ඇතැම් කරුණු පහත වැටේ.

ධාවන කාලය අනුව, මෙම පෙරණය ලාභදායී නොවේ , නමුත් පහත රූපයක් සෑදීමට තත්පර 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 වේ:

abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc
abc abc abc abc


2
මම මෙයට කැමතියි; එය දුම් වීදුරුවක් වගේ.
බොබ් ද නියමයි

3
මම මේ සමඟ මඳක් අවුල් වූ අතර, ඔබට වඩා හොඳ ප්‍රති results ල ලැබෙනු ඇත, විශේෂයෙන් අඩු ත්‍රිකෝණ රූප සඳහා, ඔබ ඩෙනොයිස්_බිලටරල් වෙනුවට ඩෙනොයිස්_ටීවී_බ්‍රෙග්මන් ආදේශ කළහොත්. එය එහි ඩෙනොයිසින් තුළ ඊටත් වඩා පැච් ජනනය කරයි, එය උපකාරී වේ.
එල් ක්ලෙවින්

@LKlevin ඔබ භාවිතා කළ බර කුමක්ද?
orlp

මම බර ලෙස 1.0 භාවිතා කළෙමි.
එල් ක්ලෙවින්

65

සී ++

මගේ ප්‍රවේශය තරමක් මන්දගාමී ය, නමුත් එය ලබා දෙන ප්‍රති results ලවල ගුණාත්මකභාවය ගැන මම විශේෂයෙන් සතුටු වෙමි, විශේෂයෙන් දාර ආරක්ෂා කිරීම සම්බන්ධයෙන්. උදාහරණයක් ලෙස, මෙහි අඩවි 1000 ක් බැගින් ඇති යොෂි සහ කෝර්නෙල් පෙට්ටිය :

එය ටික් බවට පත් කරන ප්‍රධාන කොටස් දෙකක් තිබේ. පළමුවැන්න, evaluate()ශ්‍රිතයේ අන්තර්ගත වන අපේක්ෂක අඩවි ස්ථාන සමූහයක් ගෙන ඒවා මත ප්‍රශස්ත වර්ණ සකසා ඉලක්කගත රූපයට එදිරිව විදැහුම් කරන ලද වොරොනොයි ටෙසෙලේෂන් හි පීඑස්එන්ආර් සඳහා ලකුණු ලබා දෙයි . එක් එක් වෙබ් අඩවිය සඳහා වර්ණ තීරණය කරනු ලබන්නේ වෙබ් අඩවිය වටා ඇති කොටුව ආවරණය කර ඇති ඉලක්කගත පික්සෙල් සාමාන්‍යයෙනි. විචල්‍යතාවය, එම්එස්ඊ සහ පීඑස්එන්ආර් අතර සම්බන්ධතාවය ගසාකමින් එක් එක් සෛලයට හොඳම වර්ණය සහ එහි ප්‍රති ing ලයක් ලෙස ඇති පීඑස්එන්ආර් රූපය හරහා එක පාස් එකක් පමණක් භාවිතා කිරීමට මම වෙල්ෆර්ඩ්ගේ ඇල්ගොරිතම භාවිතා කරමි . මෙය වර්ණයට විශේෂ සැලකිල්ලක් නොදක්වා හොඳම අඩවි ස්ථාන සොයා ගැනීම සඳහා ගැටළුව අඩු කරයි.

දෙවන කොටස, පසුව, main()මෙම කට්ටලය සොයා ගැනීමට උත්සාහ කරයි. එය ආරම්භ වන්නේ අහඹු ලෙස ලකුණු සමූහයක් තෝරා ගැනීමෙනි. ඉන්පසු සෑම පියවරකදීම එය එක් ලක්ෂ්‍යයක් (රවුන්ඩ් රොබින් වෙත) ඉවත් කර එය ප්‍රතිස්ථාපනය කිරීම සඳහා අහඹු අපේක්ෂක ලකුණු සමූහයක් පරීක්ෂා කරයි. පොකුරේ ඉහළම PSNR ලබා දෙන එක පිළිගෙන තබා ගනී. Effectively ලදායි ලෙස, මෙය වෙබ් අඩවිය නව ස්ථානයකට පනින්නට හේතු වන අතර සාමාන්‍යයෙන් රූපය බිට්-බිට් වැඩි දියුණු කරයි. ඇල්ගොරිතම හිතාමතාම අපේක්ෂකයෙකු ලෙස මුල් ස්ථානය රඳවා නොගන්නා බව සලකන්න . සමහර විට මෙයින් අදහස් වන්නේ පැනීම සමස්ත රූපයේ ගුණාත්මකභාවය අඩු කරන බවයි. මෙය සිදුවීමට ඉඩ දීම දේශීය උපරිමයට හසු නොවී සිටීමට උපකාරී වේ. එය නැවැත්වීමේ නිර්ණායකයක් ද ලබා දෙයි; මෙතෙක් සොයාගෙන ඇති හොඳම අඩවි සමූහය වැඩිදියුණු නොකර නිශ්චිත පියවර ගණනක් අනුගමනය කිරීමෙන් පසු වැඩසටහන අවසන් වේ.

මෙම ක්‍රියාවට නැංවීම තරමක් මූලික වන අතර විශේෂයෙන් අඩවි ගණන වැඩි වන විට CPU-core කාලය පහසුවෙන් ගත කළ හැකි බව සලකන්න. එය සෑම අපේක්ෂකයෙකු සඳහාම සම්පූර්ණ වොරොනොයි සිතියම නැවත ගණනය කරන අතර තිරිසන් බලය සෑම පික්සෙල් සඳහාම සියලුම වෙබ් අඩවි වලට ඇති දුර පරීක්ෂා කරයි. එක් එක් මෙහෙයුමට වරකට එක් ලක්ෂ්‍යයක් ඉවත් කිරීම සහ තවත් එකක් එකතු කිරීම ඇතුළත් වන හෙයින්, සෑම පියවරකදීම රූපයේ සත්‍ය වෙනස්කම් තරමක් දේශීය වනු ඇත. වොරොනොයි රූප සටහන කාර්යක්ෂමව වැඩි දියුණු කිරීම සඳහා ඇල්ගොරිතම ඇති අතර ඔවුන් මෙම ඇල්ගොරිතමයට විශාල වේගයක් ලබා දෙනු ඇතැයි මම විශ්වාස කරමි. කෙසේ වෙතත්, මෙම තරගය සඳහා, මම දේවල් සරල හා තිරිසන් ලෙස තබා ගැනීමට තෝරාගෙන ඇත්තෙමි.

කේතය

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

ධාවනය

වැඩසටහන ස්වයං අන්තර්ගත වන අතර සම්මත පුස්තකාලයෙන් ඔබ්බට බාහිර පරායත්තතා නොමැත, නමුත් එයට රූප ද්විමය පීපීඑම් ආකෘතියේ තිබිය යුතුය. පින්තූර PPM බවට පරිවර්තනය කිරීම සඳහා මම ImageMagick භාවිතා කරමි , නමුත් GIMP සහ තවත් වැඩසටහන් කිහිපයකට එය කළ හැකිය.

එය සම්පාදනය කිරීම සඳහා, වැඩසටහන මෙසේ සුරකින්න, voronoi.cppපසුව ක්‍රියාත්මක කරන්න:

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

මම මෙය අත්හදා බැලුවේ නැතත්, එය දෘශ්‍ය ස්ටුඩියෝ හි මෑත සංස්කරණ සමඟ වින්ඩෝස් මත ක්‍රියා කරනු ඇතැයි මම බලාපොරොත්තු වෙමි. ඔබ C ++ 11 සමඟ සම්පාදනය කරන බව සහතික කිරීමට ඔබට අවශ්‍ය වනු ඇත. OpenMP දැඩි ලෙස අවශ්‍ය නොවේ, නමුත් එය ක්‍රියාත්මක කිරීමේ වේලාවන් වඩාත් ඉවසා දරා ගැනීමට එය බෙහෙවින් උපකාරී වේ.

එය ක්‍රියාත්මක කිරීමට, මෙවැනි දෙයක් කරන්න:

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

පසුකාලීන ගොනුව අඩවි දත්ත සමඟ යාවත්කාලීන වේ. පළමු පේළියේ රූපයේ පළල සහ උස ඇති අතර ගැටළු විස්තරයේ ජාවාස්ක්‍රිප්ට් විදැහුම්කරුට පිටපත් කිරීමට හා ඇලවීමට සුදුසු x, y, r, g, b අගයන් ඇත.

වැඩසටහනේ ඉහළින් ඇති නියතයන් තුන ඔබට වේගයෙන් එදිරිව ගුණාත්මකභාවය සඳහා එය සුසර කිරීමට ඉඩ දෙයි. මෙම decimationවර්ණ සහ PSNR සඳහා වෙබ් අඩවි මාලාවක් ඇගයීමට ලක් කරන විට සාධකයක් ඉලක්කය රූපය coarsens. එය වැඩි වන තරමට වැඩසටහන වේගයෙන් ක්‍රියාත්මක වේ. එය 1 ට සැකසීම සම්පූර්ණ විභේදන රූපය භාවිතා කරයි. මෙම candidatesනිරන්තර පාලන කොපමණ අපේක්ෂකයින් එක් එක් පියවර මත පරීක්ෂා කිරීමට. ඉහළට යාමට හොඳ ස්ථානයක් සොයා ගැනීමට වඩා හොඳ අවස්ථාවක් ලබා දෙන නමුත් වැඩසටහන මන්දගාමී කරයි. අවසාන වශයෙන්, terminationවැඩසටහනෙන් ඉවත්වීමට පෙර එහි ප්‍රතිදානය වැඩිදියුණු නොකර පියවර කීයක් යා හැකිද යන්නයි. එය වැඩි කිරීමෙන් වඩා හොඳ ප්‍රති results ල ලබා ගත හැකි නමුත් එය සුළු කාලයක් ගත වේ.

රූප

N = 100, 300, 1000 සහ 3000:


1
මෙය IMO දිනා ගත යුතුව තිබුණි - මට වඩා හොඳය.
orlp

1
@orlp - ස්තූතියි! සාධාරණ වීමට නම්, ඔබ ඉතා ඉක්මණින් ඔබගේ ලිපි පළ කළ අතර එය වඩාත් ඉක්මණින් ක්‍රියාත්මක වේ. වේග ගණනය!
බූජුම්

1
හොඳයි, මගේ ඇත්ත වශයෙන්ම වොරොනෝයි සිතියම් පිළිතුරක් නොවේ :) එය ඇත්තෙන්ම හොඳ නියැදි ඇල්ගොරිතමයකි, නමුත් නියැදි ලකුණු වොරොනොයි අඩවි බවට පත් කිරීම පැහැදිලිවම ප්‍රශස්ත නොවේ.
orlp

55

IDL, අනුවර්තී ශෝධනය

මෙම ක්‍රමය තාරකා විද්‍යාත්මක සමාකරණ වලින් අනුවර්තී දැල පිරිපහදු කිරීම සහ උප කොට් ision ාශ පෘෂ් by ය මගින් දේවානුභාවයෙන් . අයිඩීඑල් විසින්ම අභිමානවත් වන ආකාරයේ කාර්යයක් මෙය වන අතර, මට භාවිතා කිරීමට හැකි වූ බිල්ඩින් කාර්යයන් විශාල සංඛ්‍යාවක් මඟින් ඔබට පැවසිය හැකිය. : ඩී

මම කළු-පසුබිම් යොෂි පරීක්ෂණ රූපය සඳහා අතරමැදි කිහිපයක් ප්‍රතිදානය කර n = 1000ඇත.

පළමුවෙන්ම, අපි රූපයේ දීප්තිමත් ග්‍රීස්කේල් එකක් (භාවිතා කරමින් ) සිදු කරන අතර හොඳ දාර හඳුනා ගැනීම සඳහා ct_luminanceප්‍රිවිට් ෆිල්ටරයක් ​​( විකිපීඩියාවprewitt බලන්න ) යොදන්න :

abc abc

එවිට සැබෑ මැසිවිලි-වැඩ පැමිණේ: අපි රූපය 4 ට බෙදන්නෙමු, පෙරහන් කළ රූපයේ එක් එක් චතුරස්රයේ විචලනය මැනීම. අපගේ විචල්‍යතාවය උප කොට් ision ාශයේ ප්‍රමාණයෙන් බර කර ඇත (මෙම පළමු පියවරේදී එය සමාන වේ), එබැවින් ඉහළ විචල්‍යතාවයක් ඇති “දාර” කලාප කුඩා හා කුඩා හා කුඩා ලෙස බෙදී නොයයි. ඉන්පසුව, අපි වැඩි විස්තර සහිතව උප කොට් isions ාශ ඉලක්ක කර ගැනීම සඳහා බර කිරන විචල්‍යතාවය භාවිතා කරන අතර, අපගේ ඉලක්කගත අඩවි ගණනට පහර දෙන තෙක් එක් එක් සවිස්තරාත්මක කොටස අතිරේක 4 කට නැවත බෙදන්නෙමු (එක් එක් උප කොට් ision ාශයේ හරියටම එක් වෙබ් අඩවියක් අඩංගු වේ). අපි නැවත වරක් වෙබ් අඩවි 3 ක් එකතු කරන බැවින්, අපි අවසන් වන්නේ n - 2 <= N <= nඅඩවි සමඟ ය .

මෙම රූපය සඳහා මම උප කොට් process ාශ ක්‍රියාවලියෙහි .webm එකක් සෑදුවෙමි, එය මට කාවැද්දිය නොහැක, නමුත් එය මෙහි ඇත ; එක් එක් උපවගන්තියේ වර්ණය තීරණය වන්නේ බරින් යුත් විචලනයෙනි. (සුදු-පසුබිම් යොෂි සඳහා මම එකම ආකාරයේ වීඩියෝවක් සාදන ලදී, සංසන්දනය කිරීම සඳහා, වර්ණ වගුව ආපසු හැරවීම නිසා එය කළු වෙනුවට සුදු පැහැය දෙසට ගමන් කරයි; එය මෙහි ඇත .) උප කොට් ision ාශයේ අවසාන නිෂ්පාදනය මේ වගේ ය:

abc

අපගේ උප කොට් isions ාශ ලැයිස්තුවක් අප සතුව ඇති පසු, අපි එක් එක් උප කොට් ision ාශය හරහා යමු. අවසාන අඩවි පිහිටීම වන්නේ ප්‍රිවිට් රූපයේ අවම ස්ථානය, එනම් අවම “එඩිතර” පික්සෙල් වල පිහිටීම වන අතර කොටසේ වර්ණය එම පික්සෙල් වල වර්ණයයි; අඩවි සලකුණු කර ඇති මුල් රූපය මෙන්න:

abc

ඉන්පසුව, අපි triangulateඅඩවි වල Delaunay ත්රිකෝණය ගණනය කිරීම සඳහා ගොඩනගනු ලබන අතර voronoi, එක් එක් බහුඅස්රයන් එහි වර්ණයෙන් රූප බෆරයකට ඇඳීමට පෙර, එක් එක් Voronoi බහුඅස්රයේ සිරස් නිර්වචනය කිරීමට අපි ගොඩනගා ඇත. අවසාන වශයෙන්, අපි රූප බෆරයේ ඡායාරූපයක් සුරකිමු.

abc

කේතය:

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

abc abc abc abc abc abc abc abc abc abc abc abc abc

n = 500

abc abc abc abc abc abc abc abc abc abc abc abc abc

n = 1000

abc abc abc abc abc abc abc abc abc abc abc abc abc


9
මෙම විසඳුම වඩාත් සංකීර්ණ ප්‍රදේශවල වැඩි ලකුණු ප්‍රමාණයක් තැබීමට මම ඇත්තෙන්ම කැමතියි, එය මගේ අභිප්‍රාය යැයි සිතන අතර, මේ අවස්ථාවේ දී අනෙක් ඒවාගෙන් එය වෙන් කරයි.
ඇලෙක්සැන්ඩර්-බ්‍රෙට්

ඔව්, සවිස්තරාත්මක කාණ්ඩගත කරුණු පිළිබඳ අදහස මා අනුවර්තී
ශෝධනයට යොමු කළේ

3
ඉතා පිළිවෙලට පැහැදිලි කිරීමක් වන අතර රූප සිත් ඇදගන්නා සුළුය! මට ප්‍රශ්නයක් තිබේ - යොෂි සුදු පසුබිමක සිටින විට ඔබට වෙනස් රූප කිහිපයක් ලැබෙන බව පෙනේ, එහිදී අපට අමුතු හැඩයන් කිහිපයක් තිබේ. එයට හේතුව කුමක් විය හැකිද?
බ්‍රේන්ස්ටීල්

2
Rian බ්‍රයන්ස්ටීල් මම සිතන්නේ දළ සටහන් ඉහළ විචල්‍යතා ප්‍රදේශ ලෙස තෝරාගෙන අනවශ්‍ය ලෙස අවධානය යොමු කරනු ඇති අතර, පසුව අනෙකුත් සැබවින්ම ඉහළ විස්තර සහිත ප්‍රදේශවලට අඩු ලකුණු ප්‍රමාණයක් ලබා දී ඇත.
doppelgreener

Ra බ්‍රේන්ස්ටීල් මම හිතන්නේ ඩොපල් හරි - කළු මායිම සහ සුදු පසුබිම අතර ශක්තිමත් මායිමක් ඇත, එය ඇල්ගොරිතමයේ බොහෝ විස්තර ඉල්ලා සිටී. මෙය මට කළ හැකි දෙයක් (හෝ, වඩා වැදගත් ලෙස ) නිවැරදි කළ හැකිදැයි මට විශ්වාස නැත ...
සර්පර්සිවල්

47

Python 3 + PIL + SciPy, Fuzzy k-means

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)

ඇල්ගොරිතම

මූලික අදහස නම්, කේ-මධ්යන්ය පොකුරු කිරීම ස්වභාවිකවම රූපය වොරොනොයි සෛල වලට බෙදා වෙන් කරන බැවින් ලක්ෂ්යයන් ළඟම ඇති සෙන්ට්රොයිඩ් සමඟ බැඳී ඇති බැවිනි. කෙසේ වෙතත්, අපි කෙසේ හෝ අවහිරයක් ලෙස වර්ණ එකතු කළ යුතුය.

පළමුව අපි එක් එක් පික්සෙල් වඩා හොඳ වර්ණ හැසිරවීම සඳහා විද්‍යාගාර වර්ණ අවකාශයට පරිවර්තනය කරමු .

එවිට අපි "දුප්පත් මිනිසාගේ මායිම හඳුනාගැනීම" කරන්නෙමු. සෑම පික්සෙල් සඳහාම, අපි එහි විකලාංග හා විකර්ණ අසල්වැසියන් දෙස බලා වර්ණවල මධ්‍යන්‍ය-වර්ග වෙනස ගණනය කරමු. අපි පසුව මෙම වෙනස අනුව සියලුම පික්සෙල් වර්ග කරන්නෙමු, ලැයිස්තුවේ ඉදිරියෙන් සිටින අසල්වැසියන්ට වඩා පික්සෙල් සමාන වන අතර පික්සල් ඔවුන්ගේ අසල්වැසියන්ට පිටුපසින් වඩාත්ම සමාන වේ (එනම්, දාර ලක්ෂ්‍යයක් වීමට වැඩි ඉඩක් ඇත). මෙන්න පෘථිවියට උදාහරණයක්, පික්සෙල් දීප්තිමත් වන විට එය අසල්වැසියන්ට වඩා වෙනස් ය:

රූප විස්තරය මෙහි ඇතුළත් කරන්න

.

ඊළඟට, අපි මෙම පික්සල් ඇණවුම පොකුරු කළ යුතු ලකුණු විශාල සංඛ්‍යාවක් නියැදි කිරීමට භාවිතා කරමු. අපි on ාතීය බෙදාහැරීමක් භාවිතා කරන අතර, වඩාත් අද්විතීය හා “සිත්ගන්නාසුලු” ලකුණු වලට ප්‍රමුඛතාවය දෙන්නෙමු.

රූප විස්තරය මෙහි ඇතුළත් කරන්න

පොකුරු කිරීම සඳහා, අපි පළමුව Nසෙන්ට්‍රොයිඩ් තෝරා ගනිමු , අහඹු ලෙස තෝරාගෙන ඇත්තේ ඉහත on ාතීය ව්‍යාප්තියෙනි. ආරම්භක පුනරාවර්තනයක් සිදු කරන අතර, එහි ප්‍රති ing ලයක් ලෙස ඇති වන සෑම පොකුරු සඳහාම අපි මධ්‍යන්‍ය වර්ණයක් සහ වර්ණ විචල්‍යතා සීමාවක් පවරන්නෙමු. පුනරාවර්තන ගණනාවක් සඳහා, අපි:

  • අසල්වැසියන්ට පහසුවෙන් සෙන්ට්‍රොයිඩ් වලට විමසීමට හැකි වන පරිදි සෙන්ට්‍රොයිඩ් වල ඩෙලූනේ ත්‍රිකෝණය සාදන්න.
  • බොහෝ අසල්වැසියන්ට (> 4/5) ආසන්නව ඇති සෙන්ට්‍රොයිඩ් ඉවත් කිරීමට ත්රිකෝණය භාවිතා කරන්න. සම්බන්ධිත ඕනෑම නියැදි ලකුණු ද ඉවත් කරනු ලබන අතර නව ප්‍රතිස්ථාපන සෙන්ට්‍රොයිඩ් සහ නියැදි ලකුණු එකතු කරනු ලැබේ. මෙම පියවර ඇල්ගොරිතමයට වැඩි පොකුරු ස්ථානගත කිරීමට බල කිරීමට උත්සාහ කරයි.
  • නව සෙන්ට්‍රොයිඩ් සඳහා kd- ගසක් සාදන්න, එවිට අපට ඕනෑම නියැදි ස්ථානයකට ආසන්නතම සෙන්ට්‍රොයිඩ් පහසුවෙන් විමසිය හැකිය.
  • එක් එක් නියැදි ලක්ෂ්‍යය ආසන්නතම සෙන්ට්‍රොයිඩ් 6 න් එකකට පැවරීමට ගස භාවිතා කරන්න (6 ආනුභවිකව තෝරාගෙන ඇත). සෙන්ට්‍රොයිඩ් විසින් සාම්පල ලක්ෂ්‍යයක් පිළිගන්නේ ලක්ෂ්‍යයේ වර්ණය සෙන්ට්‍රොයිඩ් වල වර්ණ විචල්‍යතා සීමාව තුළ නම් පමණි. සෑම සාම්පල ලක්ෂ්‍යයක්ම පළමු පිළිගැනීමේ සෙන්ට්‍රොයිඩ් වෙත පැවරීමට අපි උත්සාහ කරමු, නමුත් එය කළ නොහැකි නම් අපි එය ආසන්නතම සෙන්ට්‍රොයිඩ් වෙත පවරමු. පොකුරු අතිච්ඡාදනය විය හැකි බැවින් ඇල්ගොරිතමයේ "නොපැහැදිලි බව" මෙම පියවරෙන් පැමිණේ.
  • සෙන්ට්‍රොයිඩ් නැවත ගණනය කරන්න.

රූප විස්තරය මෙහි ඇතුළත් කරන්න

(සම්පූර්ණ ප්‍රමාණය සඳහා ක්ලික් කරන්න)

අවසාන වශයෙන්, අපි ලකුණු විශාල සංඛ්‍යාවක් නියැදි කරමු, මේ වතාවේ ඒකාකාර බෙදාහැරීමක් භාවිතා කරමු. තවත් kd-tree භාවිතා කරමින්, අපි සෑම ලක්ෂ්‍යයක්ම එහි ආසන්නතම සෙන්ට්‍රොයිඩ් වෙත පවරා, පොකුරු සාදන්නෙමු. කඳු නැගීමේ ඇල්ගොරිතමයක් භාවිතා කරමින් අපි එක් එක් පොකුරු වල මධ්‍ය වර්ණය දළ වශයෙන් ගණනය කර අපගේ අවසාන සෛල වර්ණ ලබා දෙමු (මෙම පියවර සඳහා අදහස @PhiNotPi සහ @ MartBüttner ට ස්තූතියි).

රූප විස්තරය මෙහි ඇතුළත් කරන්න

සටහන්

ස්නිපෙට් ( OUTFILE) සඳහා පෙළ ගොනුවක් ප්‍රතිදානය කිරීමට අමතරව , වැඩසටහනට DEBUGසකසා ඇත්නම්, Trueඉහත පින්තූර ප්‍රතිදානය කර නැවත ලියයි. ඇල්ගොරිතම සෑම රූපයක් සඳහා මිනිත්තු කිහිපයක් ගත වේ, එබැවින් එය ප්‍රගතිය පරීක්ෂා කිරීමට හොඳ ක්‍රමයක් වන අතර එය ධාවන වේලාවට භයානක දේ එකතු නොකරයි.

නියැදි ප්‍රතිදාන

එන් = 32:

රූප විස්තරය මෙහි ඇතුළත් කරන්න

N = 100:

රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න

N = 1000:

රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න

එන් = 3000:

රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න


1
මම ඇත්තටම කැමතියි ඔයාගේ සුදු යෝෂි කොච්චර හොඳද කියලා.
මැක්ස්

අවසාන
ප්‍රති

26

ගණිතය, සසම්භාවී සෛල

මෙය මූලික විසඳුමයි, එබැවින් මම ඔබෙන් ඉල්ලා සිටින අවම දේ පිළිබඳ අදහසක් ඔබට ලැබෙනු ඇත. ගොනුවේ නම (දේශීයව හෝ URL ලෙස) fileසහ N දී ඇති විට n, පහත කේතය සරලවම N අහඹු පික්සෙල් තෝරාගෙන එම පික්සෙල්වල ඇති වර්ණ භාවිතා කරයි. මෙය ඇත්තෙන්ම බොළඳ එකක් වන අතර ඇදහිය නොහැකි තරම් හොඳින් ක්‍රියා නොකරයි, නමුත් මට අවශ්‍ය ඔබ මේ සියල්ල පරාජය කිරීමටයි. :)

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

N = 100 සඳහා වන සියලුම පරීක්ෂණ රූප මෙන්න (සියලුම පින්තූර විශාල අනුවාද වලට සම්බන්ධ වේ):

රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න

ඔබට පෙනෙන පරිදි, මේවා අත්‍යවශ්‍යයෙන්ම නිෂ් .ල ය. ඒවාට යම් කලාත්මක වටිනාකමක් තිබිය හැකි නමුත්, ප්‍රකාශන ආකාරයකින්, මුල් රූප යන්තම් හඳුනාගත නොහැකිය.

සඳහා N = 500 , තත්වය ටිකක් දියුණු වේ, නමුත් තවමත් ඉතා අමුතු සොයා බලා ඇත්තේ, ඒ රූප දෙස සේදී හා සෛල ගොඩක් විස්තර තොරව ප්රදේශ නාස්ති කර ඇත:

රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න රූප විස්තරය මෙහි ඇතුළත් කරන්න

ඔබේ වාරය!


මම හොඳ කෝඩරයක් නොව මගේ දෙවියනේ මේ රූප ලස්සන පෙනුමක්. නියම අදහස!
ෆරාස් මස්රූර්

කිසියම් හේතුවක් නිසා Dimensions@ImageDataනොව ImageDimensions? භාවිතා කිරීමෙන් ඔබට මන්දගාමී වීම ImageDataසම්පූර්ණයෙන්ම වළක්වා ගත හැකිය PixelValue.
2012rcampion

R 2012rcampion කිසිම හේතුවක් නැහැ, මම දැනගෙන හිටියේ නැහැ මේ දෙකම පවතින බව. මම පසුව එය නිවැරදි කර උදාහරණ රූප නිර්දේශිත N- අගයන්ට වෙනස් කරමි.
මාටින් එන්ඩර්

23

ගණිතය

මාටින් ගණිතයට ආදරය කරන බව අපි කවුරුත් දනිමු. එබැවින් මට ගණිතය සමඟ උත්සාහ කර බලන්නම්.

මගේ ඇල්ගොරිතමය ආරම්භක වොරොනෝයි රූප සටහනක් නිර්මාණය කිරීම සඳහා රූප දාරවල අහඹු ලකුණු භාවිතා කරයි. රූප සටහන පසුව සරල මධ්යන්ය පෙරණයක් සහිත දැලෙහි පුනරාවර්තන ගැලපුම මගින් මනාව සකස් කරනු ලැබේ. මෙය ඉහළ පරස්පර කලාප අසල ඉහළ සෛල ity නත්වයක් ඇති රූප සහ පිස්සු කෝණවලින් තොරව දෘශ්‍ය ප්‍රසන්න සෛල ලබා දෙයි.

පහත දැක්වෙන රූප මඟින් ක්‍රියාවලියේ ක්‍රියාවලිය පිළිබඳ උදාහරණයක් පෙන්වයි. ගණිතය නරක ඇන්ටිලියැසිං විසින් විනෝදය තරමක් නරක් වී ඇත, නමුත් අපට දෛශික ග්‍රැෆික්ස් ලැබේ, එය යමක් වටිනා විය යුතුය.

අහඹු නියැදීමකින් තොරව මෙම ඇල්ගොරිතම මෙහි ඇති 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 ක් සහිත වොරොනෝයි පරීක්ෂණ රූපය උත්සාහ කිරීමට අවශ්‍ය විය හැකිය (එය රූපයේ ඇති සෛල ගණන).
මාටින් එන්ඩර්

ස්තූතියි! මෙම උදාහරණය මත මගේ ඇල්ගොරිතම දරුණු ලෙස ක්‍රියා කරනු ඇතැයි මම සිතමි. බීජ සෛල දාරවල ආරම්භ වන අතර පුනරාවර්තනය වඩා හොඳ නොවනු ඇත;)
paw

මුල් රූපයට අභිසාරී වීමට මන්දගාමී අනුපාතයක් තිබියදීත්, ඔබේ ඇල්ගොරිතම ඉතා කලාත්මක ප්‍රති result ලයක් ලබා දෙන බව මට පෙනේ! (ජෝර්ජස් සේරත් වැඩ වල වැඩිදියුණු කළ අනුවාදයක් වැනි). නියම වැඩක්!
neizod

ඔබේ අවසාන රේඛා වෙනස් කිරීමෙන් ඔබට වීදුරු පෙනුමක් ඇති අන්තර්-බහුඅවයවික වර්ණ ලබා ගත හැකියGraphics@Table[ Append[mp, VertexColors -> RGBColor /@ ImageValue[img, First[mp]]], {mp, MeshPrimitives[voronoiRelaxed, 2]}]
හිස්ටෝග්‍රෑම්

13

Python + SciPy + emcee

මා භාවිතා කළ ඇල්ගොරිතම පහත දැක්වේ:

  1. පින්තූර කුඩා ප්‍රමාණයකට (pix 150 පික්සල්) ප්‍රමාණයට වෙනස් කරන්න
  2. උපරිම නාලිකා අගයන්හි නිරුපද්‍රිතව වෙස්මුහුණු රූපයක් සාදන්න (මෙය සුදු ප්‍රදේශ දැඩි ලෙස තෝරා නොගැනීමට උපකාරී වේ).
  3. නිරපේක්ෂ වටිනාකම ගන්න.
  4. මෙම රූපයට සමානුපාතික සම්භාවිතාවක් සහිත අහඹු ලකුණු තෝරන්න. මෙය අත්හිටුවීමේ දෙපස ලකුණු තෝරා ගනී.
  5. පිරිවැය ශ්‍රිතයක් අඩු කිරීම සඳහා තෝරාගත් කරුණු පිරිපහදු කරන්න. ශ්‍රිතය යනු නාලිකා වල වර්ග අපගමනයන්ගේ එකතුවෙහි උපරිමයයි (නැවතත් solid න වර්ණවලට නැඹුරු වීමට සහ white න සුදු පමණක් නොව). මාකෝව් චේන් මොන්ටේ කාලෝව එම්සී මොඩියුලය සමඟ (අතිශයින් නිර්දේශිත) ප්‍රශස්තකරණය කර ඇත. එන් දාම පුනරාවර්තනයෙන් පසුව නව දියුණුවක් සොයාගත නොහැකි වූ විට ක්‍රියා පටිපාටිය ඇරඹේ.

ඇල්ගොරිතම ඉතා හොඳින් ක්‍රියාත්මක වන බව පෙනේ. අවාසනාවට එයට සංවේදීව ධාවනය කළ හැක්කේ කුඩා රූප මත පමණි. වොරොනොයි ලකුණු ගෙන ඒවා විශාල රූපවලට යෙදීමට මට කාලය නැත. මෙම අවස්ථාවේදී ඒවා පිරිපහදු කළ හැකිය. වඩා හොඳ මිනිමා ලබා ගැනීම සඳහා මට තව දුරටත් MCMC ධාවනය කළ හැකිව තිබුණි. ඇල්ගොරිතමයේ දුර්වල කාරණය වන්නේ එය තරමක් මිල අධික වීමයි. මට ලකුණු 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 * npts දක්වා වැඩි කරන්නේ නම්, පිරිවැය ක්‍රියාකාරිත්වය සියලු නාලිකා වල අපගමනයන්ගේ වර්ග කිහිපයක් ලෙස වෙනස් කර දීර් time කාලයක් රැඳී සිටින්න (පුඩුවෙන් පිටතට යාම සඳහා පුනරාවර්තන ගණන වැඩි කිරීම 200), ලකුණු 100 ක් සමඟ හොඳ රූප කිහිපයක් සෑදිය හැකිය:

රූපය 11, ලකුණු 100 යි රූපය 2, ලකුණු 100 යි රූපය 4, ලකුණු 100 යි රූපය 10, ලකුණු 100 යි


3

ලක්ෂ්‍ය-බර සිතියමක් ලෙස රූප ශක්තිය භාවිතා කිරීම

මෙම අභියෝගයට මගේ ප්‍රවේශයේදී, මට අවශ්‍ය වූයේ යම් රූපයක් වෝරෝනොයි සෙන්ට්‍රොයිඩ් ලෙස තෝරා ගැනීමේ සම්භාවිතාවයට විශේෂිත රූප ප්‍රදේශයක “අදාළත්වය” සිතියම් ගත කිරීමට ය. කෙසේ වෙතත්, අහඹු ලෙස රූප ලක්ෂ්‍ය තෝරා ගැනීමෙන් වොරොනෝයි මොසෙයික් කිරීමේ කලාත්මක හැඟීම ආරක්ෂා කර ගැනීමට මට තවමත් අවශ්‍ය විය. මීට අමතරව, මට විශාල රූප මත ක්‍රියා කිරීමට අවශ්‍ය විය, එබැවින් පහත හෙලීමේ ක්‍රියාවලියේදී මට කිසිවක් අහිමි නොවේ. මගේ ඇල්ගොරිතම දළ වශයෙන් මේ වගේ ය:

  1. සෑම රූපයක් සඳහාම තියුණු සිතියමක් සාදන්න. තියුණු සිතියම අර්ථ දැක්වෙන්නේ සාමාන්‍යකරණය කළ රූප ශක්තිය (හෝ රූපයේ ඉහළ සංඛ්‍යාත සං signal ාවේ වර්ග) ය. උදාහරණයක් මේ වගේ ය:

තියුණු සිතියම

  1. තියුණු සිතියමේ ඇති ලකුණු වලින් සියයට 70 ක් සහ අනෙක් සියලුම ලක්ෂ්‍යයන්ගෙන් සියයට 30 ක් ලබා ගනිමින් රූපයෙන් ලකුණු ගණනාවක් ජනනය කරන්න. මෙයින් අදහස් කරන්නේ රූපයේ ඉහළ සවිස්තරාත්මක කොටස් වලින් ලකුණු වඩාත් ely න ලෙස සාම්පල ලබා ගන්නා බවයි.
  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
අ) මෙය ජනනය කිරීම සඳහා භාවිතා කරන ප්‍රභව කේතය ඇතුළුව, සහ ආ) සෑම සිඟිති රූපයක්ම සම්පූර්ණ ප්‍රමාණයේ රූපයට සම්බන්ධ කරන්න.
මාටින් එන්ඩර්
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.