алгоритм фото / изображение к эскизу - PullRequest
10 голосов
/ 22 марта 2012

У кого-нибудь есть идея, ссылка, библиотека, исходный код, ... о том, как преобразовать фотографии и изображения (растровые изображения) в схематичные изображения? Я не могу найти хорошие источники о том, как это сделать.

Я нашел эту ссылку Как программно изобразить изображение в мультфильме? о том, как программно изобразить изображение из мультфильма, но я предпочитаю сделать его из изображения в эскиз.

Я хочу создать приложение для Android, которое может программно «конвертировать» фотографии JPEG в схематичные изображения.

Ответы [ 5 ]

28 голосов
/ 24 марта 2012

Хорошо, поэтому я нашел свой собственный ответ, используя разные техники, как сказал мне Марк.Я использую следующий псевдокод:

*s = Read-File-Into-Image("/path/to/image")
*g = Convert-To-Gray-Scale(s)
*i = Invert-Colors(g)
*b = Apply-Gaussian-Blur(i)
*result = Color-Dodge-Blend-Merge(b,g)

Первые четыре метода было легко найти в Интернете, однако на последнем я не смог найти много информации, даже исходного кода.Поэтому я искал, как PS это сделал, и нашел следующую формулу в c ++:

((uint8)((B == 255) ? B:min(255, ((A << 8 ) / (255 - B)))))

Затем я преобразовал ее в Java со следующим кодом:

private int colordodge(int in1, int in2) {
    float image = (float)in2;
    float mask = (float)in1;
    return ((int) ((image == 255) ? image:Math.min(255, (((long)mask << 8 ) / (255 - image)))));

}

/**
 * Blends 2 bitmaps to one and adds the color dodge blend mode to it.
 */
public Bitmap ColorDodgeBlend(Bitmap source, Bitmap layer) {
    Bitmap base = source.copy(Config.ARGB_8888, true);
    Bitmap blend = layer.copy(Config.ARGB_8888, false);

    IntBuffer buffBase = IntBuffer.allocate(base.getWidth() * base.getHeight());
    base.copyPixelsToBuffer(buffBase);
    buffBase.rewind();

    IntBuffer buffBlend = IntBuffer.allocate(blend.getWidth() * blend.getHeight());
    blend.copyPixelsToBuffer(buffBlend);
    buffBlend.rewind();

    IntBuffer buffOut = IntBuffer.allocate(base.getWidth() * base.getHeight());
    buffOut.rewind();

    while (buffOut.position() < buffOut.limit()) {
        int filterInt = buffBlend.get();
        int srcInt = buffBase.get();

        int redValueFilter = Color.red(filterInt);
        int greenValueFilter = Color.green(filterInt);
        int blueValueFilter = Color.blue(filterInt);

        int redValueSrc = Color.red(srcInt);
        int greenValueSrc = Color.green(srcInt);
        int blueValueSrc = Color.blue(srcInt);

        int redValueFinal = colordodge(redValueFilter, redValueSrc);
        int greenValueFinal = colordodge(greenValueFilter, greenValueSrc);
        int blueValueFinal = colordodge(blueValueFilter, blueValueSrc);

        int pixel = Color.argb(255, redValueFinal, greenValueFinal, blueValueFinal);

        buffOut.put(pixel);
    }

    buffOut.rewind();

    base.copyPixelsFromBuffer(buffOut);
    blend.recycle();

    return base;
}

Если код могулучшение, пожалуйста, оставьте новый ответ или комментарий ниже.Спасибо!

6 голосов
/ 12 октября 2012

И добавление цвета.

*s = Read-File-Into-Image("/path/to/image")
*g = Convert-To-Gray-Scale(s)
*i = Invert-Colors(g)
*b = Apply-Gaussian-Blur(i)
*result = Color-Dodge-Blend-Merge(b,g)   
*s2 = Apply-Gaussian-Blur(s) //I use radius 3
*cartoon = Apply-Color(s2, result)

Я немного изменил ColorDodgeBlend, чтобы устранить все цвета.

public Bitmap ColorDodgeBlend(Bitmap source, Bitmap layer) 
....
//before buffOut.put(pixel);

float[] hsv = new float[3];
        Color.colorToHSV(pixel, hsv);
        hsv[1] = 0.0f;
        float top = VALUE_TOP; //Between 0.0f .. 1.0f I use 0.87f
        if (hsv[2] <= top) {
            hsv[2] = 0.0f;
        } else {
            hsv[2] = 1.0f;
        }
        pixel = Color.HSVToColor(hsv);

Метод нанесения цвета:

//hue, saturarion, value intervals size are for reduce colors on Bitmap
//saturation, value percents are for increment or decrement [0..100..)
public Bitmap getCartoonizedBitmap(Bitmap realBitmap, Bitmap dodgeBlendBitmap, int hueIntervalSize, int saturationIntervalSize, int valueIntervalSize, int saturationPercent, int valuePercent) {
    // Bitmap bitmap = Bitmap.createBitmap(scaledBitmap);
    // //fastblur(scaledBitmap, 4);
    Bitmap base = fastblur(realBitmap, 3).copy(Config.ARGB_8888, true);
    Bitmap dodge = dodgeBlendBitmap.copy(Config.ARGB_8888, false);
    try {
        int realColor;
        int color;
        float top = VALUE_TOP; //Between 0.0f .. 1.0f I use 0.87f
        IntBuffer templatePixels = IntBuffer.allocate(dodge.getWidth()
                * dodge.getHeight());
        IntBuffer scaledPixels = IntBuffer.allocate(base.getWidth()
                * base.getHeight());
        IntBuffer buffOut = IntBuffer.allocate(base.getWidth()
                * base.getHeight());

        base.copyPixelsToBuffer(scaledPixels);
        dodge.copyPixelsToBuffer(templatePixels);

        templatePixels.rewind();
        scaledPixels.rewind();
        buffOut.rewind();

        while (buffOut.position() < buffOut.limit()) {
            color = (templatePixels.get());
            realColor = scaledPixels.get();

            float[] realHSV = new float[3];
            Color.colorToHSV(realColor, realHSV);

            realHSV[0] = getRoundedValue(realHSV[0], hueIntervalSize);

            realHSV[2] = (getRoundedValue(realHSV[2] * 100,
                    valueIntervalSize) / 100) * (valuePercent / 100);
            realHSV[2] = realHSV[2]<1.0?realHSV[2]:1.0f;

            realHSV[1] = realHSV[1] * (saturationPercent / 100);
            realHSV[1] = realHSV[1]<1.0?realHSV[1]:1.0f;

            float[] HSV = new float[3];
            Color.colorToHSV(color, HSV);

            boolean putBlackPixel = HSV[2] <= top;

            realColor = Color.HSVToColor(realHSV);

            if (putBlackPixel) {
                buffOut.put(color);
            } else {
                buffOut.put(realColor);
            }
        }// END WHILE
        dodge.recycle();
        buffOut.rewind();
        base.copyPixelsFromBuffer(buffOut); 

    } catch (Exception e) {
        // TODO: handle exception
    }

    return base;
}

public static float getRoundedValue(float value, int intervalSize) {
        float result = Math.round(value);
        int mod = ((int) result) % intervalSize;
        result += mod < (intervalSize / 2) ? -mod : intervalSize - mod;
        return result;

    }

Это не улучшается. Лучше, если Apply-Color и Color-Dodge-Blend-Merge сливаются.

Спасибо XverhelstX за вопрос-ответ

4 голосов
/ 22 марта 2012

Вот пример того, как создать такой эффект в программе редактирования графики:

http://www.createblog.com/paintshop-pro-tutorials/14018-sketch-effect/

  1. Преобразовать изображение в оттенки серого.
  2. Сделатькопию и инвертируйте интенсивности.
  3. Размытие копии.
  4. Объедините два изображения, используя формулу Color Dodge .
3 голосов
/ 09 мая 2016

Вот ясный ответ с кодом в соответствии с @XverhelstX. Я создал код, чтобы получить четкий эскиз с фотографии.Возьмите изображение из источника и преобразуйте его во входное растровое изображение.Теперь вызовите метод ниже и передайте в него Bitmap.

public Bitmap Changetosketch(Bitmap bmp){
    Bitmap Copy,Invert,Result;
    Copy =bmp;
    Copy = toGrayscale(Copy);
    Invert = createInvertedBitmap(Copy);
    Invert = Blur.blur(MainActivity.this, Invert);
    Result = ColorDodgeBlend(Invert, Copy);

    return Result;
}

Здесь мы получили 3 метода GrayScale, Inverted и Color-DodgeBlend.

public static Bitmap toGrayscale(Bitmap bmpOriginal)
{
    int width, height;
    height = bmpOriginal.getHeight();
    width = bmpOriginal.getWidth();

    Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    Canvas c = new Canvas(bmpGrayscale);
    Paint paint = new Paint();
    ColorMatrix cm = new ColorMatrix();
    cm.setSaturation(0);
    ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
    paint.setColorFilter(f);
    c.drawBitmap(bmpOriginal, 0, 0, paint);
    return bmpGrayscale;
}

public static Bitmap createInvertedBitmap(Bitmap src) {
    ColorMatrix colorMatrix_Inverted =
            new ColorMatrix(new float[] {
                    -1,  0,  0,  0, 255,
                    0, -1,  0,  0, 255,
                    0,  0, -1,  0, 255,
                    0,  0,  0,  1,   0});

    ColorFilter ColorFilter_Sepia = new ColorMatrixColorFilter(
            colorMatrix_Inverted);

    Bitmap bitmap = Bitmap.createBitmap(src.getWidth(), src.getHeight(),
            Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);

    Paint paint = new Paint();

    paint.setColorFilter(ColorFilter_Sepia);
    canvas.drawBitmap(src, 0, 0, paint);

    return bitmap;
}
public Bitmap ColorDodgeBlend(Bitmap source, Bitmap layer) {
    Bitmap base = source.copy(Bitmap.Config.ARGB_8888, true);
    Bitmap blend = layer.copy(Bitmap.Config.ARGB_8888, false);

    IntBuffer buffBase = IntBuffer.allocate(base.getWidth() * base.getHeight());
    base.copyPixelsToBuffer(buffBase);
    buffBase.rewind();

    IntBuffer buffBlend = IntBuffer.allocate(blend.getWidth() * blend.getHeight());
    blend.copyPixelsToBuffer(buffBlend);
    buffBlend.rewind();

    IntBuffer buffOut = IntBuffer.allocate(base.getWidth() * base.getHeight());
    buffOut.rewind();

    while (buffOut.position() < buffOut.limit()) {

        int filterInt = buffBlend.get();
        int srcInt = buffBase.get();

        int redValueFilter = Color.red(filterInt);
        int greenValueFilter = Color.green(filterInt);
        int blueValueFilter = Color.blue(filterInt);

        int redValueSrc = Color.red(srcInt);
        int greenValueSrc = Color.green(srcInt);
        int blueValueSrc = Color.blue(srcInt);

        int redValueFinal = colordodge(redValueFilter, redValueSrc);
        int greenValueFinal = colordodge(greenValueFilter, greenValueSrc);
        int blueValueFinal = colordodge(blueValueFilter, blueValueSrc);


        int pixel = Color.argb(255, redValueFinal, greenValueFinal, blueValueFinal);


        buffOut.put(pixel);
    }

    buffOut.rewind();

    base.copyPixelsFromBuffer(buffOut);
    blend.recycle();

    return base;
}

private int colordodge(int in1, int in2) {
    float image = (float)in2;
    float mask = (float)in1;
    return ((int) ((image == 255) ? image:Math.min(255, (((long)mask << 8 ) / (255 - image)))));
}

Одна вещь, которую следует отметить, что вмой код Я размываю растровое изображение с помощью Renderscript.

Вот класс Blur.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;
import android.view.View;


public class Blur {
    private static final float BITMAP_SCALE = 0.4f;
    private static final float BLUR_RADIUS = 4.5f;

    public static Bitmap blur(View v) {
        return blur(v.getContext(), getScreenshot(v));
    }

    public static Bitmap blur(Context ctx, Bitmap image) {
        Bitmap photo = image.copy(Bitmap.Config.ARGB_8888, true);

        try {
            final RenderScript rs = RenderScript.create( ctx );
            final Allocation input = Allocation.createFromBitmap(rs, photo, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
            final Allocation output = Allocation.createTyped(rs, input.getType());
            final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
            script.setRadius( BLUR_RADIUS ); /* e.g. 3.f */
            script.setInput( input );
            script.forEach( output );
            output.copyTo( photo );
        }catch (Exception e){
            e.printStackTrace();
        }
        return photo;
    }

    private static Bitmap getScreenshot(View v) {
        Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        v.draw(c);
        return b;
    }
}

После установки всего этого просто передайте входное растровое изображение в первый метод из метода onCreate, т.е.

Done.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

      ResultBitmap = ChangetoSketch(InputBitmap);
      ImageView.setImageBitmap(ResultBitmap);

    }
});
1 голос
/ 22 марта 2012

Хорошо, если у вас есть один, тогда вы можете опубликовать код здесь и посмотреть, может ли кто-нибудь помочь вам перевести код на java ... другой альтернативой является ... вам, возможно, придется использовать ndk, возможно .. Однако, когда я нашел какой-то ссылки, и я публикую их здесь .. надеюсь, вы найдете что-то интересное здесь в этих ссылках

Как создать мультфильм программно для изображения? Вы можете проверить эту ссылку

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...