Wymuś średnią na obrazie

20

Napisz program, który pobierze standardowy obraz truecolor i pojedynczy 24-bitowy kolor RGB (trzy liczby od 0 do 255). Zmodyfikuj obraz wejściowy (lub wydrukuj nowy obraz o takich samych wymiarach), aby jego średni kolor był dokładnie tym samym kolorem, który został wprowadzony. Możesz modyfikować piksele na obrazie wejściowym w dowolny sposób, aby to osiągnąć, ale celem jest, aby zmiany kolorów były jak najbardziej niezauważalne wizualnie .

Średni kolor z obrazu RGB jest naprawdę zestaw trzech średnie arytmetyczne , po jednym dla każdego kanału koloru. Średnia wartość czerwieni to suma wartości czerwieni we wszystkich pikselach na obrazie podzielona przez całkowitą liczbę pikseli (obszar obrazu), zaokrągloną w dół do najbliższej liczby całkowitej. Średnie zielone i niebieskie oblicza się w ten sam sposób.

Ten skrypt Python 2 (z PIL ) może obliczyć średni kolor większości formatów plików graficznych:

from PIL import Image
print 'Enter image file'
im = Image.open(raw_input()).convert('RGB')
pixels = im.load()
avg = [0, 0, 0]
for x in range(im.size[0]):
    for y in range(im.size[1]):
        for i in range(3):
            avg[i] += pixels[x, y][i]
print 'The average color is', tuple(c // (im.size[0] * im.size[1]) for c in avg)

(Istnieją podobne programy średni kolor tutaj , ale nie koniecznie zrobić dokładnie te same obliczenia.)

Głównym wymaganiem dla twojego programu jest to, że dla każdego obrazu wejściowego średni kolor odpowiadającego mu wyjścia musi dokładnie odpowiadać kolorowi, który został wprowadzony - zgodnie z oceną fragmentu kodu Python lub innego równoważnego kodu. Obraz wyjściowy musi również mieć dokładnie takie same wymiary jak obraz wejściowy.

Więc możesz technicznie przesłać program, który po prostu pokoloruje całe wejście określonym średnim kolorem (ponieważ średnia zawsze będzie tym kolorem), ale jest to konkurs popularności - wygrywa zgłoszenie z największą liczbą głosów , a takie banalne zgłoszenie nie zapewni Ci wielu pozytywnych opinii. Nowatorskie pomysły, takie jak wykorzystywanie dziwactw w ludzkim widzeniu lub zmniejszanie obrazu i rysowanie wokół niego kolorowej ramki, (mam nadzieję) przyniosą ci głosy.

Należy pamiętać, że niektóre kombinacje średnich kolorów i obrazów wymagają wyjątkowo zauważalnych zmian kolorów. Na przykład, jeśli przeciętnym pasującym kolorem był czarny (0, 0, 0), każdy obraz wejściowy musiałby być całkowicie czarny, ponieważ jeśli jakiekolwiek piksele miałyby wartości niezerowe, to również uczyniłyby średnią niezerową ( blokujące błędy zaokrąglania). Pamiętaj o takich ograniczeniach podczas głosowania.

Testuj obrazy

Niektóre obrazy i ich domyślne średnie kolory do zabawy. Kliknij, aby zobaczyć pełne rozmiary.

A. średnia (127, 127, 127)

Od fejesjoco „s Obrazy z wszystkich kolorach odpowiedzieć . Znaleziono oryginał na jego blogu .

B. średnia (62, 71, 73)

Jokohama . Dostarczone przez Geobits .

C. średnia (115, 112, 111)

Tokio . Dostarczone przez Geobits .

D. średnia (154, 151, 154)

Wodospad Eschera . Oryginalna .

E. średnia (105, 103, 102)

Góra Shasta . Dostarczone przeze mnie.

F. średnia (75, 91, 110)

Gwieździsta noc

Uwagi

  • Dokładne formaty wejściowe i wyjściowe oraz typy plików obrazów używanych przez program nie mają większego znaczenia. Tylko upewnij się, że jest jasne, jak korzystać z programu.
  • Prawdopodobnie dobrym pomysłem (ale nie wymaganiem technicznym) jest to, że jeśli obraz ma już średnią docelową barwę, powinien zostać wydrukowany w niezmienionej postaci.
  • Proszę zamieścić zdjęcia testowe ze średnią ilością kolorów jako (150, 100, 100) lub (75, 91, 110), aby wyborcy mogli zobaczyć te same dane wejściowe w różnych rozwiązaniach. (Publikowanie większej liczby przykładów jest w porządku, nawet zalecane).
Hobby Calvina
źródło
2
Czy uczestnicy mogą wybrać kolory wejściowe, których używają, aby zademonstrować skuteczność swojego rozwiązania? Czy to nie utrudnia ludziom porównywania rozwiązań? W skrajnym przypadku ktoś mógłby wybrać kolory wejściowe, które są bardzo podobne do średniej obrazu, i wyglądałoby na to, że ich rozwiązanie jest bardzo skuteczne.
Reto Koradi,
1
@ vihan1086 Jeśli dobrze zrozumiałem, średni kolor jest podawany jako 24-bitowy wejściowy kolor RGB, którego nie można znaleźć na obrazie wejściowym.
trichoplax
3
Interesujące może być użycie interpretacji @ vihan1086 i wykorzystanie przykładowych obrazów jako źródła kolorów wejściowych, aby jeden obraz był wyświetlany w średnim kolorze innego. W ten sposób można porównać różne odpowiedzi.
trichoplax
Głównym problemem jest to, że większość z nich ma średnią bardzo zbliżoną do szarości. Gwiaździsta noc jest prawdopodobnie najdalej od tego, ale reszta przeciętnie dość płasko.
Geobits
@RetoKoradi Mam nadzieję, że wyborcy będą wystarczająco inteligentni, aby brać pod uwagę takie rzeczy, chociaż dodałem notatkę o tym, jakie domyślne kolory należy stosować.
Calvin's Hobbies

Odpowiedzi:

11

Python 2 + PIL, proste skalowanie kolorów

from PIL import Image
import math

INFILE = "street.jpg"
OUTFILE = "output.png"
AVERAGE = (150, 100, 100)

im = Image.open(INFILE)
im = im.convert("RGB")
width, height = prev_size = im.size
pixels = {(x, y): list(im.getpixel((x, y)))
          for x in range(width) for y in range(height)}

def get_avg():
    total_rgb = [0, 0, 0]

    for x in range(width):
        for y in range(height):
            for i in range(3):
                total_rgb[i] += int(pixels[x, y][i])

    return [float(x)/(width*height) for x in total_rgb]

curr_avg = get_avg()

while tuple(int(x) for x in curr_avg) != AVERAGE:
    print curr_avg   
    non_capped = [0, 0, 0]
    total_rgb = [0, 0, 0]

    for x in range(width):
        for y in range(height):
            for i in range(3):
                if curr_avg[i] < AVERAGE[i] and pixels[x, y][i] < 255:
                    non_capped[i] += 1
                    total_rgb[i] += int(pixels[x, y][i])

                elif curr_avg[i] > AVERAGE[i] and pixels[x, y][i] > 0:
                    non_capped[i] += 1
                    total_rgb[i] += int(pixels[x, y][i])

    ratios = [1 if z == 0 else
              x/(y/float(z))
              for x,y,z in zip(AVERAGE, total_rgb, non_capped)]

    for x in range(width):
        for y in range(height):
            col = []

            for i in range(3):
                new_col = (pixels[x, y][i] + 0.01) * ratios[i]
                col.append(min(255, max(0, new_col)))

            pixels[x, y] = tuple(col)

    curr_avg = get_avg()

print curr_avg

for pixel in pixels:
    im.putpixel(pixel, tuple(int(x) for x in pixels[pixel]))

im.save(OUTFILE)

Oto naiwne podejście, które powinno służyć jako dobry punkt odniesienia. Przy każdej iteracji porównujemy naszą bieżącą średnią z pożądaną średnią i skalujemy RGB każdego piksela o odpowiedni współczynnik. Musimy jednak zachować ostrożność z dwóch powodów:

  • Skalowanie 0 nadal daje wynik 0, więc przed skalowaniem dodajemy coś małego (tutaj 0.01)

  • Wartości RGB mieszczą się w zakresie od 0 do 255, więc musimy odpowiednio dostosować współczynnik, aby zrekompensować fakt, że skalowanie pikseli z ograniczeniem nic nie robi.

Obrazy zapisywane są jako PNG, ponieważ zapisywanie jako JPG wydaje się popsuć średnie kolorów.

Próbka wyjściowa

(40, 40, 40)

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

(150, 100, 100)

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

(75, 91, 110), paleta Starry Night

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Sp3000
źródło
2
Zdecydowanie chcesz użyć do tego formatu obrazu z kompresją bezstratną. JPEG nie jest więc dobrą opcją.
Reto Koradi,
Zawsze możesz liczyć na Sp.
Alex A.,
6

C ++, korekcja gamma

Dokonuje regulacji jasności obrazu za pomocą prostej korekcji gamma, przy czym wartość gamma jest określana osobno dla każdego komponentu w celu dopasowania do średniej docelowej.

Najważniejsze kroki to:

  1. Przeczytaj obraz i wyodrębnij histogram dla każdego składnika koloru.
  2. Wykonaj binarne wyszukiwanie wartości gamma dla każdego elementu. Wyszukiwanie binarne jest przeprowadzane na wartościach gamma, dopóki wynikowy histogram nie uzyska pożądanej średniej.
  3. Przeczytaj obraz po raz drugi i zastosuj korekcję gamma.

Wszystkie wejścia / wyjścia obrazów używają plików PPM w ASCII. Obrazy zostały przekonwertowane z / do PNG za pomocą GIMP. Kod został uruchomiony na komputerze Mac, konwersje obrazu przeprowadzono w systemie Windows.

Kod:

#include <cmath>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <iostream>

static inline int mapVal(int val, float gamma)
{
    float relVal = (val + 1.0f) / 257.0f;
    float newRelVal = powf(relVal, gamma);

    int newVal = static_cast<int>(newRelVal * 257.0f - 0.5f);
    if (newVal < 0)
    {
        newVal = 0;
    }
    else if (newVal > 255)
    {
        newVal = 255;
    }

    return newVal;
}

struct Histogram
{
    Histogram();

    bool read(const std::string fileName);
    int getAvg(int colIdx) const;
    void adjust(const Histogram& origHist, int colIdx, float gamma);

    int pixCount;
    std::vector<int> freqA[3];
};

Histogram::Histogram()
  : pixCount(0)
{
    for (int iCol = 0; iCol < 3; ++iCol)
    {
        freqA[iCol].resize(256, 0);
    }
}

bool Histogram::read(const std::string fileName)
{
    for (int iCol = 0; iCol < 3; ++iCol)
    {
        freqA[iCol].assign(256, 0);
    }

    std::ifstream inStrm(fileName);

    std::string format;
    inStrm >> format;
    if (format != "P3")
    {
        std::cerr << "invalid PPM header" << std::endl;
        return false;
    }

    int w = 0, h = 0;
    inStrm >> w >> h;
    if (w <= 0 || h <= 0)
    {
        std::cerr << "invalid size" << std::endl;
        return false;
    }

    int maxVal = 0;
    inStrm >> maxVal;
    if (maxVal != 255)
    {
        std::cerr << "invalid max value (255 expected)" << std::endl;
        return false;
    }

    pixCount = w * h;

    int sumR = 0, sumG = 0, sumB = 0;
    for (int iPix = 0; iPix < pixCount; ++iPix)
    {
        int r = 0, g = 0, b = 0;
        inStrm >> r >> g >> b;
        ++freqA[0][r];
        ++freqA[1][g];
        ++freqA[2][b];
    }

    return true;
}

int Histogram::getAvg(int colIdx) const
{
    int avg = 0;
    for (int val = 0; val < 256; ++val)
    {
        avg += freqA[colIdx][val] * val;
    }

    return avg / pixCount;
}

void Histogram::adjust(const Histogram& origHist, int colIdx, float gamma)
{
    freqA[colIdx].assign(256, 0);

    for (int val = 0; val < 256; ++val)
    {
        int newVal = mapVal(val, gamma);
        freqA[colIdx][newVal] += origHist.freqA[colIdx][val];
    }
}

void mapImage(const std::string fileName, float gammaA[])
{
    std::ifstream inStrm(fileName);

    std::string format;
    inStrm >> format;

    int w = 0, h = 0;
    inStrm >> w >> h;

    int maxVal = 0;
    inStrm >> maxVal;

    std::cout << "P3" << std::endl;
    std::cout << w << " " << h << std::endl;
    std::cout << "255" << std::endl;

    int nPix = w * h;

    for (int iPix = 0; iPix < nPix; ++iPix)
    {
        int inRgb[3] = {0};
        inStrm >> inRgb[0] >> inRgb[1] >> inRgb[2];

        int outRgb[3] = {0};
        for (int iCol = 0; iCol < 3; ++iCol)
        {
            outRgb[iCol] = mapVal(inRgb[iCol], gammaA[iCol]);
        }

        std::cout << outRgb[0] << " " << outRgb[1] << " "
                  << outRgb[2] << std::endl;
    }
}

int main(int argc, char* argv[])
{
    if (argc < 5)
    {
        std::cerr << "usage: " << argv[0]
                  << " ppmFileName targetR targetG targetB"
                  << std::endl;
        return 1;
    }

    std::string inFileName = argv[1];

    int targAvg[3] = {0};
    std::istringstream strmR(argv[2]);
    strmR >> targAvg[0];
    std::istringstream strmG(argv[3]);
    strmG >> targAvg[1];
    std::istringstream strmB(argv[4]);
    strmB >> targAvg[2];

    Histogram origHist;
    if (!origHist.read(inFileName))
    {
        return 1;
    }

    Histogram newHist(origHist);
    float gammaA[3] = {0.0f};

    for (int iCol = 0; iCol < 3; ++iCol)
    {
        float minGamma = 0.0f;
        float maxGamma = 1.0f;
        for (;;)
        {
            newHist.adjust(origHist, iCol, maxGamma);
            int avg = newHist.getAvg(iCol);
            if (avg <= targAvg[iCol])
            {
                break;
            }
            maxGamma *= 2.0f;
        }

        for (;;)
        {
            float midGamma = 0.5f * (minGamma + maxGamma);

            newHist.adjust(origHist, iCol, midGamma);
            int avg = newHist.getAvg(iCol);
            if (avg < targAvg[iCol])
            {
                maxGamma = midGamma;
            }
            else if (avg > targAvg[iCol])
            {
                minGamma = midGamma;
            }
            else
            {
                gammaA[iCol] = midGamma;
                break;
            }
        }
    }

    mapImage(inFileName, gammaA);

    return 0;
}

Sam kod jest dość prosty. Jednym subtelnym, ale ważnym szczegółem jest to, że chociaż wartości kolorów mieszczą się w zakresie [0, 255], odwzorowuję je na krzywą gamma tak, jakby zakres wynosił [-1, 256]. Pozwala to na wymuszenie średniej na 0 lub 255. W przeciwnym razie 0 zawsze pozostanie 0, a 255 zawsze pozostanie 255, co może nigdy nie pozwolić na średnią 0/255.

Używać:

  1. Zapisz kod w pliku z rozszerzeniem .cpp, np force.cpp.
  2. Kompiluj z c++ -o force -O2 force.cpp.
  3. Uruchom z ./force input.ppm targetR targetG target >output.ppm.

Wyjściowa próbka dla 40, 40, 40

Należy pamiętać, że obrazy wszystkich większych próbek są dołączone jako pliki JPEG, ponieważ przekraczają limit wielkości SE jako pliki PNG. Ponieważ JPEG jest formatem kompresji stratnej, mogą nie odpowiadać dokładnie docelowej średniej. Mam wersję wszystkich plików PNG, która dokładnie pasuje.

Af1 Bf1 Por.1 Df1 Ef1 Ff1

Przykładowe dane wyjściowe dla 150, 100, 100:

Af2 Bf2 Cf2 Df2 Ef2 Ff2

Przykładowe dane wyjściowe dla 75, 91, 110:

Af3 Bf3 Cf3 Df3 Ef3 Ff3

Reto Koradi
źródło
Musiałem zmniejszyć inne obrazy, aby osiągnąć limit - może spróbuj?
Sp3000,
@ Sp3000 Ok, teraz wszystkie obrazy są uwzględnione. Teraz także z miniaturami. Skończyło się na użyciu wersji JPEG dla dużych. W rzeczywistości jeden z nich był poniżej limitu rozmiaru, ale wygląda na to, że został automatycznie przekonwertowany na JPEG. Pierwszym i ostatnim przykładem są nadal PNG.
Reto Koradi
2

Python 2 + PIL

from PIL import Image
import random
import math

SOURCE = 'input.png'
OUTPUT = 'output.png'
AVERAGE = [150, 100, 100]

im = Image.open(SOURCE).convert('RGB')
pixels = im.load()
w = im.size[0]
h = im.size[1]
npixels = w * h

maxdiff = 0.1

# for consistent results...
random.seed(42)
order = range(npixels)
random.shuffle(order)

def calc_sum(pixels, w, h):
    sums = [0, 0, 0]
    for x in range(w):
        for y in range(h):
            for i in range(3):
                sums[i] += pixels[x, y][i]
    return sums

def get_coordinates(index, w):
    return tuple([index % w, index // w])

desired_sums = [AVERAGE[0] * npixels, AVERAGE[1] * npixels, AVERAGE[2] * npixels]

sums = calc_sum(pixels, w, h)
for i in range(3):
    while sums[i] != desired_sums[i]:
        for j in range(npixels):
            if sums[i] == desired_sums[i]:
                break
            elif sums[i] < desired_sums[i]:
                coord = get_coordinates(order[j], w)
                pixel = list(pixels[coord])
                delta = int(maxdiff * (255 - pixel[i]))
                if delta == 0 and pixel[i] != 255:
                    delta = 1
                delta = min(delta, desired_sums[i] - sums[i])

                sums[i] += delta
                pixel[i] += delta
                pixels[coord] = tuple(pixel)
            else:
                coord = get_coordinates(order[j], w)
                pixel = list(pixels[coord])
                delta = int(maxdiff * pixel[i])
                if delta == 0 and pixel[i] != 0:
                    delta = 1
                delta = min(delta, sums[i] - desired_sums[i])

                sums[i] -= delta
                pixel[i] -= delta
                pixels[coord] = tuple(pixel)

# output image
for x in range(w):
    for y in range(h):
        im.putpixel(tuple([x, y]), pixels[tuple([x, y])])

im.save(OUTPUT)

Powoduje to iterację każdego piksela w losowej kolejności i zmniejsza odległość między każdym składnikiem koloru piksela i 255lub 0(w zależności od tego, czy bieżąca średnia jest mniejsza czy większa od pożądanej średniej). Odległość jest zmniejszana o stały współczynnik mnożenia. Jest to powtarzane do momentu uzyskania pożądanej średniej. Redukcja jest zawsze co najmniej 1, chyba że kolor ma 255(lub 0), aby zapewnić, że przetwarzanie nie zostanie zatrzymane, gdy piksel będzie zbliżony do białego lub czarnego.

Próbka wyjściowa

(40, 40, 40)

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

(150, 100, 100)

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

(75, 91, 110)

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

es1024
źródło
1

Jawa

Podejście oparte na RNG. Trochę wolno dla dużych obrazów wejściowych.

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.*;

import javax.imageio.ImageIO;


public class Averager {
    static Random r;
    static long sigmaR=0,sigmaG=0,sigmaB=0;
    static int w,h;
    static int rbar,gbar,bbar;
    static BufferedImage i;
    private static File file;
    static void upRed(){
        int x=r.nextInt(w);
        int y=r.nextInt(h);
        Color c=new Color(i.getRGB(x, y));
        if(c.getRed()==255)return;
        sigmaR++;
        c=new Color(c.getRed()+1,c.getGreen(),c.getBlue());
        i.setRGB(x, y,c.getRGB());
    }
    static void downRed(){
        int x=r.nextInt(w);
        int y=r.nextInt(h);
        Color c=new Color(i.getRGB(x, y));
        if(c.getRed()==0)return;
        sigmaR--;
        c=new Color(c.getRed()-1,c.getGreen(),c.getBlue());
        i.setRGB(x, y,c.getRGB());
    }
    static void upGreen(){
        int x=r.nextInt(w);
        int y=r.nextInt(h);
        Color c=new Color(i.getRGB(x, y));
        if(c.getGreen()==255)return;
        sigmaG++;
        c=new Color(c.getRed(),c.getGreen()+1,c.getBlue());
        i.setRGB(x, y,c.getRGB());
    }
    static void downGreen(){
        int x=r.nextInt(w);
        int y=r.nextInt(h);
        Color c=new Color(i.getRGB(x, y));
        if(c.getGreen()==0)return;
        sigmaG--;
        c=new Color(c.getRed(),c.getGreen()-1,c.getBlue());
        i.setRGB(x,y,c.getRGB());
    }
    static void upBlue(){
        int x=r.nextInt(w);
        int y=r.nextInt(h);
        Color c=new Color(i.getRGB(x, y));
        if(c.getBlue()==255)return;
        sigmaB++;
        c=new Color(c.getRed(),c.getGreen(),c.getBlue()+1);
        i.setRGB(x, y,c.getRGB());
    }
    static void downBlue(){
        int x=r.nextInt(w);
        int y=r.nextInt(h);
        Color c=new Color(i.getRGB(x, y));
        if(c.getBlue()==0)return;
        sigmaB--;
        c=new Color(c.getRed(),c.getGreen(),c.getBlue()-1);
        i.setRGB(x,y,c.getRGB());
    }
    public static void main(String[]a) throws Exception{
        Scanner in=new Scanner(System.in);
        i=ImageIO.read(file=new File(in.nextLine()));
        rbar=in.nextInt();
        gbar=in.nextInt();
        bbar=in.nextInt();
        w=i.getWidth();
        h=i.getHeight();
        final int npix=w*h;
        r=new Random(npix*(long)i.hashCode());
        for(int x=0;x<w;x++){
            for(int y=0;y<h;y++){
                Color c=new Color(i.getRGB(x, y));
                sigmaR+=c.getRed();
                sigmaG+=c.getGreen();
                sigmaB+=c.getBlue();
            }
        }
        while(sigmaR/npix<rbar){
            upRed();
        }
        while(sigmaR/npix>rbar){
            downRed();
        }
        while(sigmaG/npix<gbar){
            upGreen();
        }
        while(sigmaG/npix>gbar){
            downGreen();
        }
        while(sigmaB/npix<bbar){
            upBlue();
        }
        while(sigmaB/npix>bbar){
            downBlue();
        }
        String k=file.getName().split("\\.")[0];
        ImageIO.write(i,"png",new File(k="out_"+k+".png"));
    }
}

Testy:

(40,40, 40)

wprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutaj

(150,100,100)

wprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutaj

(75 91,110)

wprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutajwprowadź opis zdjęcia tutaj

SuperJedi224
źródło