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.
Ok, so i found my own answer using different techniques like Mark told me.
I use the following pseudocode:
*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++:
((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 could be improved, please post a new answer or comment below. Thanks!
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 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);
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-Color and Color-Dodge-Blend-Merge merges.
Thanks to XverhelstX for his Question-Answer
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/
Convert the image to grayscale.
Make a copy and invert the intensities.
Blur the copy.
Combine the two images using a Color Dodge formula.
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.
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)))));
}
One thing to be noted that in my code I am blurring the 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 ); /* 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:
Done.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ResultBitmap = ChangetoSketch(InputBitmap);
ImageView.setImageBitmap(ResultBitmap);
}
});
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
How to cartoon-ify an image programmatically? you can check this link
Related
In my android app, I have a car from which the user can click and select different panels. The image is relatively complicated (as opposed to the one pasted here) so its difficult to overlay the buttons in the correct spots. In addition there are a lot of different images.
The solution I would like to try:
Detect which panel was selected by using a colour mask as suggested here: https://blahti.wordpress.com/2012/06/26/images-with-clickable-areas/
Depending on the panels selected (in my example the blue and green) generate a mask.
Depending on the mask, have a red overlay on the car - just a colour filter will be fine.
(First image represents the colours used to determine which panel was clicked, second image represents the mask generated and the last image the 'result').
The only problem I'm having is: How do I dynamically create the mask? I thought of using a floodfill type method to create a new canvas with the 'mask' of the selected panels. But, I worry that it might be too computationally heavy. Any simpler suggestions?
[
UPDATE: Ok so I've come pretty far. As expected, the creation of the mask too way too long (2-4 seconds for a small image). But, then I discovered RenderScripts!! I think I can still get this to work. The only little snag that I have now is: How do I pass in the colours that have been pressed?
My current code looks like this:
// create a bitmap for the mask.
ImageView img = (ImageView) findViewById (mask);
img.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(img.getDrawingCache());
// Create a tiny bitmap to store the colours of the panels that are
//'selected'
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
Bitmap myBitmap = Bitmap.createBitmap(pickedPanels.size(), 1, conf);
int [] myInts = new int[pickedPanels.size()];
for (int i = 0; i<pickedPanels.size(); i++){
myInts[i] = pickedPanels.get(i).intValue();
}
myBitmap.setPixels(myInts, 0, myBitmap.getWidth(), 0, 0,
myBitmap.getWidth(),0);
//Run thescript and set the output
final RenderScript rs = RenderScript.create(this);
final Allocation input = Allocation.createFromBitmap(rs, bitmap,
Allocation.MipmapControl.MIPMAP_NONE,Allocation.USAGE_SCRIPT);
final Allocation output = Allocation.createTyped(rs, input.getType());
final ScriptC_singlesource script = new
ScriptC_singlesource(rs);
script.set_image(Allocation.createFromBitmap(rs, myBitmap,
Allocation.MipmapControl.MIPMAP_NONE,Allocation.USAGE_SCRIPT));
script.set_imgWidth(pickedPanels.size());
script.forEach_root(input, output);
output.copyTo(bitmap);
img.setImageBitmap(bitmap);
ImageView destim = (ImageView) findViewById (dest);
destim.setDrawingCacheEnabled(true);
destim.setImageBitmap(bitmap);
and this is the script:
#pragma version(1)
#pragma rs java_package_name(za.co.overtake)
rs_allocation image;
int imgWidth;
uchar4 RS_KERNEL root(uchar4 in, uint32_t x, uint32_t y) {
for(int col = 0; col < imgWidth; col++){
const uchar4 colour = *(const uchar4*)rsGetElementAt(image, col,0);
if (in.r == colour.r && in.g == colour.g && in.b == colour.b){
in.r = 255;
in.g = 0;
in.b = 0;
break;
} else {
in.r = 0;
in.g = 255;
in.b = 0;
rsDebug("HELLLLLP>>", colour);
}
}
return in;
}
But, when I try and read the pixel values from myBitmap (or image in the script), RGB is always 0.
(Sorry for the bad naming, etc. I've been going crazy trying to figure this out)
Ok, finally got this figured out.
In my renderscript code I have:
#pragma version(1)
#pragma rs java_package_name(za.co.overtake)
int*reds;
int*greens;
int*blues;
int imgWidth;
uchar4 RS_KERNEL root(uchar4 in, uint32_t x, uint32_t y) {
bool colourme = false;
for(int col = 0; col < imgWidth; col++){
const int red = reds[col];
const int green = greens[col];
const int blue = blues[col];
if (in.r == red && in.g == green && in.b == blue){
colourme = true;
}
}
if (colourme) {
in.r = 255;
in.g = 0;
in.b = 0;
in.a = 50;
} else {
in.r = 0;
in.g = 0;
in.b = 0;
in.a = 0;
}
return in;
}
Then in Java
public void showDamagedPanels(int dest, int mask) {
int noOfColours = pickedPanels.size();
if (noOfColours > 0) {
ImageView img = (ImageView) findViewById (mask);
img.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(img.getDrawingCache());
img.setDrawingCacheEnabled(false);
int [] reds = new int[noOfColours];
int [] greens = new int[noOfColours];
int [] blues = new int[noOfColours];
for (int i = 0; i< noOfColours; i++){
int colour = pickedPanels.get(i);
reds[i] = (colour >> 16) & 0xFF;
greens[i] = (colour >> 8) & 0xFF;
blues[i] = (colour >> 0) & 0xFF;
}
final RenderScript rs = RenderScript.create(this);
final Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
final Allocation output = Allocation.createTyped(rs, input.getType());
final ScriptC_singlesource script = new ScriptC_singlesource(rs);
Allocation red = Allocation.createSized(rs, Element.I32(rs), reds.length);
red.copyFrom(reds);
script.bind_reds(red);
Allocation green = Allocation.createSized(rs, Element.I32(rs), greens.length);
green.copyFrom(greens);
script.bind_greens(green);
Allocation blue = Allocation.createSized(rs, Element.I32(rs), blues.length);
blue.copyFrom(blues);
script.bind_blues(blue);
script.set_imgWidth(pickedPanels.size());
script.forEach_root(input, output);
output.copyTo(bitmap);
ImageView destim = (ImageView) findViewById (dest);
destim.setDrawingCacheEnabled(true);
destim.setImageBitmap(bitmap);
} else {
ImageView destim = (ImageView) findViewById (dest);
destim.setImageBitmap(null);
}
}
where dest is the overlay image and mask in the image acting as the mask. So basically, when a panel is clicked - place its colour in pickedPanels. Then call the showPanels method, which calls the script. The script checks the colours and sets the resulting image red or clear.
Update: For anyone trying to use this, but having some issues: It is possible to do this without the renderscript code, but it does run a bit slower - though it has been ok in my case for small images.
private Bitmap changeColor(Bitmap src, Set<Integer> pickedPanelsList) {
int fine = getResources().getColor(R.color.colorAccent);
int width = src.getWidth();
int height = src.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
src.getPixels(pixels, 0, width, 0, 0, width, height);
Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
int AGood = 100, RGood = Color.red(fine), GGood = Color.green(fine), BGood = Color.blue(fine);
int ABad = 100, RBad = Color.red(Color.RED), GBad = Color.green(Color.RED), BBad = Color.blue(Color.RED);
int pixel;
// iteration through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
// get current index in 2D-matrix
int index = y * width + x;
pixel = pixels[index];
if(pickedPanelsList.contains(pixel)){
pixels[index] = Color.argb(ABad, RBad, GBad, BBad);
} else if (Color.alpha(pixel) > 0){
pixels[index] = Color.argb(AGood, RGood, GGood, BGood);
}
}
}
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}
Here, the picked panel set is all the colours that should be coloured red (or chosen) and the bitmap is the mask (if I remember correctly, I did this a while ago). I've also found that doing a slight blur on the result makes the image look nicer - since it will obviously be less jagged.
I am unable to successfully convert a javafx.scene.image.Image to a org.opencv.core.Mat. The resulting matrix produces a black image. I've not used PixelReader before so I am unsure wether or not I am using it correctly.
Here is my code:
public static Mat imageToMat(Image image) {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
byte[] buffer = new byte[width * height * 3];
PixelReader reader = image.getPixelReader();
WritablePixelFormat format = WritablePixelFormat.getByteBgraInstance();
reader.getPixels(0, 0, width, height, format, buffer, 0, 0);
Mat mat = new Mat(height, width, CvType.CV_8UC3);
mat.put(0, 0, buffer);
return mat;
}
Any help/solutions would be greatly appreciated! :) Thank you.
That stuff is still circumstantial. I've found 2 working solutions. I'll just post my OpenCvUtils class, hope it helps until someone comes up with a better solution:
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
public class OpenCvUtils {
/**
* Convert a Mat object (OpenCV) in the corresponding Image for JavaFX
*
* #param frame
* the {#link Mat} representing the current frame
* #return the {#link Image} to show
*/
public static Image mat2Image(Mat frame) {
// create a temporary buffer
MatOfByte buffer = new MatOfByte();
// encode the frame in the buffer, according to the PNG format
Imgcodecs.imencode(".png", frame, buffer);
// build and return an Image created from the image encoded in the
// buffer
return new Image(new ByteArrayInputStream(buffer.toArray()));
}
public static Mat image2Mat( Image image) {
BufferedImage bImage = SwingFXUtils.fromFXImage(image, null);
return bufferedImage2Mat( bImage);
}
// http://www.codeproject.com/Tips/752511/How-to-Convert-Mat-to-BufferedImage-Vice-Versa
public static Mat bufferedImage2Mat(BufferedImage in)
{
Mat out;
byte[] data;
int r, g, b;
int height = in.getHeight();
int width = in.getWidth();
if(in.getType() == BufferedImage.TYPE_INT_RGB || in.getType() == BufferedImage.TYPE_INT_ARGB)
{
out = new Mat(height, width, CvType.CV_8UC3);
data = new byte[height * width * (int)out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, width, height, null, 0, width);
for(int i = 0; i < dataBuff.length; i++)
{
data[i*3 + 2] = (byte) ((dataBuff[i] >> 16) & 0xFF);
data[i*3 + 1] = (byte) ((dataBuff[i] >> 8) & 0xFF);
data[i*3] = (byte) ((dataBuff[i] >> 0) & 0xFF);
}
}
else
{
out = new Mat(height, width, CvType.CV_8UC1);
data = new byte[height * width * (int)out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, width, height, null, 0, width);
for(int i = 0; i < dataBuff.length; i++)
{
r = (byte) ((dataBuff[i] >> 16) & 0xFF);
g = (byte) ((dataBuff[i] >> 8) & 0xFF);
b = (byte) ((dataBuff[i] >> 0) & 0xFF);
data[i] = (byte)((0.21 * r) + (0.71 * g) + (0.07 * b)); //luminosity
}
}
out.put(0, 0, data);
return out;
}
public static String getOpenCvResource(Class<?> clazz, String path) {
try {
return Paths.get( clazz.getResource(path).toURI()).toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
// Convert image to Mat
// alternate version http://stackoverflow.com/questions/21740729/converting-bufferedimage-to-mat-opencv-in-java
public static Mat bufferedImage2Mat_v2(BufferedImage im) {
im = toBufferedImageOfType(im, BufferedImage.TYPE_3BYTE_BGR);
// Convert INT to BYTE
//im = new BufferedImage(im.getWidth(), im.getHeight(),BufferedImage.TYPE_3BYTE_BGR);
// Convert bufferedimage to byte array
byte[] pixels = ((DataBufferByte) im.getRaster().getDataBuffer()).getData();
// Create a Matrix the same size of image
Mat image = new Mat(im.getHeight(), im.getWidth(), CvType.CV_8UC3);
// Fill Matrix with image values
image.put(0, 0, pixels);
return image;
}
private static BufferedImage toBufferedImageOfType(BufferedImage original, int type) {
if (original == null) {
throw new IllegalArgumentException("original == null");
}
// Don't convert if it already has correct type
if (original.getType() == type) {
return original;
}
// Create a buffered image
BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), type);
// Draw the image onto the new buffer
Graphics2D g = image.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, null);
}
finally {
g.dispose();
}
return image;
}
}
Thanks to Nikos Paraskevopoulos for suggesting setting the scanlineStride parameter of the PixelReader::getPixels() method, this has solved it. :)
Working code below:
public static Mat imageToMat(Image image) {
int width = (int) image.getWidth();
int height = (int) image.getHeight();
byte[] buffer = new byte[width * height * 4];
PixelReader reader = image.getPixelReader();
WritablePixelFormat<ByteBuffer> format = WritablePixelFormat.getByteBgraInstance();
reader.getPixels(0, 0, width, height, format, buffer, 0, width * 4);
Mat mat = new Mat(height, width, CvType.CV_8UC4);
mat.put(0, 0, buffer);
return mat;
}
You need convert : Mat > BufferedImage > FXImage
private Image mat2Image(Mat src)
{
BufferedImage image = ImageConverter.toImage(src);
return SwingFXUtils.toFXImage(image, null);
}
Class:
public class ImageConverter {
/**
* Converts/writes a Mat into a BufferedImage.
*
* #param src Mat of type CV_8UC3 or CV_8UC1
* #return BufferedImage of type TYPE_3BYTE_BGR or TYPE_BYTE_GRAY
*/
public static BufferedImage toImage(Mat src)
{
if ( src != null ) {
int cols = src.cols();
int rows = src.rows();
int elemSize = (int)src.elemSize();
byte[] data = new byte[cols * rows * elemSize];
int type;
src.data().get(data);
switch (src.channels()) {
case 1:
type = BufferedImage.TYPE_BYTE_GRAY;
break;
case 3:
type = BufferedImage.TYPE_3BYTE_BGR;
// bgr to rgb
byte b;
for(int i=0; i<data.length; i=i+3) {
b = data[i];
data[i] = data[i+2];
data[i+2] = b;
}
break;
default:
return null;
}
BufferedImage bimg = new BufferedImage(cols, rows, type);
bimg.getRaster().setDataElements(0, 0, cols, rows, data);
return bimg;
}
return null;
}
}
Following the solution above it may be also necessary to convert the format from four bytes (CvType.CV_8UC4) to three bytes (CvType.CV_8UC3) depending on what you are finally seeking. For example, if I read a xx.jpa image, it is RGB format.
if (isRGB)
Imgproc.cvtColor(mat,mat,Imgproc.COLOR_RGBA2RGB);
//or...COLOR_BGR2RGB,COLOR_BGRA2RGB,COLOR_BGR2BGRA
We are building a native extension for air, to generate bitmap data from text.
The code below generates the bitmap of a smiley ant "test" those should bee yellow but the color is blue.
http://i.stack.imgur.com/wC1ZH.png
After a lot of searching and trying different example code we are stuck.
public static Bitmap drawText(String text, int textWidth, int textSize, String color) {
try {
text = URLDecoder.decode("%F0%9F%98%8D test", "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// Get text dimensions
TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);
textPaint.setColor(Color.parseColor("#ffe400"));
textPaint.setTextSize(textSize);
textPaint.setAntiAlias(true);
StaticLayout mTextLayout = new StaticLayout(text, textPaint, textWidth, Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
// Create bitmap and canvas to draw to
Bitmap b = Bitmap.createBitmap(textWidth, mTextLayout.getHeight(), Config.ARGB_8888);
Canvas c = new Canvas(b);
// Draw text
c.save();
c.translate(0, 0);
mTextLayout.draw(c);
c.restore();
Extension.log("Color " + Integer.toString(b.getPixel(15,10), 16));
return b;
}
When logging the returned pixels its already blue so we assume it goes wrong in this function.
It seems that the red and blue color channel are switched.
Fixed it by reversing the blue and red color chanel:
private static Bitmap reversColors(Bitmap b){
int width = b.getWidth();
int height = b.getHeight();
int[] pixels = new int[width * height];
b.getPixels(pixels, 0, width, 0, 0, width, height);
int[] finalArray = new int[width * height];
for(int i = 0; i < finalArray.length; i++) {
int red = Color.red(pixels[i]);
int green = Color.green(pixels[i]);
int blue = Color.blue(pixels[i]);
int alpha = Color.alpha(pixels[i]);
finalArray[i] = Color.argb(alpha, blue, green, red);
}
return Bitmap.createBitmap(finalArray, width, height, Bitmap.Config.ARGB_8888);
}
It is not the best way, but i can't find a better solution
I have been stuck on this for about a week, I have searched all over the internet trying to find a solution of how to create a 2d cube effect using shear, I have tried numorous equations and this is the closest I have gotten in a week, it's purely math I believe but I can't figure it out. It is for a Multiplayer game, here is a video on Youtube of what I currently have with shearing.
https://www.youtube.com/watch?v=J0qP7T0k6xk
Here is my code:
public void addWall(Graphics2D g2d, ImageIcon img, int objX, int objY, int dir) {
//objX = objX/16;
objX = objX*32;
//objY = objY/16;
objY = (objY+4)*32;
int OffsetX = screen.width;
int OffsetY = screen.height*0;
if(dir == 1) {
//g2d.drawImage(img.getImage(), (((2*objX)+OffsetX)-(player.x*2)), (((2*objY)+OffsetY)-(player.y*2)) ,32*SCALE, ((32*SCALE)*-2)-(player.y-objY), null);
} else if(dir == 0) {
g2d.drawImage(img.getImage(), (((2*objX)+OffsetX)-(player.x*2)), (((2*objY)+OffsetY-(32*SCALE))-(player.y*2)) ,32*SCALE, ((32*SCALE)*-2)-(player.y-objY), null);
}
double skewX = (((player.x-objX)*2)*.100)*.1;
double skewY = 0;
//int x = (skewX < 0) ? -skewX*(32) : 0;
AffineTransform reset = g2d.getTransform();
AffineTransform at = AffineTransform.getTranslateInstance((-skewX*100) , 0);
at.shear(skewX, skewY);
//g2d.translate((((2*objX)+OffsetX)-(player.x*2)), (((2*objY)+OffsetY)-(player.y*2)));
// g2d.scale(SCALE, (((SCALE)*-2)-(player.y-objY)));
g2d.setTransform(at);
g2d.drawImage(img.getImage(), (((2*objX)+OffsetX)-(player.x*2)), (((2*objY)+OffsetY)-(player.y*2)) ,32*SCALE, ((32*SCALE)*-2)-(player.y-objY), null);
g2d.setTransform(reset);
//g2d.dispose();
}
public void addRoof(Graphics2D g2d, ImageIcon img, int objX, int objY) {
//objX = objX/16;
objX = objX*32;
//objY = objY/16;
objY = (objY+2)*32;
int shadowY = (objY+3)*32;
int OffsetX = screen.width;
int OffsetY = screen.height*0;
/* SHADOW */
/* SHADOW */
ImageIcon shadow = new ImageIcon("./res/shadowPixel.png");
g2d.drawImage(shadow.getImage(), (2*objX+OffsetX)-(player.x*2), (2*objY+(32*SCALE)+OffsetY)-(player.y*2), 32*SCALE, 32*SCALE, null);
/* SHADOW */
/* SHADOW */
g2d.drawImage(img.getImage(), (3*objX+OffsetX)-(player.x*3), (3*objY+OffsetY)-(player.y*3), 32*SCALE, 32*SCALE, null);
}
I am trying to resized a bufferedimage. I am able to store it and show up on a jframe no problems but I can't seem to resize it. Any tips on how I can change this to make it work and show the image as a 200*200 file would be great
private void profPic(){
String path = factory.getString("bottle");
BufferedImage img = ImageIO.read(new File(path));
}
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
Graphics2D g = dimg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
g.dispose();
return dimg;
}
Updated answer
I cannot recall why my original answer worked but having tested it in a separate environment, I agree, the original accepted answer doesn't work (why I said it did I cannot remember either). This, on the other hand, did work:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
If all that is required is to resize a BufferedImage in the resize method, then the Thumbnailator library can do that fairly easily:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
return Thumbnails.of(img).size(newW, newH).asBufferedImage();
}
The above code will resize the img to fit the dimensions of newW and newH while maintaining the aspect ratio of the original image.
If maintaining the aspect ratio is not required and resizing to exactly the given dimensions is required, then the forceSize method can be used in place of the size method:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
return Thumbnails.of(img).forceSize(newW, newH).asBufferedImage();
}
Using the Image.getScaledInstance method will not guarantee that the aspect ratio of the original image will be maintained for the resized image, and furthermore, it is in general very slow.
Thumbnailator uses a technique to progressively resize the image which can be several times faster than Image.getScaledInstance while achieving an image quality which generally is comparable.
Disclaimer: I am the maintainer of this library.
Here's some code that I have used to resize bufferedimages, no frills, pretty quick:
public static BufferedImage scale(BufferedImage src, int w, int h)
{
BufferedImage img =
new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int x, y;
int ww = src.getWidth();
int hh = src.getHeight();
int[] ys = new int[h];
for (y = 0; y < h; y++)
ys[y] = y * hh / h;
for (x = 0; x < w; x++) {
int newX = x * ww / w;
for (y = 0; y < h; y++) {
int col = src.getRGB(newX, ys[y]);
img.setRGB(x, y, col);
}
}
return img;
}
This class resize from a file and get the format name:
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import org.apache.commons.io.IOUtils;
public class ImageResizer {
public static void main(String as[]) throws IOException{
File f = new File("C:/Users/samsungrob/Desktop/shuttle.jpg");
byte[] ba = resize(f, 600, 600);
IOUtils.write(ba, new FileOutputStream( new File("C:/Users/samsungrob/Desktop/shuttle_resized.jpg") ) );
}
public static byte[] resize(File file,
int maxWidth, int maxHeight) throws IOException{
int scaledWidth = 0, scaledHeight = 0;
BufferedImage img = ImageIO.read((ImageInputStream) file );
scaledWidth = maxWidth;
scaledHeight = (int) (img.getHeight() * ( (double) scaledWidth / img.getWidth() ));
if (scaledHeight> maxHeight) {
scaledHeight = maxHeight;
scaledWidth= (int) (img.getWidth() * ( (double) scaledHeight/ img.getHeight() ));
if (scaledWidth > maxWidth) {
scaledWidth = maxWidth;
scaledHeight = maxHeight;
}
}
Image resized = img.getScaledInstance( scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
BufferedImage buffered = new BufferedImage(scaledWidth, scaledHeight, Image.SCALE_REPLICATE);
buffered.getGraphics().drawImage(resized, 0, 0 , null);
String formatName = getFormatName( file ) ;
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(buffered,
formatName,
out);
return out.toByteArray();
}
private static String getFormatName(ImageInputStream iis) {
try {
// Find all image readers that recognize the image format
Iterator iter = ImageIO.getImageReaders(iis);
if (!iter.hasNext()) {
// No readers found
return null;
}
// Use the first reader
ImageReader reader = (ImageReader)iter.next();
// Close stream
iis.close();
// Return the format name
return reader.getFormatName();
} catch (IOException e) {
}
return null;
}
private static String getFormatName(File file) throws IOException {
return getFormatName( ImageIO.createImageInputStream(file) );
}
private static String getFormatName(InputStream is) throws IOException {
return getFormatName( ImageIO.createImageInputStream(is) );
}
}
This is a shortened version of what is actually happening in imgscalr, if you just want to use the "balanced" smoothing:
/**
* Takes a BufferedImage and resizes it according to the provided targetSize
*
* #param src the source BufferedImage
* #param targetSize maximum height (if portrait) or width (if landscape)
* #return a resized version of the provided BufferedImage
*/
private BufferedImage resize(BufferedImage src, int targetSize) {
if (targetSize <= 0) {
return src; //this can't be resized
}
int targetWidth = targetSize;
int targetHeight = targetSize;
float ratio = ((float) src.getHeight() / (float) src.getWidth());
if (ratio <= 1) { //square or landscape-oriented image
targetHeight = (int) Math.ceil((float) targetWidth * ratio);
} else { //portrait image
targetWidth = Math.round((float) targetHeight / ratio);
}
BufferedImage bi = new BufferedImage(targetWidth, targetHeight, src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); //produces a balanced resizing (fast and decent quality)
g2d.drawImage(src, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return bi;
}
try the imgscalr library. Best lib i found- very fast, good quality and simple to use
BufferedImage thumbnail = Scalr.resize(image, 150);
deprecated link: http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/
Apache 2 License
Check this out, it helps:
BufferedImage bImage = ImageIO.read(new File(C:\image.jpg);
BufferedImage thumbnail = Scalr.resize(bImage, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_WIDTH,
750, 150, Scalr.OP_ANTIALIAS);