Minyatür Faking


26

Herhangi bir amatör fotoğrafçının size söyleyebileceği gibi, aşırı post-işlem her zaman iyidir. Böyle bir teknik " minyatür-sahte " olarak adlandırılır .

Amaç, bir görüntünün minyatür, oyuncak versiyonunun bir fotoğrafı gibi görünmesini sağlamaktır. Bu, nesnenin yüksekliğinde düşük bir değişkenlikle, orta / yüksek bir açıdan zemine çekilen fotoğraflar için en iyi şekilde çalışır, ancak diğer görüntülere değişken etkinlikle uygulanabilir.

Zorluk: Bir fotoğraf çekin ve minyatür faktoring algoritması uygulayın. Bunu yapmanın pek çok yolu var, ancak bu zorluğun amaçları doğrultusunda, aşağı doğru kayıyor:

  • Seçici bulanıklık

    Sığ alan derinliğini simüle etmek için görüntünün bir kısmı bulanıklaştırılmalıdır. Bu genellikle doğrusal veya şekilli olsun, bazı meyveler boyunca yapılır. İstediğiniz bulanıklık / gradyan algoritmasını seçin, ancak görüntünün% 15-85 arasında "fark edilebilir" bulanıklığa sahip olması gerekir.

  • Doygunluk artışı

    Şeylerin elle boyanmış görünmesini sağlamak için rengi pompalayın. Çıktı, girdiye kıyasla ortalama>% 5 doygunluk seviyesine sahip olmalıdır. ( HSV doygunluğu kullanarak )

  • Kontrast Artırma

    Daha sert aydınlatma koşullarını simüle etmek için kontrastı artırın (örneğin güneş yerine iç mekan / stüdyo ışığıyla gördüğünüz gibi). Çıktı, girdiye kıyasla> +% 5 kontrast değerine sahip olmalıdır. ( RMS algoritmasını kullanarak )

Bu üç değişiklik yapılmalı ve başka iyileştirmelere / değişikliklere izin verilmez. Kırpma, keskinleştirme, beyaz dengesi ayarı yok, hiçbir şey yok.

  • Giriş bir görüntüdür ve bir dosyadan veya bellekten okunabilir. Harici kütüphaneleri kullanabilirsiniz.Okumak ve yazmak görüntü, ancak edemez için kullanabilirsiniz işlemek görüntü. Sağlanan işlevlere bu amaç için izin verilmez ( Image.blur()örneğin yalnızca arayamazsınız ).

  • Başka giriş yok. İşlem gücü, seviyeleri vb. Bir insan tarafından değil program tarafından belirlenmelidir.

  • Çıktı, standart bir görüntü formatında (PNG, BMP, vb.) Görüntülenebilir veya bir dosya olarak kaydedilebilir.

  • Genelleştirmeye çalış. Sadece üzerinde çalışmamalı bir tanesinde görüntü , ancak tüm görüntülerde çalışmadığı anlaşılabilir . Bazı sahneler algoritma ne kadar iyi olursa olsun, bu tekniğe iyi cevap vermez. Cevap verirken hem cevap verirken hem de oy verirken burada sağduyu uygulayın.

  • Davranış, geçersiz girdiler ve özelliği yerine getirmek imkansız olan görüntüler için tanımsızdır. Örneğin, gri tonlamalı bir görüntü doygun hale getirilemez (temel renk tonu yoktur), saf beyaz görüntülenenler kontrastı arttırmaz vb.

  • Cevabınıza en az iki çıktı görüntüsü ekleyin:

    Bu dropbox klasöründeki resimlerden birinin yaratılması gerekiyor . Seçilebilecek altı tane var, ama hepsini değişik derecelerde uygulanabilir hale getirmeye çalıştım. Her biri için örnek çıktıları example-outputsalt klasörde görebilirsiniz. Lütfen bunların fotoğraf makinesinden çıkan 10MP JPG'lik tam görüntüleri olduğunu ve üzerinde çalışılacak çok sayıda pikselin bulunduğunu unutmayın.

    Diğeri seçtiğiniz herhangi bir görüntü olabilir. Açıkçası, özgürce kullanılabilir görüntüleri seçmeye çalışın. Ayrıca, orijinal görüntü veya karşılaştırma için bir link ekleyin.


Örneğin, bu görüntüden:

orijinal

Gibi bir şey çıktı olabilir:

işlenmiş

Başvuru için, yukarıdaki örnek GIMP'de açısal kutu şeklinde bir gradyan gauss bulanıklığı, doygunluk +80, kontrast +20 ile işlendi. (GIMP'nin bunlar için hangi birimleri kullandığını bilmiyorum)

Daha fazla ilham almak ya da ne elde etmeye çalıştığınızla ilgili daha iyi bir fikir edinmek için, bu siteyi ya da bunu kontrol edin . Ayrıca örnekleri arayabilir miniature fakingve arayabilirsiniz tilt shift photography.


Bu bir popülerlik yarışması. Seçmenler, hedefe sadık kalırken en iyi göründüğünü düşündüğünüz girişlere oy veriyor.


Açıklama:

Hangi fonksiyonlara izin verilmediğinin açıklığa kavuşturulması, matematik fonksiyonlarını yasaklamak amacım değildi . İmaj manipülasyon fonksiyonlarını yasaklamak niyetindeydim . Evet, orada bazı örtüşme var, ancak FFT, konvolüsyonlar, matris matematiği, vb. Gibi şeyler birçok başka alanda yararlı. Sen gerektiğini değil , sadece bir görüntü ve bulanıklığa alır bir işlevi kullanıyor. Bir bulanıklık oluşturmak için uygun bir matematik yolu bulursanız , bu adil oyun.


Bu olağanüstü gösteri demonstrasyonlar.wolfram.com/DiitalTiltShiftFotoğrafçılık , Dijital Eğme-Geçişli Görüntü İşleme, Yu-Sung Chang tarafından, kontrast, parlaklık ve yerel odağın (fotoğrafın oval veya dikdörtgen bir bölgesi içinde) nasıl ayarlanacağı hakkında birçok fikir aktarıyor. ) kullanılarak yerleşik Mathematica'nın fonksiyonları ( GeometricTransformation, DistanceTransform, ImageAdd, ColorNegate, ImageMultiply, Rasterize, ve ImageAdjust.) Hatta bu tür üst düzey görüntü işleme fonksiyonları yardımıyla, kod 22 k kaplıyor. Kullanıcı arayüzü için kod yine de çok küçük.
DavidC

5
" Sadece 22 k alır" demeliydim . Yukarıda belirtilen fonksiyonlarda kapsanan perde arkası kodu çok fazladır, bu zorluğa başarılı bir şekilde verilen cevap, çoğu dilde özel görüntü işleme kitaplıkları kullanmadan elde edilmesi çok, çok zor olduğunu kanıtlamalıdır.
DavidC

Güncelleme: 2,5 k karakterde yapıldı, böylece daha da verimli oldu.
DavidC

1
@DavidCarraher Bu yüzden açıkça özellikleri sınırlandırdım. Aşağıdaki spesifikasyona göre 4.3 k karakter ungolfed Java ile gösterildiğinden, sadece teknik özellikleri kapsayacak bir şeyler yazmak zor değil . Profesyonel stüdyo düzeyinde sonuçlar beklemiyorum. Elbette, spesifikasyonu aşan her şey (daha iyi sonuçlara yol açan) IMO'nun yürekten kaldırılması gerekir. Bunun olmadığını kabul edersiniz basit için meydan excel de, ama olması gerekiyordu değildi. Asgari çaba temeldir, ancak “iyi” girişler mutlaka daha fazla ilgili olacaktır.
Geobits

Daha ikna edici "minyatürler" üretmek için bunlarla birleştirilebilecek bir başka algoritma, daha büyük özellikleri keskin tutarken görüntüdeki küçük özellikleri filtrelemek için dalgacık ayrıştırma kullanmaktır.
AJMansfield

Yanıtlar:


15

Java: Referans Uygulaması

İşte Java'da temel bir referans uygulaması. Yüksek açılı çekimlerde en iyi şekilde çalışır ve korkunç derecede verimsizdir.

Bulanıklık, çok basit bir kutu bulanıklığıdır, bu yüzden aynı piksellerin üzerinde gereğinden çok daha fazla döngü oluşturur. Kontrast ve doygunluk olabilir tek bir ilmek halinde birleştirilebilir, ancak harcanan zamanın büyük bir kısmı bulanıklaştığından, bundan çok fazla kazanç elde edemezdi. Olduğu söyleniyor, 2MP altındaki görüntülerde oldukça hızlı çalışıyor. 10MP resmin tamamlanması biraz zaman aldı.

Bulanıklık kalitesi, düz bir kutu bulanıklığı dışında, temel olarak her şey kullanılarak kolayca geliştirilebilir . Kontrast / doygunluk algoritmaları işlerini yapar, bu yüzden orada gerçek bir şikayet yoktur.

Programda gerçek bir zeka yok. Bulanıklık, doygunluk ve kontrast için sabit faktörleri kullanır. Mutlu orta ayarları bulmak için onunla oynadım. Sonuç olarak, orada olan çok iyi yapmaz bazı sahneleri. Örneğin, kontrast / doygunluğu o kadar fazla pompalar ki, benzer renkli alanların (düşünme gökyüzü) olduğu görüntüler renkli bantlara bölünür.

Kullanımı basit; sadece dosya ismini sadece argüman olarak yazınız. Giriş dosyasının ne olduğuna bakılmaksızın PNG'de çıktı verir.

Örnekler:

Dropbox seçiminden:

Bu ilk görüntüler gönderim kolaylığı için küçültülür. Tam boyutta görmek için resme tıklayın.

Sonra:

görüntü tanımını buraya girin

Önce:

görüntü tanımını buraya girin

Çeşitli seçim:

Sonra:

görüntü tanımını buraya girin

Önce:

görüntü tanımını buraya girin

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

import javax.imageio.ImageIO;

public class MiniFake {

    int maxBlur;
    int maxDist;
    int width;
    int height;

    public static void main(String[] args) {
        if(args.length < 1) return;
        new MiniFake().run(args[0]);
    }

    void run(String filename){
        try{
            BufferedImage in = readImage(filename);
            BufferedImage out = blur(in);
            out = saturate(out, 0.8);
            out = contrast(out, 0.6);

            String[] tokens = filename.split("\\.");
            String outname = tokens[0];
            for(int i=1;i<tokens.length-1;i++)
                outname += "." + tokens[i];
            ImageIO.write(out, "png", new File(outname + "_post.png"));
            System.out.println("done");
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    BufferedImage contrast(BufferedImage in, double level){
        BufferedImage out = copyImage(in);
        long lumens=0;
        for(int x=0;x<width;x++)
            for(int y=0;y<height;y++){
                int color = out.getRGB(x,y);
                lumens += lumen(getR(color), getG(color), getB(color));
            }
        lumens /= (width * height);

        for(int x=0;x<width;x++)
            for(int y=0;y<height;y++){
                int color = out.getRGB(x,y);
                int r = getR(color);
                int g = getG(color);
                int b = getB(color);
                double ratio = ((double)lumen(r, g, b) / (double)lumens) - 1d;
                ratio *= (1+level) * 0.1;
                r += (int)(getR(color) * ratio+1);
                g += (int)(getG(color) * ratio+1);
                b += (int)(getB(color) * ratio+1);
                out.setRGB(x,y,getColor(clamp(r),clamp(g),clamp(b)));
            }   
        return out;
    }

    BufferedImage saturate(BufferedImage in, double level){
        BufferedImage out = copyImage(in);
        for(int x=0;x<width;x++)
            for(int y=0;y<height;y++){
                int color = out.getRGB(x,y);
                int r = getR(color);
                int g = getG(color);
                int b = getB(color);
                int brightness = Math.max(r, Math.max(g, b));
                int grey = (int)(Math.min(r, Math.min(g,b)) * level);
                if(brightness == grey)
                    continue;
                r -= grey;
                g -= grey;
                b -= grey;
                double ratio = brightness / (double)(brightness - grey);
                r = (int)(r * ratio);
                g = (int)(g * ratio);
                b = (int)(b * ratio);
                out.setRGB(x, y, getColor(clamp(r),clamp(g),clamp(b)));
            }
        return out;
    }


    BufferedImage blur(BufferedImage in){
        BufferedImage out = copyImage(in);
        int[] rgb = in.getRGB(0, 0, width, height, null, 0, width);
        for(int i=0;i<rgb.length;i++){
            double dist = Math.abs(getY(i)-(height/2));
            dist = dist * dist / maxDist;
            int r=0,g=0,b=0,p=0;
            for(int x=-maxBlur;x<=maxBlur;x++)
                for(int y=-maxBlur;y<=maxBlur;y++){
                    int xx = getX(i) + x;
                    int yy = getY(i) + y;
                    if(xx<0||xx>=width||yy<0||yy>=height)
                        continue;
                    int color = rgb[getPos(xx,yy)];
                    r += getR(color);
                    g += getG(color);
                    b += getB(color);
                    p++;
                }

            if(p>0){
                r /= p;
                g /= p;
                b /= p;
                int color = rgb[i];
                r = (int)((r*dist) + (getR(color) * (1 - dist)));
                g = (int)((g*dist) + (getG(color) * (1 - dist)));
                b = (int)((b*dist) + (getB(color) * (1 - dist)));
            } else {
                r = in.getRGB(getX(i), getY(i));
            }
            out.setRGB(getX(i), getY(i), getColor(r,g,b));
        }
        return out;
    }

    BufferedImage readImage(String filename) throws IOException{
         BufferedImage image = ImageIO.read(new File(filename));
         width = image.getWidth();
         height = image.getHeight();
         maxBlur = Math.max(width, height) / 100;
         maxDist =  (height/2)*(height/2);
         return image;
    }

    public BufferedImage copyImage(BufferedImage in){
        BufferedImage out = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics g = out.getGraphics();
        g.drawImage(in, 0, 0, null);
        g.dispose();
        return out;
    }

    static int clamp(int c){return c<0?0:c>255?255:c;}
    static int getColor(int a, int r, int g, int b){return (a << 24) | (r << 16) | (g << 8) | (b);}
    static int getColor(int r, int g, int b){return getColor(0xFF, r, g, b);}
    static int getR(int color){return color >> 16 & 0xFF;}
    static int getG(int color){return color >> 8 & 0xFF;}
    static int getB(int color){return color & 0xFF;}
    static int lumen(int r, int g, int b){return (r*299)+(g*587)+(b*114);}
    int getX(int pos){return pos % width;}
    int getY(int pos){return pos / width;}
    int getPos(int x, int y){return y*width+x;} 
}

12

C #

Herhangi bir yinelemeli kutu bulanıklığı yapmak yerine, tüm yolu kullanmaya ve Gauss bulanıklığı yazmaya karar verdim. GetPixelBüyük çekirdekleri kullanılırken çağrılar gerçekten yavaşlatmak, ancak kullanım yöntemleri dönüştürmek için gerçekten değerli değil LockBitsbazı büyük resimler işlerken sürece.

Ayarladığım varsayılan ayar parametrelerini kullanan bazı örnekler aşağıdadır (Ayarlama parametreleriyle fazla oynamadım, çünkü test görüntüsü için iyi çalışıyorlardı).

Sunulan test durumu için ...

1-Orijinal 1-Değiştirilmiş

Bir diğeri...

2-Orijinal 2-Değiştirilmiş

Bir diğeri...

3-Orijinal 3-Modifiye

Doygunluk ve kontrast artışları koddan oldukça açık olmalıdır. Bunu HSL alanında yapıyorum ve tekrar RGB'ye dönüştürüyorum.

2B Gauss çekirdek boyutuna göre üretilir nile belirtilen:

EXP(-((x-x0)^2/2+(y-y0)^2/2)/2)

... ve tüm çekirdek değerleri atandıktan sonra normalleştirilir. Bunu not al A=sigma_x=sigma_y=1.

Çekirdeğin nereye uygulanacağını bulmak için hesaplanan bulanıklık ağırlığını kullanıyorum:

SQRT([COS(PI*x_norm)^2 + COS(PI*y_norm)^2]/2)

... iyi bir yanıt verir, esas olarak, yavaş yavaş solup giden bulanıklıktan korunan bir değerlerin elipsini oluşturur. Diğer denklemlerle birleştirilen bir bant geçiren filtre (belki de bazı değişkenler y=-x^2), bazı görüntüler için potansiyel olarak daha iyi çalışabilir. Kosinüs ile gittim çünkü test ettiğim temel durum için iyi bir cevap verdi.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace FakeMini
{
    static class Program
    {
        static void Main()
        {
            // Some tuning variables
            double saturationValue = 1.7;
            double contrastValue = 1.2;
            int gaussianSize = 13; // Must be odd and >1 (3, 5, 7...)

            // NxN Gaussian kernel
            int padding = gaussianSize / 2;
            double[,] kernel = GenerateGaussianKernel(gaussianSize);

            Bitmap src = null;
            using (var img = new Bitmap(File.OpenRead("in.jpg")))
            {
                src = new Bitmap(img);
            }

            // Bordering could be avoided by reflecting or wrapping instead
            // Also takes advantage of the fact that a new bitmap returns zeros from GetPixel
            Bitmap border = new Bitmap(src.Width + padding*2, src.Height + padding*2);

            // Get average intensity of entire image
            double intensity = 0;
            for (int x = 0; x < src.Width; x++)
            {
                for (int y = 0; y < src.Height; y++)
                {
                    intensity += src.GetPixel(x, y).GetBrightness();
                }
            }
            double averageIntensity = intensity / (src.Width * src.Height);

            // Modify saturation and contrast
            double brightness;
            double saturation;
            for (int x = 0; x < src.Width; x++)
            {
                for (int y = 0; y < src.Height; y++)
                {
                    Color oldPx = src.GetPixel(x, y);
                    brightness = oldPx.GetBrightness();
                    saturation = oldPx.GetSaturation() * saturationValue;

                    Color newPx = FromHSL(
                                oldPx.GetHue(),
                                Clamp(saturation, 0.0, 1.0),
                                Clamp(averageIntensity - (averageIntensity - brightness) * contrastValue, 0.0, 1.0));
                    src.SetPixel(x, y, newPx);
                    border.SetPixel(x+padding, y+padding, newPx);
                }
            }

            // Apply gaussian blur, weighted by corresponding sine value based on height
            double blurWeight;
            Color oldColor;
            Color newColor;
            for (int x = padding; x < src.Width+padding; x++)
            {
                for (int y = padding; y < src.Height+padding; y++)
                {
                    oldColor = border.GetPixel(x, y);
                    newColor = Convolve2D(
                        kernel,
                        GetNeighbours(border, gaussianSize, x, y)
                       );

                    // sqrt([cos(pi*x_norm)^2 + cos(pi*y_norm)^2]/2) gives a decent response
                    blurWeight = Clamp(Math.Sqrt(
                        Math.Pow(Math.Cos(Math.PI * (y - padding) / src.Height), 2) +
                        Math.Pow(Math.Cos(Math.PI * (x - padding) / src.Width), 2)/2.0), 0.0, 1.0);
                    src.SetPixel(
                        x - padding,
                        y - padding,
                        Color.FromArgb(
                            Convert.ToInt32(Math.Round(oldColor.R * (1 - blurWeight) + newColor.R * blurWeight)),
                            Convert.ToInt32(Math.Round(oldColor.G * (1 - blurWeight) + newColor.G * blurWeight)),
                            Convert.ToInt32(Math.Round(oldColor.B * (1 - blurWeight) + newColor.B * blurWeight))
                            )
                        );
                }
            }
            border.Dispose();

            // Configure some save parameters
            EncoderParameters ep = new EncoderParameters(3);
            ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            ep.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, (int)EncoderValue.ScanMethodInterlaced);
            ep.Param[2] = new EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, (int)EncoderValue.RenderProgressive);
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo ici = null;
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.MimeType == "image/jpeg")
                    ici = codec;
            }
            src.Save("out.jpg", ici, ep);
            src.Dispose();
        }

        // Create RGB from HSL
        // (C# BCL allows me to go one way but not the other...)
        private static Color FromHSL(double h, double s, double l)
        {
            int h0 = Convert.ToInt32(Math.Floor(h / 60.0));
            double c = (1.0 - Math.Abs(2.0 * l - 1.0)) * s;
            double x = (1.0 - Math.Abs((h / 60.0) % 2.0 - 1.0)) * c;
            double m = l - c / 2.0;
            int m0 = Convert.ToInt32(255 * m);
            int c0 = Convert.ToInt32(255*(c + m));
            int x0 = Convert.ToInt32(255*(x + m));
            switch (h0)
            {
                case 0:
                    return Color.FromArgb(255, c0, x0, m0);
                case 1:
                    return Color.FromArgb(255, x0, c0, m0);
                case 2:
                    return Color.FromArgb(255, m0, c0, x0);
                case 3:
                    return Color.FromArgb(255, m0, x0, c0);
                case 4:
                    return Color.FromArgb(255, x0, m0, c0);
                case 5:
                    return Color.FromArgb(255, c0, m0, x0);
            }
            return Color.FromArgb(255, m0, m0, m0);
        }

        // Just so I don't have to write "bool ? val : val" everywhere
        private static double Clamp(double val, double min, double max)
        {
            if (val >= max)
                return max;
            else if (val <= min)
                return min;
            else
                return val;
        }

        // Simple convolution as C# BCL doesn't appear to have any
        private static Color Convolve2D(double[,] k, Color[,] n)
        {
            double r = 0;
            double g = 0;
            double b = 0;
            for (int i=0; i<k.GetLength(0); i++)
            {
                for (int j=0; j<k.GetLength(1); j++)
                {
                    r += n[i,j].R * k[i,j];
                    g += n[i,j].G * k[i,j];
                    b += n[i,j].B * k[i,j];
                }
            }
            return Color.FromArgb(
                Convert.ToInt32(Math.Round(r)),
                Convert.ToInt32(Math.Round(g)),
                Convert.ToInt32(Math.Round(b)));
        }

        // Generates a simple 2D square (normalized) Gaussian kernel based on a size
        // No tuning parameters - just using sigma = 1 for each
        private static double [,] GenerateGaussianKernel(int n)
        {
            double[,] kernel = new double[n, n];
            double currentValue;
            double normTotal = 0;
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    currentValue = Math.Exp(-(Math.Pow(i - n / 2, 2) + Math.Pow(j - n / 2, 2)) / 2.0);
                    kernel[i, j] = currentValue;
                    normTotal += currentValue;
                }
            }
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    kernel[i, j] /= normTotal;
                }
            }
            return kernel;
        }

        // Gets the neighbours around the current pixel
        private static Color[,] GetNeighbours(Bitmap bmp, int n, int x, int y)
        {
            Color[,] neighbours = new Color[n, n];
            for (int i = -n/2; i < n-n/2; i++)
            {
                for (int j = -n/2; j < n-n/2; j++)
                {
                    neighbours[i+n/2, j+n/2] = bmp.GetPixel(x + i, y + j);
                }
            }
            return neighbours;
        }
    }
}

9

Java

Gauss bulanıklığını taklit eden çoklu geçişleri çalıştırmak için yeterince hızlı olması için hızlı çalışan ortalama iki yönlü kutu bulanıklaştırmasını kullanır. Blur, aynı zamanda bi-linear yerine eliptik bir gradyandır.

Görsel olarak, büyük görüntülerde en iyi sonucu verir. Daha koyu, grungier teması var. Bulanıklığın uygun boyutta görüntülere nasıl dönüştüğü ile mutluyum, nerede başladığını anlamak oldukça yavaş ve zor.

Tüm hesaplamalar tam sayı veya çiftler dizisi üzerinde yapılmıştır (HSV için).

Dosya yolunu argüman olarak bekler, dosyayı "miniaturized.png" ekiyle aynı konuma çıkarır. Ayrıca girdi ve çıktıyı hemen görüntülemek için bir JFrame'de görüntüler.

(büyük sürümleri görmek için tıklayın, çok daha iyi)

Önce:

http://i.imgur.com/cOPl6EOl.jpg

Sonra:

görüntü tanımını buraya girin

Oldukça karanlık olabileceğinden daha akıllı ton eşlemesi veya luma koruması eklemem gerekebilir:

Önce:

görüntü tanımını buraya girin

Sonra:

görüntü tanımını buraya girin

Yine de ilginç olsa da, yepyeni bir atmosfere koyar.

Kod:

import java.awt.*;
import java.awt.image.*;
import java.io.*;

import javax.imageio.*;
import javax.swing.*;

public class SceneMinifier {

    static final double CONTRAST_INCREASE = 8;
    static final double SATURATION_INCREASE = 7;

    public static void main(String[] args) throws IOException {

        if (args.length < 1) {
            System.out.println("Please specify an input image file.");
            return;
        }

        BufferedImage temp = ImageIO.read(new File(args[0]));

        BufferedImage input = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_INT_ARGB);
        input.getGraphics().drawImage(temp, 0, 0, null); // just want to guarantee TYPE_ARGB

        int[] pixels = ((DataBufferInt) input.getData().getDataBuffer()).getData();

        // saturation

        double[][] hsv = toHSV(pixels);
        for (int i = 0; i < hsv[1].length; i++)
            hsv[1][i] = Math.min(1, hsv[1][i] * (1 + SATURATION_INCREASE / 10));

        // contrast

        int[][] rgb = toRGB(hsv[0], hsv[1], hsv[2]);

        double c = (100 + CONTRAST_INCREASE) / 100;
        c *= c;

        for (int i = 0; i < pixels.length; i++)
            for (int q = 0; q < 3; q++)
                rgb[q][i] = (int) Math.max(0, Math.min(255, ((rgb[q][i] / 255. - .5) * c + .5) * 255));

        // blur

        int w = input.getWidth();
        int h = input.getHeight();

        int k = 5;
        int kd = 2 * k + 1;
        double dd = 1 / Math.hypot(w / 2, h / 2);

        for (int reps = 0; reps < 5; reps++) {

            int tmp[][] = new int[3][pixels.length];
            int vmin[] = new int[Math.max(w, h)];
            int vmax[] = new int[Math.max(w, h)];

            for (int y = 0, yw = 0, yi = 0; y < h; y++) {
                int[] sum = new int[3];
                for (int i = -k; i <= k; i++) {
                    int ii = yi + Math.min(w - 1, Math.max(i, 0));
                    for (int q = 0; q < 3; q++)
                        sum[q] += rgb[q][ii];
                }
                for (int x = 0; x < w; x++) {

                    int dx = x - w / 2;
                    int dy = y - h / 2;
                    double dist = Math.sqrt(dx * dx + dy * dy) * dd;
                    dist *= dist;

                    for (int q = 0; q < 3; q++)
                        tmp[q][yi] = (int) Math.min(255, sum[q] / kd * dist + rgb[q][yi] * (1 - dist));

                    if (y == 0) {
                        vmin[x] = Math.min(x + k + 1, w - 1);
                        vmax[x] = Math.max(x - k, 0);
                    }

                    int p1 = yw + vmin[x];
                    int p2 = yw + vmax[x];

                    for (int q = 0; q < 3; q++)
                        sum[q] += rgb[q][p1] - rgb[q][p2];
                    yi++;
                }
                yw += w;
            }

            for (int x = 0, yi = 0; x < w; x++) {
                int[] sum = new int[3];
                int yp = -k * w;
                for (int i = -k; i <= k; i++) {
                    yi = Math.max(0, yp) + x;
                    for (int q = 0; q < 3; q++)
                        sum[q] += tmp[q][yi];
                    yp += w;
                }
                yi = x;
                for (int y = 0; y < h; y++) {

                    int dx = x - w / 2;
                    int dy = y - h / 2;
                    double dist = Math.sqrt(dx * dx + dy * dy) * dd;
                    dist *= dist;

                    for (int q = 0; q < 3; q++)
                        rgb[q][yi] = (int) Math.min(255, sum[q] / kd * dist + tmp[q][yi] * (1 - dist));

                    if (x == 0) {
                        vmin[y] = Math.min(y + k + 1, h - 1) * w;
                        vmax[y] = Math.max(y - k, 0) * w;
                    }
                    int p1 = x + vmin[y];
                    int p2 = x + vmax[y];

                    for (int q = 0; q < 3; q++)
                        sum[q] += tmp[q][p1] - tmp[q][p2];

                    yi += w;
                }
            }
        }

        // pseudo-lighting pass

        for (int i = 0; i < pixels.length; i++) {
            int dx = i % w - w / 2;
            int dy = i / w - h / 2;
            double dist = Math.sqrt(dx * dx + dy * dy) * dd;
            dist *= dist;

            for (int q = 0; q < 3; q++) {
                if (dist > 1 - .375)
                    rgb[q][i] *= 1 + (Math.sqrt((1 - dist + .125) / 2) - (1 - dist) - .125) * .7;
                if (dist < .375 || dist > .375)
                    rgb[q][i] *= 1 + (Math.sqrt((dist + .125) / 2) - dist - .125) * dist > .375 ? 1 : .8;
                rgb[q][i] = Math.min(255, Math.max(0, rgb[q][i]));
            }
        }

        // reassemble image

        BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);

        pixels = ((DataBufferInt) output.getData().getDataBuffer()).getData();

        for (int i = 0; i < pixels.length; i++)
            pixels[i] = 255 << 24 | rgb[0][i] << 16 | rgb[1][i] << 8 | rgb[2][i];

        output.setRGB(0, 0, output.getWidth(), output.getHeight(), pixels, 0, output.getWidth());

        // display results

        display(input, output);

        // output image

        ImageIO.write(output, "PNG", new File(args[0].substring(0, args[0].lastIndexOf('.')) + " miniaturized.png"));

    }

    private static int[][] toRGB(double[] h, double[] s, double[] v) {
        int[] r = new int[h.length];
        int[] g = new int[h.length];
        int[] b = new int[h.length];

        for (int i = 0; i < h.length; i++) {
            double C = v[i] * s[i];
            double H = h[i];
            double X = C * (1 - Math.abs(H % 2 - 1));

            double ri = 0, gi = 0, bi = 0;

            if (0 <= H && H < 1) {
                ri = C;
                gi = X;
            } else if (1 <= H && H < 2) {
                ri = X;
                gi = C;
            } else if (2 <= H && H < 3) {
                gi = C;
                bi = X;
            } else if (3 <= H && H < 4) {
                gi = X;
                bi = C;
            } else if (4 <= H && H < 5) {
                ri = X;
                bi = C;
            } else if (5 <= H && H < 6) {
                ri = C;
                bi = X;
            }

            double m = v[i] - C;

            r[i] = (int) ((ri + m) * 255);
            g[i] = (int) ((gi + m) * 255);
            b[i] = (int) ((bi + m) * 255);
        }

        return new int[][] { r, g, b };
    }

    private static double[][] toHSV(int[] c) {
        double[] h = new double[c.length];
        double[] s = new double[c.length];
        double[] v = new double[c.length];

        for (int i = 0; i < c.length; i++) {
            double r = (c[i] & 0xFF0000) >> 16;
            double g = (c[i] & 0xFF00) >> 8;
            double b = c[i] & 0xFF;

            r /= 255;
            g /= 255;
            b /= 255;

            double M = Math.max(Math.max(r, g), b);
            double m = Math.min(Math.min(r, g), b);
            double C = M - m;

            double H = 0;

            if (C == 0)
                H = 0;
            else if (M == r)
                H = (g - b) / C % 6;
            else if (M == g)
                H = (b - r) / C + 2;
            else if (M == b)
                H = (r - g) / C + 4;

            h[i] = H;
            s[i] = C / M;
            v[i] = M;
        }
        return new double[][] { h, s, v };
    }

    private static void display(final BufferedImage original, final BufferedImage output) {

        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int wt = original.getWidth();
        int ht = original.getHeight();
        double ratio = (double) wt / ht;
        if (ratio > 1 && wt > d.width / 2) {
            wt = d.width / 2;
            ht = (int) (wt / ratio);
        }
        if (ratio < 1 && ht > d.getHeight() / 2) {
            ht = d.height / 2;
            wt = (int) (ht * ratio);
        }

        final int w = wt, h = ht;

        JFrame frame = new JFrame();
        JPanel pan = new JPanel() {
            BufferedImage buffer = new BufferedImage(w * 2, h, BufferedImage.TYPE_INT_RGB);
            Graphics2D gg = buffer.createGraphics();

            {
                gg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }

            @Override
            public void paint(Graphics g) {
                gg.drawImage(original, 0, 0, w, h, null);
                gg.drawImage(output, w, 0, w, h, null);
                g.drawImage(buffer, 0, 0, null);
            }
        };
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pan.setPreferredSize(new Dimension(w * 2, h));
        frame.setLayout(new BorderLayout());
        frame.add(pan, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }
}

8

J

Bu güzel bir meydan okumaydı. Bulanıklık, doygunluk ve kontrast ayarları zor kodlanmıştır ancak istenirse kolayca değiştirilebilir. Bununla birlikte, odaktaki alan, merkezde yatay bir çizgi olarak kodlanmıştır. Diğer ayarlar gibi kolayca değiştirilemez. Test görüntülerinin çoğu şehir genelinde manzara içerdiğinden seçilmiştir.

Gauss bulanıklığı yaptıktan sonra görüntüyü yatay olarak 5 bölgeye ayırdım. Üst ve alt bölgeler bulanıklığın% 100'ünü alır. Orta bölge bulanıklığın% 0'ını alır. Kalan iki bölge her ikisi de ters küp ile orantılı olarak% 0 ile% 100 arasında ölçeklenir.

Kod, J'de bir komut dosyası olarak kullanılacaktır ve bu komut dosyası input.bmp, giriş görüntüsü ile aynı klasörde olacaktır. Bu yaratacak output.bmpbir olacak sahte minyatür girişinin.

Performans iyi ve bilgisayarımda bir i7-4770k kullanıyorsanız, OP ayarlarından bir görüntüyü işlemek yaklaşık 20 saniye sürüyor. Önceden, ;._3alt dizi operatörü ile standart evrişim kullanarak bir görüntünün işlenmesi yaklaşık 70 saniye sürdü . Konvolüsyon gerçekleştirmek için FFT kullanılarak performans iyileştirildi.

döngü Döngü-Mini Kent İl-Mini

Bu kod bmpve math/fftweklerinin kurulmasını gerektirir . Bunları install 'bmp've kullanarak yükleyebilirsiniz install 'math/fftw'. Sisteminizin fftwde kurulu kütüphaneye ihtiyacı olabilir .

load 'bmp math/fftw'

NB. Define 2d FFT
fft2d =: 4 : 0
  s =. $ y
  i =. zzero_jfftw_ + , y
  o =. 0 * i
  p =. createplan_jfftw_ s;i;o;x;FFTW_ESTIMATE_jfftw_
  fftwexecute_jfftw_ p
  destroyplan_jfftw_ p
  r =. s $ o
  if. x = FFTW_BACKWARD_jfftw_ do.
    r =. r % */ s
  end.
  r
)

fft2 =: (FFTW_FORWARD_jfftw_ & fft2d) :. (FFTW_BACKWARD_jfftw & fft2d)
ifft2 =: (FFTW_BACKWARD_jfftw_ & fft2d) :. (FFTW_FORWARD_jfftw & fft2d)

NB. Settings: Blur radius - Saturation - Contrast
br =: 15
s =: 3
c =: 1.5

NB. Read image and extract rgb channels
i =: 255 %~ 256 | (readbmp 'input.bmp') <.@%"_ 0 ] 2 ^ 16 8 0
'h w' =: }. $ i

NB. Pad each channel to fit Gaussian blur kernel
'hp wp' =: (+: br) + }. $ i
ip =: (hp {. wp {."1 ])"_1 i

NB. Gaussian matrix verb
gm =: 3 : '(%+/@,)s%~2p1%~^(s=.*:-:y)%~-:-+&*:/~i:y'

NB. Create a Gaussian blur kernel
gk =: fft2 hp {. wp {."1 gm br

NB. Blur each channel using FFT-based convolution and unpad
ib =: (9 o. (-br) }. (-br) }."1 br }. br }."1 [: ifft2 gk * fft2)"_1 ip

NB. Create the blur gradient to emulate tilt-shift
m =: |: (w , h) $ h ({. >. (({.~ -)~ |.)) , 1 ,: |. (%~i.) 0.2 I.~ (%~i.) h

NB. Tilt-shift each channel
it =: i ((m * ]) + (-. m) * [)"_1 ib

NB. Create the saturation matrix
sm =: |: ((+ ] * [: =@i. 3:)~ 3 3 |:@$ 0.299 0.587 0.114 * -.) s

NB. Saturate and clamp
its =: 0 >. 1 <. sm +/ .*"1 _ it

NB. Contrast and clamp
itsc =: 0 >. 1 <. 0.5 + c * its - 0.5

NB. Output the image
'output.bmp' writebmp~ (2 <.@^ 16 8 0) +/ .* 255 <.@* itsc

exit ''
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.