照片/图像到草图算法

时间:2012-03-22 16:14:06

标签: java android

有没有人有关于如何将照片和图像(位图)转换为粗略图片的想法,链接,库,源代码......我找不到任何关于如何做的好消息来源。

我发现此链接How to cartoon-ify an image programmatically?关于如何以编程方式对图像进行卡通,但我更喜欢将其设置为图像到草图。

我想制作一款能够以编程方式将“JPEG照片”转换为粗略图像的Android应用程序。

5 个答案:

答案 0 :(得分:28)

好的,所以我用马克告诉我的不同技术找到了自己的答案。 我使用以下伪代码:

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

如果代码可以改进,请在下面发布新的答案或评论。谢谢!

答案 1 :(得分:6)

添加颜色。

*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-ColorColor-Dodge-Blend-Merge合并,则会更好。

感谢XverhelstX提出的问题解答

答案 2 :(得分:4)

以下是如何在图形编辑程序中创建此类效果的示例:

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

  1. 将图像转换为灰度。
  2. 制作副本并反转强度。
  3. 模糊副本。
  4. 使用Color Dodge formula合并两张图片。

答案 3 :(得分:3)

根据@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方法i-e中的第一个方法:

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

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

    }
});

答案 4 :(得分:1)

好的,如果你有一个,那么你可以在这里发布代码,看看是否有人可以帮助你将代码翻译成java ..另一种选择是......你可能不得不使用ndk或者..但是我确实找到了一些链接,我在这里发布..希望你在这些链接中找到一些有趣的东西

How to cartoon-ify an image programmatically?您可以查看此链接