ফটোমোসাইক বা: একটি হালকা বাল্ব প্রতিস্থাপন করতে কত প্রোগ্রামার লাগবে?


33

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

স্ট্যাকওভারফ্লো হেডশটগুলি মোজাইক

আপনার কাজটি এমন একটি অ্যালগরিদম লিখতে হবে যা তাদের এই 45 × 45 গ্রিড থেকে 48 × 48 পিক্সেল অবতার ব্যবহার করে অন্য চিত্রের একটি সঠিক ফটোমোস্যাক তৈরি করবে।

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

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

লাইটবাল্ব চুমু লা গ্র্যান্ড জাট্টের দ্বীপে একটি রবিবার দুপুর স্টিভ জবস গোলকের

উইকিপিডিয়া ধন্যবাদ রেট্রেসড গোলক বাদে সকলের জন্য।

পূর্ণ আকারে এই চিত্রগুলির 48 টি দ্বারা বিভাজ্য মাত্রা রয়েছে The বড়গুলি জেপিইজি থাকতে হয়েছিল যাতে তারা আপলোড করার জন্য যথেষ্ট সংকুচিত হতে পারে।

স্কোরিং

এটি একটি জনপ্রিয়তা প্রতিযোগিতা। মোজাইকগুলির সাথে জমা দেওয়া যা আসল চিত্রগুলিকে সুনির্দিষ্টভাবে চিত্রিত করে ভোট দেওয়া উচিত। আমি এক বা দুই সপ্তাহের মধ্যে সর্বাধিক ভোট দেওয়া উত্তর গ্রহণ করব।

বিধি

  • আপনার ফোটোমোসাইকগুলি অবশ্যই গ্রিডে সাজিয়ে উপরে মোজাইক থেকে নেওয়া অবিকল্পিত 48 × 48 পিক্সেল অবতারের সাথে সম্পূর্ণরূপে গঠিত ।

  • আপনি কোনও মোজাইকতে অবতার পুনরায় ব্যবহার করতে পারেন। (প্রকৃতপক্ষে বৃহত্তর পরীক্ষার চিত্রগুলির জন্য আপনাকে অবশ্যই করতে হবে))

  • আপনার আউটপুটটি দেখান, তবে মনে রাখবেন যে পরীক্ষার চিত্রগুলি খুব বড় এবং স্ট্যাকএক্সচেঞ্জ কেবল 2MB অবধি ছবি পোস্ট করার অনুমতি দেয় । সুতরাং আপনার চিত্রগুলি সংকুচিত করুন বা সেগুলি অন্য কোথাও হোস্ট করুন এবং ছোট সংস্করণ এখানে রাখুন।

  • বিজয়ী নিশ্চিত হওয়ার জন্য আপনাকে অবশ্যই আপনার হালকা বাল্ব বা গোলক মোজাইকের পিএনজি সংস্করণ সরবরাহ করতে হবে। মোজাইককে আরও ভাল করে তুলতে আপনি অবতারগুলিতে অতিরিক্ত রঙ যুক্ত করছেন না তা নিশ্চিত করার জন্য আমি এগুলি (নীচের দেখুন) যাচাই করতে পারি।

ভ্যালিডেটার

এই পাইথন স্ক্রিপ্টটি কোনও মোজাইক সত্যই অবারিত অবতার ব্যবহার করে কিনা তা পরীক্ষা করতে ব্যবহার করা যেতে পারে। শুধু সেট toValidateএবং allTiles। পিক্সেল-পিক্সেলের জন্য জিনিসগুলির হুবহু তুলনা করার কারণে এটি জেপিইজি বা অন্যান্য ক্ষতিকারক বিন্যাসগুলির জন্য কাজ করার সম্ভাবনা নেই।

from PIL import Image, ImageChops

toValidate = 'test.png' #test.png is the mosaic to validate
allTiles = 'avatars.png' #avatars.png is the grid of 2025 48x48 avatars

def equal(img1, img2):
    return ImageChops.difference(img1, img2).getbbox() is None

def getTiles(mosaic, (w, h)):
    tiles = {}
    for i in range(mosaic.size[0] / w):
        for j in range(mosaic.size[1] / h):
            x, y = i * w, j * h
            tiles[(i, j)] = mosaic.crop((x, y, x + w, y + h))
    return tiles

def validateMosaic(mosaic, allTiles, tileSize):
    w, h = tileSize
    if mosaic.size[0] % w != 0 or mosaic.size[1] % h != 0:
        print 'Tiles do not fit mosaic.'
    elif allTiles.size[0] % w != 0 or allTiles.size[1] % h != 0:
        print 'Tiles do not fit allTiles.'
    else:
        pool = getTiles(allTiles, tileSize)
        tiles = getTiles(mosaic, tileSize)
        matches = lambda tile: equal(tiles[pos], tile)
        success = True
        for pos in tiles:
            if not any(map(matches, pool.values())):
                print 'Tile in row %s, column %s was not found in allTiles.' % (pos[1] + 1, pos[0] + 1)
                success = False
        if success:
            print 'Mosaic is valid.'
            return
    print 'MOSAIC IS INVALID!'

validateMosaic(Image.open(toValidate).convert('RGB'), Image.open(allTiles).convert('RGB'), (48, 48))

সৌভাগ্য সবার! আমি ফলাফল দেখতে অপেক্ষা করতে পারি না।

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


1
এটি মূলত আগেরটির সদৃশ নয়? প্রত্যেকের রঙ গণনা করুন, 2025px লক্ষ্যটি ডাউনস্কেল করুন এবং বিদ্যমান অ্যালগরিদমটি প্রয়োগ করুন?
জন ডিভোরাক


2
@ জনডভোরাক এটি একই রকম তবে আমি মনে করি এটি সদৃশ হওয়ার পক্ষে যথেষ্ট নয়। আপনি উল্লিখিত অ্যালগরিদম হল ফলাফল পাওয়ার এক উপায়। যদিও আরও অনেক পরিশীলিত সমাধান রয়েছে।
হাওয়ার্ড

1
আমার বিড়ালটি অবতারদের থেকে অনুপস্থিত :-(
জো

2
আপনি " একটি হালকা বাল্ব তৈরি করতে" " একটি হালকা বাল্ব প্রতিস্থাপন করতে " পরিবর্তন করতে ইচ্ছুক হতে পারেন ।
ডেভিডসি

উত্তর:


15

জাভা, গড় দূরত্ব

package photomosaic;

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

import javax.imageio.ImageIO;

public class MainClass {

    static final String FILE_IMAGE = "45148_sunday.jpg";
    static final String FILE_AVATARS = "25745_avatars.png";
    static final String FILE_RESULT = "mosaic.png";

    static final int BLOCKSIZE = 48;

    static final int SCALING = 4;

    static final int RADIUS = 3;

    public static void main(String[] args) throws IOException {
        BufferedImage imageAvatars = ImageIO.read(new File(FILE_AVATARS));
        int[] avatars = deBlock(imageAvatars, BLOCKSIZE);

        BufferedImage image = ImageIO.read(new File(FILE_IMAGE));
        int[] data = deBlock(image, BLOCKSIZE);

        // perform the mosaic search on a downscaled version
        int[] avatarsScaled = scaleDown(avatars, BLOCKSIZE, SCALING);
        int[] dataScaled = scaleDown(data, BLOCKSIZE, SCALING);
        int[] bests = mosaicize(dataScaled, avatarsScaled, (BLOCKSIZE / SCALING) * (BLOCKSIZE / SCALING), image.getWidth() / BLOCKSIZE);

        // rebuild the image from the mosaic tiles
        reBuild(bests, data, avatars, BLOCKSIZE);

        reBlock(image, data, BLOCKSIZE);
        ImageIO.write(image, "png", new File(FILE_RESULT));
    }

    // a simple downscale function using simple averaging
    private static int[] scaleDown(int[] data, int size, int scale) {
        int newsize = size / scale;
        int newpixels = newsize * newsize;
        int[] result = new int[data.length / scale / scale];
        for (int r = 0; r < result.length; r += newpixels) {
            for (int y = 0; y < newsize; y++) {
                for (int x = 0; x < newsize; x++) {
                    int avgR = 0;
                    int avgG = 0;
                    int avgB = 0;
                    for (int sy = 0; sy < scale; sy++) {
                        for (int sx = 0; sx < scale; sx++) {
                            int dt = data[r * scale * scale + (y * scale + sy) * size + (x * scale + sx)];
                            avgR += (dt & 0xFF0000) >> 16;
                            avgG += (dt & 0xFF00) >> 8;
                            avgB += (dt & 0xFF) >> 0;
                        }
                    }
                    avgR /= scale * scale;
                    avgG /= scale * scale;
                    avgB /= scale * scale;
                    result[r + y * newsize + x] = 0xFF000000 + (avgR << 16) + (avgG << 8) + (avgB << 0);
                }
            }
        }
        return result;
    }

    // the mosaicize algorithm: take the avatar with least pixel-wise distance
    private static int[] mosaicize(int[] data, int[] avatars, int pixels, int tilesPerLine) {
        int tiles = data.length / pixels;

        // use random order for tile search
        List<Integer> ts = new ArrayList<Integer>();
        for (int t = 0; t < tiles; t++) {
            ts.add(t);
        }
        Collections.shuffle(ts);

        // result array
        int[] bests = new int[tiles];
        Arrays.fill(bests, -1);

        // make list of offsets to be ignored
        List<Integer> ignores = new ArrayList<Integer>();
        for (int sy = -RADIUS; sy <= RADIUS; sy++) {
            for (int sx = -RADIUS; sx <= RADIUS; sx++) {
                if (sx * sx + sy * sy <= RADIUS * RADIUS) {
                    ignores.add(sy * tilesPerLine + sx);
                }
            }
        }

        for (int t : ts) {
            int b = t * pixels;
            int bestsum = Integer.MAX_VALUE;
            for (int at = 0; at < avatars.length / pixels; at++) {
                int a = at * pixels;
                int sum = 0;
                for (int i = 0; i < pixels; i++) {
                    int r1 = (avatars[a + i] & 0xFF0000) >> 16;
                    int g1 = (avatars[a + i] & 0xFF00) >> 8;
                    int b1 = (avatars[a + i] & 0xFF) >> 0;

                    int r2 = (data[b + i] & 0xFF0000) >> 16;
                    int g2 = (data[b + i] & 0xFF00) >> 8;
                    int b2 = (data[b + i] & 0xFF) >> 0;

                    int dr = (r1 - r2) * 30;
                    int dg = (g1 - g2) * 59;
                    int db = (b1 - b2) * 11;

                    sum += Math.sqrt(dr * dr + dg * dg + db * db);
                }
                if (sum < bestsum) {
                    boolean ignore = false;
                    for (int io : ignores) {
                        if (t + io >= 0 && t + io < bests.length && bests[t + io] == at) {
                            ignore = true;
                            break;
                        }
                    }
                    if (!ignore) {
                        bestsum = sum;
                        bests[t] = at;
                    }
                }
            }
        }
        return bests;
    }

    // build image from avatar tiles
    private static void reBuild(int[] bests, int[] data, int[] avatars, int size) {
        for (int r = 0; r < bests.length; r++) {
            System.arraycopy(avatars, bests[r] * size * size, data, r * size * size, size * size);
        }
    }

    // splits the image into blocks and concatenates all the blocks
    private static int[] deBlock(BufferedImage image, int size) {
        int[] result = new int[image.getWidth() * image.getHeight()];
        int r = 0;
        for (int fy = 0; fy < image.getHeight(); fy += size) {
            for (int fx = 0; fx < image.getWidth(); fx += size) {
                for (int l = 0; l < size; l++) {
                    image.getRGB(fx, fy + l, size, 1, result, r * size * size + l * size, size);
                }
                r++;
            }
        }
        return result;
    }

    // unsplits the block version into the original image format
    private static void reBlock(BufferedImage image, int[] data, int size) {
        int r = 0;
        for (int fy = 0; fy < image.getHeight(); fy += size) {
            for (int fx = 0; fx < image.getWidth(); fx += size) {
                for (int l = 0; l < size; l++) {
                    image.setRGB(fx, fy + l, size, 1, data, r * size * size + l * size, size);
                }
                r++;
            }
        }
    }
}

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

এই কোডটি টাইলগুলিতে কোনও পরিবর্তন করে না (উদাহরণস্বরূপ গন্তব্যের রঙগুলিতে কোনও অভিযোজন নয়)।

ফলাফল

পূর্ণ আকার ইমেজ জন্য ক্লিক করুন.

হালকা বাল্ড গোলকের
রবিবার

ব্যাসার্ধের প্রভাব

radiusআপনি ব্যবহার করে ফলস্বরূপ টাইলগুলির পুনরাবৃত্তি হ্রাস করতে পারে। সেট radius=0করার কোনও প্রভাব নেই। যেমন radius=33 টাইলের ব্যাসার্ধের মধ্যে একই টাইলকে দমন করে।

হালকা বাল্ড রবিবার ব্যাসার্ধ = 0

হালকা বাল্ড
হালকা বাল্ড
ব্যাসার্ধ = 3

স্কেলিং ফ্যাক্টরের প্রভাব

scalingফ্যাক্টরটি ব্যবহার করে আমরা নির্ধারণ করতে পারি যে কীভাবে ম্যাচের টাইল অনুসন্ধান করা হয়। একটি গড়-টাইল অনুসন্ধান করার scaling=1সময় পিক্সেল-নিখুঁত মিলের scaling=48সন্ধান করা।

স্কেলিং 48
স্কেলিং = 48

স্কেলিং 16
স্কেলিং = 16

স্কেলিং 4
স্কেলিং = 4

স্কেলিং 1
স্কেলিং = 1


1
কি দারুন. ব্যাসার্ধের ফ্যাক্টর সত্যই ফলাফলগুলি উন্নত করে। সেই একই অবতার ব্লটগুলি ভাল ছিল না।
জন ডিভোরাক

1
এটি আমি কিনা তা নিশ্চিত নই, তবে ইমোগুরের তুলনায় পিকচারশাকের কাছে নৃশংস ব্যান্ডউইথ রয়েছে বলে মনে হয়েছে
নিক টি

@ নিকট সম্ভবত, তবে ইমগুর সর্বাধিক 1MB ( imgur.com/faq#size ) এ সমস্ত কিছু সংকুচিত করে । :(
ক্যালভিনের শখ

হুঁ, কেবলমাত্র আমিই নাকি ডেভিডের ম্যাথমেটিকা ​​উত্তরটি এই শীর্ষ ভোটের উত্তরের চেয়ে অনেক ভাল?
জাস্টহাল্ফ

খুব খারাপ যে সমস্ত ফটো গেছে। আপনি কি কোনও সুযোগে পুনরায় আপলোড করতে পারেন?
MCMasty

19

গাণিতিকরণের জন্য নিয়ন্ত্রণ সহ গণিত,

এটি প্রয়োজন হিসাবে 48 x 48 পিক্সেল ফটো ব্যবহার করে। ডিফল্টরূপে, এটি চিত্রটি অনুমানের জন্য সম্পর্কিত 48x48 পিক্সেল বর্গক্ষেত্রের জন্য এই পিক্সেলগুলিকে অদলবদল করবে।

তবে গন্তব্য বর্গক্ষেত্রের আকার 48 x 48 এর চেয়ে ছোট হতে সেট করা যেতে পারে, যাতে বিশদে আরও বেশি বিশ্বস্ততার সুযোগ হয়। (নীচের উদাহরণগুলি দেখুন)।

প্যালেটটি প্রসেস করা হচ্ছে

collage প্যালেট হিসাবে পরিবেশন করার জন্য ছবিযুক্ত ছবি।

picsColorsপৃথক ফটোগুলির একটি তালিকা যা তাদের গড় লাল, গড় সবুজ এবং নীল মানগুলির সাথে যুক্ত।

টার্গেট কালারটোফোটো []। লক্ষ্য সোথের গড় রঙ নেয় এবং প্যালেট থেকে ছবিটি খুঁজে পায় যা এটির সাথে সবচেয়ে ভাল মেলে।

parts=Flatten[ImagePartition[collage,48],1];
picsColors={#,c=Mean[Flatten[ImageData[#],1]]}&/@parts;
nf=Nearest[picsColors[[All,2]]];

targetColorToPhoto[p_]:=Cases[picsColors,{pic_,nf[p,1][[1]]}:>pic][[1]]

উদাহরণ

আরজিবিসিওলারের সাথে সবচেয়ে ভাল মেলে এমন ফটোটি সন্ধান করুন [0.640, 0.134, 0.249]:

example1


photoMosaic

photoMosaic[rawPic_, targetSwathSize_: 48] :=
 Module[{targetPic, data, dims, tiles, tileReplacements, gallery},
  targetPic = Image[data = ImageData[rawPic] /. {r_, g_, b_, _} :> {r, g, b}];
  dims = Dimensions[data];
  tiles = ImagePartition[targetPic, targetSwathSize];
  tileReplacements = targetColorToPhoto /@ (Mean[Flatten[ImageData[#], 1]] & /@Flatten[tiles, 1]);
  gallery = ArrayReshape[tileReplacements, {dims[[1]]/targetSwathSize,dims[[2]]/targetSwathSize}];
  ImageAssemble[gallery]]

`ফটোমোসাক ইনপুট হিসাবে কাঁচা ছবি নেয় যা আমরা একটি ফটো মোজাইক করব।

targetPic কেবলমাত্র আর, জি, বি রেখে একটি চতুর্থ প্যারামিটার (পিএনজি এবং কিছু জেপিজির) সরিয়ে ফেলবে

dimsএর মাত্রা targetPic

tiles ছোট স্কোয়ারগুলি হ'ল একসাথে লক্ষ্য ছবিটি নিয়ে।

targetSwathSize is the granularity parameter; it defaults at 48 (x48).

tileReplacements যথাযথ ক্রমে প্রতিটি টাইলের সাথে মেলে এমন ফটো।

gallery সঠিক মাত্রা (যেমন টাইলগুলির সাথে মেলে এমন সারি এবং কলামের সংখ্যা) সহ টাইল-প্রতিস্থাপনের (ফটো) সেট।

ImageAssembly অবিচ্ছিন্ন আউটপুট চিত্রে মোজাইকটিকে যোগ দেয়।


উদাহরণ

এটি রবিবার, চিত্র থেকে প্রতিটি 12x12 বর্গকে প্রতিস্থাপিত করে একটি যথাযথ 48 x 48 পিক্সেল ফটোগ্রাফের সাথে এটি গড় রঙের সাথে সেরা মেলে।

photoMosaic[sunday, 12]

sunday2


রবিবার (বিস্তারিত)

মাথার টুপি


photoMosaic[lightbulb, 6]

লাইটবুল্ব 6


photoMosaic[stevejobs, 24]

স্টিভ কাজ 24


বিস্তারিত, স্টিভজবস

কাজের বিবরণ


photoMosaic[kiss, 24]

চুম্বন


চুম্বনের বিস্তারিত:

বিস্তারিত চুম্বন


photoMosaic[spheres, 24]

গোলকের


1
গ্রানুলারিটির ধারণাটি আমার পছন্দ হয়েছে। এটি ছোট চিত্রগুলিকে আরও বাস্তবতা দেয়।
ক্যালভিনের শখ 20

7

জাতীয়

পূর্বের গল্ফের মতো: http://jsfiddle.net/eithe/J7jEk/ : D

(এই সময়ের সাথে ডাকা unique: false, {pixel_2: {width: 48, height: 48}, pixel_1: {width: 48, height: 48}}) (একবারে পিক্সেল একবার ব্যবহার করার জন্য প্যালেটটি ব্যবহার করবেন না, প্যালেট পিক্সেলগুলি 48x48 স্য্যাচ, আকার পিক্সেল 48x48 স্য্যাচ হয়)।

নির্বাচিত অ্যালগরিদমের ওজন দ্বারা নিকটতম মিলটি খুঁজে পেতে বর্তমানে এটি অবতারের তালিকার সন্ধান করে, তবে এটি কোনও রঙের অভিন্নতা মেলানো সম্পাদন করে না (এমন কিছু যা আমাকে একবার দেখার প্রয়োজন)।

  • সুষম
  • গবেষণাগার

দুর্ভাগ্যক্রমে আমি বড় চিত্রগুলির সাথে চারপাশে খেলতে পারছি না, কারণ আমার র‌্যাম ফুরিয়েছে: ডি সম্ভব হলে আমি ছোট আউটপুট চিত্রগুলির প্রশংসা করি। যদি সরবরাহ করা চিত্রের 1/2 ব্যবহার করা হয় তবে রবিবার দুপুরে এখানে:

  • সুষম
  • গবেষণাগার

2
আমি সবেমাত্র অর্ধ-আকারের চিত্রগুলি যুক্ত করেছি যা এখনও 48 পিক্সেল দ্বারা বিভাজ্য।
ক্যালভিনের শখ 21

5

GLSL

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

পদ্ধতি

প্রকৃত টাইল মিলানো সহজ: আমি একটি লক্ষ্য অঞ্চল এবং মোজাইক টাইল অঞ্চলে প্রতিটি পিক্সেলের মধ্যে আরজিবি দূরত্ব গণনা করি এবং সর্বনিম্ন পার্থক্য (উজ্জ্বলতার মান দ্বারা ভারিত) সহ টাইলটি বেছে নিই। টাইল সূচকটি ফ্রেমের লাল এবং সবুজ বর্ণের বৈশিষ্ট্যগুলিতে লেখা আছে, তারপরে সমস্ত টুকরো টুকরো টুকরো রেন্ডার হওয়ার পরে আমি ফ্রেমবফারের বাইরে মানগুলি পড়ি এবং সেই সূচকগুলি থেকে আউটপুট চিত্র তৈরি করি। আসল বাস্তবায়ন বেশ হ্যাক; এফবিও তৈরির পরিবর্তে আমি কেবল একটি উইন্ডো খুললাম এবং এতে রেন্ডার করলাম, তবে জিএলএফডাব্লু নির্বিচারে ছোট রেজোলিউশনে উইন্ডো খুলতে পারে না, তাই আমি প্রয়োজনের চেয়ে উইন্ডোটি বড় করে তৈরি করি, তারপরে একটি ছোট আয়তক্ষেত্র আঁকো যাতে এটি সঠিক আকারের হয় টাইল প্রতি এক টুকরা যা উত্সের চিত্রটিতে মানচিত্র করে। সম্পূর্ণ MSVC2013 সমাধানটি এখানে উপলব্ধ athttps://bitbucket.org/Gibgezr/mosaicmaker এটি সংকলনের জন্য GLFW / ফ্রিআইমেজ / GLEW / GLM এবং চালনার জন্য ওপেনজিএল 3.3 বা আরও ভাল ড্রাইভার / ভিডিও কার্ড প্রয়োজন।

খণ্ড শেডার উত্স

#version 330 core

uniform sampler2D sourceTexture;
uniform sampler2D mosaicTexture;

in vec2 v_texcoord;

out vec4 out_Color;

void main(void)
{   
    ivec2 sourceSize = textureSize(sourceTexture, 0);
    ivec2 mosaicSize = textureSize(mosaicTexture, 0);

    float num_pixels = mosaicSize.x/45.f;
    vec4 myTexel;
    float difference = 0.f;

    //initialize smallest difference to a large value
    float smallest_difference = 255.0f*255.0f*255.0f;
    int closest_x = 0, closest_y = 0;

    vec2 pixel_size_src = vec2( 1.0f/sourceSize.x, 1.0f/sourceSize.y);
    vec2 pixel_size_mosaic = vec2( 1.0f/mosaicSize.x , 1.0f/mosaicSize.y);

    vec2 incoming_texcoord;
    //adjust incoming uv to bottom corner of the tile space
    incoming_texcoord.x =  v_texcoord.x - 1.0f/(sourceSize.x / num_pixels * 2.0f);
    incoming_texcoord.y =  v_texcoord.y - 1.0f/(sourceSize.y / num_pixels * 2.0f);

    vec2 texcoord_mosaic;
    vec2 pixelcoord_src, pixelcoord_mosaic;
    vec4 pixel_src, pixel_mosaic;

    //loop over all of the mosaic tiles
    for(int i = 0; i < 45; ++i)
    {
        for(int j = 0; j < 45; ++j)
        {
            difference = 0.f;
            texcoord_mosaic = vec2(j * pixel_size_mosaic.x * num_pixels, i * pixel_size_mosaic.y * num_pixels);

            //loop over all of the pixels in the images, adding to the difference
            for(int y = 0; y < num_pixels; ++y)
            {
                for(int x = 0; x < num_pixels; ++x)
                {
                    pixelcoord_src = vec2(incoming_texcoord.x + x * pixel_size_src.x, incoming_texcoord.y + y * pixel_size_src.y);                  
                    pixelcoord_mosaic = vec2(texcoord_mosaic.x + x * pixel_size_mosaic.x, texcoord_mosaic.y + y * pixel_size_mosaic.y); 
                    pixel_src = texture(sourceTexture, pixelcoord_src);
                    pixel_mosaic = texture(mosaicTexture, pixelcoord_mosaic);

                    pixel_src *= 255.0f;
                    pixel_mosaic *= 255.0f;

                    difference += (pixel_src.x - pixel_mosaic.x) * (pixel_src.x - pixel_mosaic.x) * 0.5f+
                        (pixel_src.y - pixel_mosaic.y) * (pixel_src.y - pixel_mosaic.y) +
                        (pixel_src.z - pixel_mosaic.z) * (pixel_src.z - pixel_mosaic.z) * 0.17f;
                }

            }

            if(difference < smallest_difference)
            {
                smallest_difference = difference;
                closest_x = j;
                closest_y = i;
            }               
        }
    }

    myTexel.x = float(closest_x)/255.f;
    myTexel.y = float(closest_y)/255.f;
    myTexel.z = 0.f;
    myTexel.w = 0.f;    

    out_Color = myTexel;
}

ফলাফল

চিত্রগুলি প্রায় তাত্ক্ষণিকভাবে রেন্ডার হয়, সুতরাং সমান্তরালতা ছিল একটি সাফল্য। ক্ষয়ক্ষতিটি হ'ল আমি পৃথক টুকরো টুকরো টুকরো করে অন্য কোনও টুকরো আউটপুটে নির্ভর করতে পারি না, সুতরাং নির্দিষ্ট পরিসরের মধ্যে দু'বার একই টাইলটি বাছাই না করে আপনি যে উল্লেখযোগ্য গুণমানটি পেতে পারেন তা পাওয়ার কোনও উপায় নেই। সুতরাং, দ্রুত ফলাফল, তবে টাইলগুলির বিশাল পুনরাবৃত্তির কারণে সীমিত মানের। সব মিলিয়ে মজা লাগছিল। পূর্ণ আকারের সংস্করণগুলির জন্য http://imgur.com/a/M0Db0এখানে চিত্র বর্ণনা লিখুন


4

পাইথন

একটি গড় পদ্ধতির ব্যবহার করে এখানে প্রথম পাইথন সমাধানের ব্যবস্থা করা হয়েছে। আমরা এখান থেকে বিকাশ করতে পারি। বাকি চিত্র এখানে

রবিবার স্টিভ

from PIL import Image
import numpy as np

def calcmean(b):
    meansum = 0
    for k in range(len(b)):
        meansum = meansum + (k+1)*b[k]
    return meansum/sum(b)    

def gettiles(imageh,w,h):
    k = 0 
    tiles = {}
    for x in range(0,imageh.size[0],w):
        for y in range(0,imageh.size[1],h):
            a=imageh.crop((x, y, x + w, y + h))
            b=a.resize([1,1], Image.ANTIALIAS)
            tiles[k] = [a,x,y,calcmean(b.histogram()[0:256]) \
                             ,calcmean(b.histogram()[256:256*2]) \
                             ,calcmean(b.histogram()[256*2:256*3])]
            k = k + 1
    return tiles

w = 48 
h = 48

srch = Image.open('25745_avatars.png').convert('RGB')
files = ['21104_spheres.png', '45148_sunday.jpg', '47824_steve.jpg', '61555_kiss.jpg', '77388_lightbulb.png']
for f in range(len(files)):
    desh = Image.open(files[f]).convert('RGB')

    srctiles = gettiles(srch,w,h)
    destiles = gettiles(desh,w,h)

    #build proximity matrix 
    pm = np.zeros((len(destiles),len(srctiles)))
    for d in range(len(destiles)):
        for s in range(len(srctiles)):
            pm[d,s] = (srctiles[s][3]-destiles[d][3])**2 + \
                      (srctiles[s][4]-destiles[d][4])**2 + \
                      (srctiles[s][5]-destiles[d][5])**2

    for k in range(len(destiles)):
        j = list(pm[k,:]).index(min(pm[k,:]))
        desh.paste(srctiles[j][0], (destiles[k][1], destiles[k][2]))

    desh.save(files[f].replace('.','_m'+'.'))

1

তবুও আরেকটি পাইথন সমাধান - গড় ভিত্তিক (আরজিবি বনাম এল) বি *)

ফলাফল (কিছুটা পার্থক্য রয়েছে)

বাল্ব - আরজিবি

সম্পর্ণ খোল

bulb_rgb

বাল্ব - ল্যাব

সম্পর্ণ খোল

bulb_lab

স্টিভ - আরজিবি

সম্পর্ণ খোল

steve_rgb

স্টিভ - ল্যাব

সম্পর্ণ খোল

steve_lab

গোলক - আরজিবি

সম্পর্ণ খোল

spheres_rgb

গোলক - ল্যাব

সম্পর্ণ খোল

spheres_lab

রবিবার - আরজিবি

সম্পর্ণ খোল

sunday_rgb

রবিবার - ল্যাব

সম্পর্ণ খোল

sunday_lab

চুমু - আরজিবি

সম্পর্ণ খোল

kiss_rgb

চুম্বন - ল্যাব

সম্পর্ণ খোল

kiss_lab

কোড

ল্যাবের জন্য অজগর-বর্ণমথ প্রয়োজন

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image
from colormath.color_objects import LabColor,sRGBColor
from colormath.color_conversions import convert_color
from colormath.color_diff import delta_e_cie1976

def build_photomosaic_ils(mosaic_im,target_im,block_width,block_height,colordiff,new_filename):

    mosaic_width=mosaic_im.size[0]              #dimensions of the target image
    mosaic_height=mosaic_im.size[1]

    target_width=target_im.size[0]              #dimensions of the target image
    target_height=target_im.size[1]

    target_grid_width,target_grid_height=get_grid_dimensions(target_width,target_height,block_width,block_height)       #dimensions of the target grid
    mosaic_grid_width,mosaic_grid_height=get_grid_dimensions(mosaic_width,mosaic_height,block_width,block_height)       #dimensions of the mosaic grid

    target_nboxes=target_grid_width*target_grid_height
    mosaic_nboxes=mosaic_grid_width*mosaic_grid_height

    print "Computing the average color of each photo in the mosaic..."
    mosaic_color_averages=compute_block_avg(mosaic_im,block_width,block_height)
    print "Computing the average color of each block in the target photo ..."
    target_color_averages=compute_block_avg(target_im,block_width,block_height)

    print "Computing photomosaic ..."
    photomosaic=[0]*target_nboxes
    for n in xrange(target_nboxes):
        print "%.2f " % (n/float(target_nboxes)*100)+"%"
        for z in xrange(mosaic_nboxes):
            current_diff=colordiff(target_color_averages[n],mosaic_color_averages[photomosaic[n]])
            candidate_diff=colordiff(target_color_averages[n],mosaic_color_averages[z])

            if(candidate_diff<current_diff):
                photomosaic[n]=z

    print "Building final image ..."
    build_final_solution(photomosaic,mosaic_im,target_nboxes,target_im,target_grid_width,block_height,block_width,new_filename)

def build_initial_solution(target_nboxes,mosaic_nboxes):
    candidate=[0]*target_nboxes

    for n in xrange(target_nboxes):
        candidate[n]=random.randint(0,mosaic_nboxes-1)

    return candidate

def build_final_solution(best,mosaic_im,target_nboxes,target_im,target_grid_width,block_height,block_width,new_filename):

    for n in xrange(target_nboxes):

        i=(n%target_grid_width)*block_width             #i,j -> upper left point of the target image
        j=(n/target_grid_width)*block_height

        box = (i,j,i+block_width,j+block_height)        

        #get the best photo from the mosaic
        best_photo_im=get_block(mosaic_im,best[n],block_width,block_height)

        #paste the best photo found back into the image
        target_im.paste(best_photo_im,box)

    target_im.save(new_filename);


#get dimensions of the image grid
def get_grid_dimensions(im_width,im_height,block_width,block_height):
    grid_width=im_width/block_width     #dimensions of the target image grid
    grid_height=im_height/block_height
    return grid_width,grid_height

#compute the fitness of given candidate solution
def fitness(candidate,mosaic_color_averages,mosaic_nboxes,target_color_averages,target_nboxes):
    error=0.0
    for i in xrange(target_nboxes):
        error+=colordiff_rgb(mosaic_color_averages[candidate[i]],target_color_averages[i])
    return error

#get a list of color averages, i.e, the average color of each block in the given image
def compute_block_avg(im,block_height,block_width):

    width=im.size[0]
    height=im.size[1]

    grid_width_dim=width/block_width                    #dimension of the grid
    grid_height_dim=height/block_height

    nblocks=grid_width_dim*grid_height_dim              #number of blocks

    avg_colors=[]
    for i in xrange(nblocks):
        avg_colors+=[avg_color(get_block(im,i,block_width,block_height))]
    return avg_colors

#returns the average RGB color of a given image
def avg_color(im):
    avg_r=avg_g=avg_b=0.0
    pixels=im.getdata()
    size=len(pixels)
    for p in pixels:
        avg_r+=p[0]/float(size)
        avg_g+=p[1]/float(size)
        avg_b+=p[2]/float(size)

    return (avg_r,avg_g,avg_b)

#get the nth block of the image
def get_block(im,n,block_width,block_height):

    width=im.size[0]

    grid_width_dim=width/block_width                        #dimension of the grid

    i=(n%grid_width_dim)*block_width                        #i,j -> upper left point of the target block
    j=(n/grid_width_dim)*block_height

    box = (i,j,i+block_width,j+block_height)
    block_im = im.crop(box)
    return block_im


#calculate color difference of two pixels in the RGB space
#less is better
def colordiff_rgb(pixel1,pixel2):

    delta_red=pixel1[0]-pixel2[0]
    delta_green=pixel1[1]-pixel2[1]
    delta_blue=pixel1[2]-pixel2[2]

    fit=delta_red**2+delta_green**2+delta_blue**2
    return fit

#http://python-colormath.readthedocs.org/en/latest/index.html
#calculate color difference of two pixels in the L*ab space
#less is better
def colordiff_lab(pixel1,pixel2):

    #convert rgb values to L*ab values
    rgb_pixel_1=sRGBColor(pixel1[0],pixel1[1],pixel1[2],True)
    lab_1= convert_color(rgb_pixel_1, LabColor)

    rgb_pixel_2=sRGBColor(pixel2[0],pixel2[1],pixel2[2],True)
    lab_2= convert_color(rgb_pixel_2, LabColor)

    #calculate delta e
    delta_e = delta_e_cie1976(lab_1, lab_2)
    return delta_e


if __name__ == '__main__':
    mosaic="images/25745_avatars.png"
    targets=["images/lightbulb.png","images/steve.jpg","images/sunday.jpg","images/spheres.png","images/kiss.jpg"]
    target=targets[0]
    mosaic_im=Image.open(mosaic)
    target_im=Image.open(target)
    new_filename=target.split(".")[0]+"_photomosaic.png"
    colordiff=colordiff_rgb

    build_photomosaic_ils(mosaic_im,target_im,48,48,colordiff,new_filename)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.