Photo / Image Sketch Algorithm

Does anyone have an idea, link, library, source code, ... about how to convert photos and images (bitmaps) into thumbnails? I can not find any good sources on how to do this.

I found this link. How to programmatically display an image? about how to make a cartoon image programmatically, but I prefer to make its image a sketch.

I want to make an Android application that can programmatically "convert" a JPEG photo into sketchy images.

+7
source share
5 answers

Ok, so I found my own answer using different methods like Mark told me. I am using the following pseudo code:

*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 could be easily found on the Internet, but on the last I could not find much information, not even the source code. So I searched how PS did this and found the following formula in C ++:

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

Then I converted it to Java with the following code:

 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 can be improved, post a new answer or comment below. Thanks!

+26
source

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) 

A slight modification to ColorDodgeBlend to eliminate all colors.

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

Color Method Used:

 //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 has not improved. Better if merging Apply-Color and Color-Dodge-Blend-Merge .

Thanks to XverhelstX for his question and answer

+6
source

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

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

  • Convert an image to grayscale.
  • Make a copy and invert the intensities.
  • Blur the copy.
  • Combine the two images using the Color Dodge formula.
+4
source

Here is a clear answer with code according to @XverhelstX. I created the code to get a clear sketch from the photo. Take the image from the source and convert it to input bitmap. Now call the method below and pass Bitmap to it.

  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.

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

It should be noted that in my code I draw a bitmap using Renderscript.

Here is the Blur class.

 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 ); /* eg 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 up, just pass the original bitmap to the first method from the onCreate ie method:

  Done.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ResultBitmap = ChangetoSketch(InputBitmap); ImageView.setImageBitmap(ResultBitmap); } }); 

If this helps, please vote.

+3
source

Well, if you have one, you can post the code here and see if someone can help you translate the code to java .. another alternative ... you might have to use ndk, maybe ... However, I found some links, and I post them here. I hope you find something interesting here in these links.

How to make a cartoon image programmatically? you can check this link

+1
source

All Articles