Java 照片/图像到草图算法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9826273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 08:12:27  来源:igfitidea点击:

photo/image-to-sketch algorithm

javaandroid

提问by Verhelst

Does anyone have an idea, link, library, source code, ... on how to convert photo's and images (bitmaps) to sketchy-like pictures? I can't find any good sources on how to do it.

有没有人有关于如何将照片和图像(位图)转换为粗略图片的想法、链接、库、源代码……?我找不到任何关于如何做到这一点的好资源。

I found this link How to cartoon-ify an image programmatically?about how to cartoon-ify a image programmatically, but i prefer to make it image-to-sketch one.

我找到了这个链接如何以编程方式卡通化图像?关于如何以编程方式卡通化图像,但我更喜欢将其制作成图像到草图。

I want to make an android app that can programmatically "convert" JPEG photo's to sketchy images.

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

采纳答案by Verhelst

Ok, so i found my own answer using different techniques like Mark told me. I use the following pseudocode:

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

*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)

The first four methods were easily to find on the internet, however on the last one I couldn't find a lot of information, not even source code. So I searched on how PS did it and found the following formula in c++:

前四种方法在网上很容易找到,但是最后一种我找不到很多信息,甚至找不到源代码。所以我搜索了 PS 是如何做到的,并在 C++ 中找到了以下公式:

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

Then i converted it to Java with the following code:

然后我使用以下代码将其转换为 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;
}

If the code could be improved, please post a new answer or comment below. Thanks!

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

回答by Nav

Ok if you got one then you can post the code here and see if someone can help you translate the code to java ..the other alternative being..you may have to use the ndk perhaps..However I did find some links and I am posting them here..hope you find something interesting here in these links

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

How to cartoon-ify an image programmatically?you can check this link

如何以编程方式卡通化图像?你可以检查这个链接

回答by Mark Ransom

Here's an example of how to create such an effect in a graphics editing program:

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

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

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

  1. Convert the image to grayscale.
  2. Make a copy and invert the intensities.
  3. Blur the copy.
  4. Combine the two images using a Color Dodge formula.
  1. 将图像转换为灰度。
  2. 制作副本并反转强度。
  3. 模糊副本。
  4. 使用颜色减淡公式组合两个图像。

回答by Rabamirezzan

And adding color.

并添加颜色。

*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)

I little modification to ColorDodgeBlendto eliminate all colors.

我很少修改以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);

An the applying color method:

一种上色方法:

//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;

    }

This is not improved. Its better if Apply-Colorand Color-Dodge-Blend-Mergemerges.

这没有改进。如果Apply-ColorColor-Dodge-Blend-Merge合并它会更好。

Thanks to XverhelstX for his Question-Answer

感谢 XverhelstX 的问答

回答by Dear S

Here is the clear answer with the code according to @XverhelstX I have created a code to get clearly sketch from photo. Take Image from source and convert it into an Input Bitmap. Now call the method below and pass Bitmap into it.

根据@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;
}

Here we got 3 methods GrayScale,Inverted,and Color-DodgeBlend.

这里我们有 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)))));
}

One thing to be noted that in my code I am blurring the bitmap using Renderscript.

需要注意的一件事是,在我的代码中,我使用 Renderscript 模糊了位图。

Here is the Blur class.

这是 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;
    }
}

After setting all this, simply pass your input bitmap into the first method from onCreate method i-e:

设置完所有这些后,只需将您的输入位图传递到 onCreate 方法中的第一个方法,即:

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

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

    }
});