I'm creating a app that gets a image from camera (using CameraKit library), process the image and do a OCR Read using Google Vision Api, And get this error:
FATAL EXCEPTION: main
Process: com.., PID: 1938
java.lang.OutOfMemoryError: Failed to allocate a 63701004 byte
allocation with 16777216 free bytes and 60MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:905)
at android.graphics.Bitmap.createBitmap(Bitmap.java:882)
at android.graphics.Bitmap.createBitmap(Bitmap.java:849)
at
com.****.****.Reader.ReaderResultActivity.createContrast(ReaderResultActivity.java:123)
at
com.*****.****.Reader.ReaderResultActivity.onCreate(ReaderResultActivity.java:47)
at android.app.Activity.performCreate(Activity.java:6672)
at
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1140)
at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2612)
at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2724)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1473)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
ReaderResultActivity Code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_reader_result);
ImageView img1 = (ImageView)findViewById(R.id.imageView2);
ImageView img2 = (ImageView)findViewById(R.id.imageView3);
ImageView img3 = (ImageView)findViewById(R.id.imageView4);
TextView scanResults = (TextView)findViewById(R.id.textView);
//Get bitmap from a static class.
Bitmap bitmap = Reader.img;
Bitmap grayScale = toGrayscale(bitmap);
Bitmap blackWhiteImage = createContrast(grayScale, 50);
Bitmap invertColor = invertColor(blackWhiteImage);
//Show process steps
img1.setImageBitmap(grayScale);
img2.setImageBitmap(blackWhiteImage);
img3.setImageBitmap(invertColor);
TextRecognizer detector = new TextRecognizer.Builder(getApplicationContext()).build();
try {
if (detector.isOperational()) {
Frame frame = new Frame.Builder().setBitmap(invertColor).build();
SparseArray<TextBlock> textBlocks = detector.detect(frame);
String blocks = "";
String lines = "";
String words = "";
for (int index = 0; index < textBlocks.size(); index++) {
//extract scanned text blocks here
TextBlock tBlock = textBlocks.valueAt(index);
blocks = blocks + tBlock.getValue() + "\n" + "\n";
for (Text line : tBlock.getComponents()) {
//extract scanned text lines here
lines = lines + line.getValue() + "\n";
for (Text element : line.getComponents()) {
//extract scanned text words here
words = words + element.getValue() + ", ";
}
}
}
if (textBlocks.size() == 0) {
scanResults.setText("Scan Failed: Found nothing to scan");
} else {
lines = lines.replaceAll("o", "0");
lines = lines.replaceAll("A", "1");
scanResults.setText(lines + "\n");
}
} else {
scanResults.setText("Could not set up the detector!");
}
} catch (Exception e) {
Toast.makeText(this, "Failed to load Image", Toast.LENGTH_SHORT)
.show();
Log.e("312", e.toString());
}
}
private Bitmap processImage(Bitmap bitmap){
Bitmap grayScale = toGrayscale(bitmap);
Bitmap blackWhiteImage = createContrast(grayScale, 50);
Bitmap invertColor = invertColor(blackWhiteImage);
return invertColor;
}
public Bitmap toGrayscale(Bitmap bmpOriginal) {
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, bmpOriginal.getConfig());
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 createContrast(Bitmap src, double value) {
// image size
int width = src.getWidth();
int height = src.getHeight();
// create output bitmap
Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
// color information
int A, R, G, B;
int pixel;
// get contrast value
double contrast = Math.pow((100 + value) / 100, 2);
// scan through all pixels
for(int x = 0; x < width; ++x) {
for(int y = 0; y < height; ++y) {
// get pixel color
pixel = src.getPixel(x, y);
A = Color.alpha(pixel);
// apply filter contrast for every channel R, G, B
R = Color.red(pixel);
R = (int)(((((R / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
if(R < 0) { R = 0; }
else if(R > 255) { R = 255; }
G = Color.red(pixel);
G = (int)(((((G / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
if(G < 0) { G = 0; }
else if(G > 255) { G = 255; }
B = Color.red(pixel);
B = (int)(((((B / 255.0) - 0.5) * contrast) + 0.5) * 255.0);
if(B < 0) { B = 0; }
else if(B > 255) { B = 255; }
// set new pixel color to output bitmap
bmOut.setPixel(x, y, Color.argb(A, R, G, B));
}
}
return bmOut;
}
Bitmap invertColor(Bitmap src){
Bitmap copy = src.copy(src.getConfig(), true);
for (int x = 0; x < copy.getWidth(); ++x) {
for (int y = 0; y < copy.getHeight(); ++y) {
int color = copy.getPixel(x, y);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
int avg = (r + g + b) / 3;
int newColor = Color.argb(255, 255 - avg, 255 - avg, 255 - avg);
copy.setPixel(x, y, newColor);
}
}
return copy;
}
Already try to do this in Manifest
android:largeHeap="true"
But the application just stop running when is on:
ReaderResultActivity.createContrast(ReaderResultActivity.java:123)
The same line that appears on error without the "largeHeap" tag.
Just dont know what to do, but i think that has something with all those "Bitmap.CreateBitmap" in every process function.
But without doing this, in OCR reading, appear a error saying that the bitmap has a wrong format.
You are loading three bitmaps in different imageviews without scaling it according to the size you want to show on your UI.
Android devices's camera captures pictures with much higher resolution than the screen density of your device.
Given that you are working with limited memory, ideally you only want to load a lower resolution version in memory. The lower resolution version should match the size of the UI component that displays it. An image with a higher resolution does not provide any visible benefit, but still takes up precious memory and incurs additional performance overhead due to additional on the fly scaling.
You can optimize it by following developer documentation suggestions - https://developer.android.com/topic/performance/graphics/load-bitmap.html
Related
I have an org.eclipse.swt.graphics.Image, loaded from a PNG, and want to scale it in high quality (antialiasing, interpolation). But I do not want to lose transparency and get just a white background. (I need this Image to put it on an org.eclipse.swt.widgets.Label .)
Does anybody know how to do that?
Thank you!
Based on Mark's answer I found a better solution without the "hacky bit": first copy the alphaData from the origin then use GC to scale the image.
public static Image scaleImage(final Device device, final Image orig, final int scaledWidth, final int scaledHeight) {
final Rectangle origBounds = orig.getBounds();
if (origBounds.width == scaledWidth && origBounds.height == scaledHeight) {
return orig;
}
final ImageData origData = orig.getImageData();
final ImageData destData = new ImageData(scaledWidth, scaledHeight, origData.depth, origData.palette);
if (origData.alphaData != null) {
destData.alphaData = new byte[destData.width * destData.height];
for (int destRow = 0; destRow < destData.height; destRow++) {
for (int destCol = 0; destCol < destData.width; destCol++) {
final int origRow = destRow * origData.height / destData.height;
final int origCol = destCol * origData.width / destData.width;
final int o = origRow * origData.width + origCol;
final int d = destRow * destData.width + destCol;
destData.alphaData[d] = origData.alphaData[o];
}
}
}
final Image dest = new Image(device, destData);
final GC gc = new GC(dest);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
gc.drawImage(orig, 0, 0, origBounds.width, origBounds.height, 0, 0, scaledWidth, scaledHeight);
gc.dispose();
return dest;
}
This way we don't have to make assumptions about the underlying ImageData.
Using a method described by Sean Bright here: https://stackoverflow.com/a/15685473/6245535, we can extract the alpha information from the image and use it to fill the ImageData.alphaData array which is responsible for the transparency:
public static Image resizeImage(Display display, Image image, int width, int height) {
Image scaled = new Image(display, width, height);
GC gc = new GC(scaled);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0, 0, width, height);
gc.dispose();
ImageData canvasData = scaled.getImageData();
canvasData.alphaData = new byte[width * height];
// This is the hacky bit that is making assumptions about
// the underlying ImageData. In my case it is 32 bit data
// so every 4th byte in the data array is the alpha for that
// pixel...
for (int idx = 0; idx < (width * height); idx++) {
int coord = (idx * 4) + 3;
canvasData.alphaData[idx] = canvasData.data[coord];
}
// Now that we've set the alphaData, we can create our
// final image
Image finalImage = new Image(display, canvasData);
scaled.dispose();
return finalImage;
}
Note that this method assumes that you are working with 32 bit depth of color; it won't work otherwise.
I'm writing a fairly simple app that will, in real-time, tell the user how many pixels there are above a certain color value in an image.
That is, it takes preview images from the camera and analyses them as the user move the camera around.
Right now, I have this code, which technically works:
mRgba = inputFrame.rgba();
Rect sample = new Rect();
Mat sampleRegionRgba;
numPixs = 0;
boundary.add(100); boundary.add(100);boundary.add(100);
int cols = mRgba.cols();
int rows = mRgba.rows();
double yLow = (double)mOpenCvCameraView.getHeight() * 0.2401961;
double yHigh = (double)mOpenCvCameraView.getHeight() * 0.7696078;
double xScale = (double)cols / (double)mOpenCvCameraView.getWidth();
double yScale = (double)rows / (yHigh-yLow);
int tmpX;
int tmpY;
for (int x = 0; x < cols-6; x++) {
for (int y = (int)yLow; y < yHigh-6; y++){
tmpX = (int)((double)x * xScale);
tmpY = (int)((double)y * yScale);
sample.x = tmpX+3;
sample.y = tmpY+3;
sample.width = 2;
sample.height = 2;
sampleRegionRgba = mRgba.submat(sample);
Mat sampleRegionHsv = new Mat();
Imgproc.cvtColor(sampleRegionRgba, sampleRegionHsv, Imgproc.COLOR_RGB2HSV_FULL);
mBlobColorHsv = Core.sumElems(sampleRegionHsv);
int pointCount = sample.width * sample.height;
for (int i = 0; i < mBlobColorHsv.val.length; i++){
mBlobColorHsv.val[i] /= pointCount;
}
mBlobColorRgba = convertScalarToRgba(mBlobColorHsv);
// System.out.println(mBlobColorRgba.toString());
if (mBlobColorRgba.val[0] > boundary.get(0)
&& mBlobColorRgba.val[1] > boundary.get(1)
&& mBlobColorRgba.val[2] > boundary.get(2)){
numPixs += 1;
}
// System.out.println(sampleRegionRgba.toString());
}
}
System.out.println("number of pixels above boundary: "+Integer.toString(numPixs));
massflow = m*(Math.pow(numPixs,.25))+b;
runOnUiThread(new Runnable() {
#Override
public void run() {
massflow_text.setText("Massflow: "+Double.valueOf(massflow));
}
});
While this code works, it takes about 6 seconds to run for each image.
I'd like it to have a much more reasonable frame rate. I know this can be done with numpy (I've done it with np.where()). Is it possible with Java/OpenCv/Android Studio ?
How convert Image obj to Bitmap obj and vice versa?
I have a method that get Image object input and return Image object but i want give bitmap object input and then get bitmap object output my code is this:
public Image edgeFilter(Image imageIn) {
// Image size
int width = imageIn.getWidth();
int height = imageIn.getHeight();
boolean[][] mask = null;
Paint grayMatrix[] = new Paint[256];
// Init gray matrix
for (int i = 0; i <= 255; i++) {
Paint p = new Paint();
p.setColor(Color.rgb(i, i, i));
grayMatrix[i] = p;
}
int [][] luminance = new int[width][height];
for (int y = 0; y < height ; y++) {
for (int x = 0; x < width ; x++) {
if(mask != null && !mask[x][y]){
continue;
}
luminance[x][y] = (int) luminance(imageIn.getRComponent(x, y), imageIn.getGComponent(x, y), imageIn.getBComponent(x, y));
}
}
int grayX, grayY;
int magnitude;
for (int y = 1; y < height-1; y++) {
for (int x = 1; x < width-1; x++) {
if(mask != null && !mask[x][y]){
continue;
}
grayX = - luminance[x-1][y-1] + luminance[x-1][y-1+2] - 2* luminance[x-1+1][y-1] + 2* luminance[x-1+1][y-1+2] - luminance[x-1+2][y-1]+ luminance[x-1+2][y-1+2];
grayY = luminance[x-1][y-1] + 2* luminance[x-1][y-1+1] + luminance[x-1][y-1+2] - luminance[x-1+2][y-1] - 2* luminance[x-1+2][y-1+1] - luminance[x-1+2][y-1+2];
// Magnitudes sum
magnitude = 255 - Image.SAFECOLOR(Math.abs(grayX) + Math.abs(grayY));
Paint grayscaleColor = grayMatrix[magnitude];
// Apply the color into a new image
imageIn.setPixelColor(x, y, grayscaleColor.getColor());
}
}
return imageIn;
}
If you want to convert an Image object to a Bitmap and the format has been selected as JPEG, then you can accomplish this by using the following code (if it is not a JPEG, then additional conversions will be needed):
...
if(image.getFormat() == ImageFormat.JPEG)
{
ByteBuffer buffer = capturedImage.getPlanes()[0].getBuffer();
byte[] jpegByteData = new byte[buffer.remaining()];
Bitmap bitmapImage = BitmapFactory.decodeByteArray(jpegByteData, 0, jpegByteData.length, null);
}
...
This link gives more into on saving images as a png format.
it is difficult to see what you are attempting to do, are you trying to alter this code so it also works for bitmap formats?
here is a answer of someone doing stuff with bitmap images, should be give you a idea of what other people do
after a deep search i can not understand why my result image is not what I am expecting compared to the one from wikipedia - sobel operator using the same kernel for Sobel operator.
http://s29.postimg.org/kjex7dx6f/300px_Valve_original_1.png
http://s14.postimg.org/vxhvffm29/Untitled.png
So, I have a button listener that load a bmp image, apply Sobel and display an ImageIcon
There is the code :
javax.swing.JFileChooser choose = new javax.swing.JFileChooser();
choose.setFileFilter(new DoFileFilter(".bmp"));
int returnVal = choose.showOpenDialog(this);
if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) {
try {
java.io.FileInputStream imgis = null;
// System.out.println("Ai ales fisierul : " +
// choose.getSelectedFile());
String path = choose.getSelectedFile().toString();
Path.setText(path);
imgis = new java.io.FileInputStream(path);
java.awt.image.BufferedImage img = javax.imageio.ImageIO.read(imgis);
DirectImgToSobel ds = new DirectImgToSobel(img);
javax.swing.ImageIcon image;
image = new javax.swing.ImageIcon(ds.getBuffImg());
ImgPrev.setIcon(image);
javax.swing.JFrame frame = (javax.swing.JFrame) javax.swing.SwingUtilities.getWindowAncestor(jPanel1);
frame.pack();
frame.repaint();
} catch (FileNotFoundException ex) {
Logger.getLogger(Display.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Display.class.getName()).log(Level.SEVERE, null, ex);
}
}
And Sobel class :
public class DirectImgToSobel {
private final java.awt.image.BufferedImage img;
private java.awt.image.BufferedImage buffimg;
private int[][]
sobel_x = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } },
sobel_y = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };
public DirectImgToSobel() {
this.img = null;
}
public DirectImgToSobel(java.awt.image.BufferedImage img) {
this.img = img;
aplicaFiltru();
}
private void aplicaFiltru() {
this.buffimg = new java.awt.image.BufferedImage(this.img.getWidth(), this.img.getHeight(),
java.awt.image.BufferedImage.TYPE_BYTE_GRAY);
for (int x = 1; x < this.img.getWidth() - 1; x++) {
for (int y = 1; y < this.img.getHeight() - 1; y++) {
int pixel_x =
(sobel_x[0][0] * img.getRGB(x-1,y-1)) + (sobel_x[0][1] * img.getRGB(x,y-1)) + (sobel_x[0][2] * img.getRGB(x+1,y-1)) +
(sobel_x[1][0] * img.getRGB(x-1,y)) + (sobel_x[1][1] * img.getRGB(x,y)) + (sobel_x[1][2] * img.getRGB(x+1,y)) +
(sobel_x[2][0] * img.getRGB(x-1,y+1)) + (sobel_x[2][1] * img.getRGB(x,y+1)) + (sobel_x[2][2] * img.getRGB(x+1,y+1));
int pixel_y =
(sobel_y[0][0] * img.getRGB(x-1,y-1)) + (sobel_y[0][1] * img.getRGB(x,y-1)) + (sobel_y[0][2] * img.getRGB(x+1,y-1)) +
(sobel_y[1][0] * img.getRGB(x-1,y)) + (sobel_y[1][1] * img.getRGB(x,y)) + (sobel_y[1][2] * img.getRGB(x+1,y)) +
(sobel_y[2][0] * img.getRGB(x-1,y+1)) + (sobel_y[2][1] * img.getRGB(x,y+1)) + (sobel_y[2][2] * img.getRGB(x+1,y+1));
this.buffimg.setRGB(x, y, (int) Math.sqrt(pixel_x * pixel_x + pixel_y * pixel_y));
}
}
buffimg = thresholdImage(buffimg, 28);
java.awt.Graphics g = buffimg.getGraphics();
g.drawImage(buffimg, 0, 0, null);
g.dispose();
}
public java.awt.image.BufferedImage getBuffImg() {
return this.buffimg;
}
public static java.awt.image.BufferedImage thresholdImage(java.awt.image.BufferedImage image, int threshold) {
java.awt.image.BufferedImage result = new java.awt.image.BufferedImage(image.getWidth(), image.getHeight(),
java.awt.image.BufferedImage.TYPE_BYTE_GRAY);
result.getGraphics().drawImage(image, 0, 0, null);
java.awt.image.WritableRaster raster = result.getRaster();
int[] pixels = new int[image.getWidth()];
for (int y = 0; y < image.getHeight(); y++) {
raster.getPixels(0, y, image.getWidth(), 1, pixels);
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] < threshold)
pixels[i] = 0;
else
pixels[i] = 255;
}
raster.setPixels(0, y, image.getWidth(), 1, pixels);
}
return result;
}
}
For obtain same result as in Wikipedia you have to do:
Use brightness of image point instead of colors packed to single int that returns getRGB.
Normalize result (map low values to black and high values to white).
EDIT: I accidentally found good article about Sobel filters in Java: http://asserttrue.blogspot.ru/2010/08/smart-sobel-image-filter.html
EDIT2: Check this How to convert get.rgb(x,y) integer pixel to Color(r,g,b,a) in Java? question described how to extract colors from image.
But my suggestion is to do float brightness = (new Color(img.getRGB(x, y))).RGBtoHSB()[2]; and apply Sobel to brightness.
About your threshold function: you should get grayscaled image, not black-and-white.
like:
if (pixels[i] < threshold) pixels[i] = 0;
else pixels[i] = (int)((pixels[i] - threshold)/(255.0 - threshold)*255.0);
But, again, rgba color representation isn't suitable for math.
Normalizing will be improved by finding minimum and maximum pixel values and stretch (min-max) range to (0-255)
change the image type from
TYPE_BYTE_GRAY to TYPE_INT_RGB
use the correct color channel do convolve
sobel_x[0][0] * new Color(img.getRGB(x-1,y-1)).getBlue()
pack the convolutioned color into bit packed RGB, and set the color
int packedRGB=(int)Math.sqrt(pixel_x*pixel_x+pixel_y*pixel_y);
packedRGB=(packedRGB << 16 | packedRGB << 8 | RGB);
this.buffimg.setRGB(x, y, packedRGB);
Convolution accepts only 1 color channel, it can be r,g,b or gray [(r+g+b)/3], and returns one color channel, thats why you have to pack it back to bit packed RGB, because BufferedImage.setColor() takes only bit-packed RGB.
My code
`
static BufferedImage inputImg,outputImg;
static int[][] pixelMatrix=new int[3][3];
public static void main(String[] args) {
try {
inputImg=ImageIO.read(new File("your input image"));
outputImg=new BufferedImage(inputImg.getWidth(),inputImg.getHeight(),TYPE_INT_RGB);
for(int i=1;i<inputImg.getWidth()-1;i++){
for(int j=1;j<inputImg.getHeight()-1;j++){
pixelMatrix[0][0]=new Color(inputImg.getRGB(i-1,j-1)).getRed();
pixelMatrix[0][1]=new Color(inputImg.getRGB(i-1,j)).getRed();
pixelMatrix[0][2]=new Color(inputImg.getRGB(i-1,j+1)).getRed();
pixelMatrix[1][0]=new Color(inputImg.getRGB(i,j-1)).getRed();
pixelMatrix[1][2]=new Color(inputImg.getRGB(i,j+1)).getRed();
pixelMatrix[2][0]=new Color(inputImg.getRGB(i+1,j-1)).getRed();
pixelMatrix[2][1]=new Color(inputImg.getRGB(i+1,j)).getRed();
pixelMatrix[2][2]=new Color(inputImg.getRGB(i+1,j+1)).getRed();
int edge=(int) convolution(pixelMatrix);
outputImg.setRGB(i,j,(edge<<16 | edge<<8 | edge));
}
}
File outputfile = new File("your output image");
ImageIO.write(outputImg,"jpg", outputfile);
} catch (IOException ex) {System.err.println("Image width:height="+inputImg.getWidth()+":"+inputImg.getHeight());}
}
public static double convolution(int[][] pixelMatrix){
int gy=(pixelMatrix[0][0]*-1)+(pixelMatrix[0][1]*-2)+(pixelMatrix[0][2]*-1)+(pixelMatrix[2][0])+(pixelMatrix[2][1]*2)+(pixelMatrix[2][2]*1);
int gx=(pixelMatrix[0][0])+(pixelMatrix[0][2]*-1)+(pixelMatrix[1][0]*2)+(pixelMatrix[1][2]*-2)+(pixelMatrix[2][0])+(pixelMatrix[2][2]*-1);
return Math.sqrt(Math.pow(gy,2)+Math.pow(gx,2));
}
`
What I'm trying to do is to compute 2D DCT of an image in Java and then save the result back to file.
Read file:
coverImage = readImg(coverPath);
private BufferedImage readImg(String path) {
BufferedImage destination = null;
try {
destination = ImageIO.read(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
return destination;
}
Convert to float array:
cover = convertToFloatArray(coverImage);
private float[] convertToFloatArray(BufferedImage source) {
securedImage = (WritableRaster) source.getData();
float[] floatArray = new float[source.getHeight() * source.getWidth()];
floatArray = securedImage.getPixels(0, 0, source.getWidth(), source.getHeight(), floatArray);
return floatArray;
}
Run the DCT:
runDCT(cover, coverImage.getHeight(), coverImage.getWidth());
private void runDCT(float[] floatArray, int rows, int cols) {
dct = new FloatDCT_2D(rows, cols);
dct.forward(floatArray, false);
securedImage.setPixels(0, 0, cols, rows, floatArray);
}
And then save it as image:
convertDctToImage(securedImage, coverImage.getHeight(), coverImage.getWidth());
private void convertDctToImage(WritableRaster secured, int rows, int cols) {
coverImage.setData(secured);
File file = new File(securedPath);
try {
ImageIO.write(coverImage, "png", file);
} catch (IOException ex) {
Logger.getLogger(DCT2D.class.getName()).log(Level.SEVERE, null, ex);
}
}
But what I get is: http://kyle.pl/up/2012/05/29/dct_stack.png
Can anyone tell me what I'm doing wrong? Or maybe I don't understand something here?
This is a piece of code, that works for me:
//reading image
BufferedImage image = javax.imageio.ImageIO.read(new File(filename));
//width * 2, because DoubleFFT_2D needs 2x more space - for Real and Imaginary parts of complex numbers
double[][] brightness = new double[img.getHeight()][img.getWidth() * 2];
//convert colored image to grayscale (brightness of each pixel)
for ( int y = 0; y < image.getHeight(); y++ ) {
raster.getDataElements( 0, y, image.getWidth(), 1, dataElements );
for ( int x = 0; x < image.getWidth(); x++ ) {
//notice x and y swapped - it's JTransforms format of arrays
brightness[y][x] = brightnessRGB(dataElements[x]);
}
}
//do FT (not FFT, because FFT is only* for images with width and height being 2**N)
//DoubleFFT_2D writes data to the same array - to brightness
new DoubleFFT_2D(img.getHeight(), img.getWidth()).realForwardFull(brightness);
//visualising frequency domain
BufferedImage fd = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
outRaster = fd.getRaster();
for ( int y = 0; y < img.getHeight(); y++ ) {
for ( int x = 0; x < img.getWidth(); x++ ) {
//we calculate complex number vector length (sqrt(Re**2 + Im**2)). But these lengths are to big to
//fit in 0 - 255 scale of colors. So I divide it on 223. Instead of "223", you may want to choose
//another factor, wich would make you frequency domain look best
int power = (int) (Math.sqrt(Math.pow(brightness[y][2 * x], 2) + Math.pow(brightness[y][2 * x + 1], 2)) / 223);
power = power > 255 ? 255 : power;
//draw a grayscale color on image "fd"
fd.setRGB(x, y, new Color(c, c, c).getRGB());
}
}
draw(fd);
Resulting image should look like big black space in the middle and white spots in all four corners. Usually people visualise FD so, that zero frequency appears in the center of the image. So, if you need classical FD (one, that looks like star for reallife images), you need to upgrade "fd.setRGB(x, y..." a bit:
int w2 = img.getWidth() / 2;
int h2 = img.getHeight() / 2;
int newX = x + w2 >= img.getWidth() ? x - w2 : x + w2;
int newY = y + h2 >= img.getHeight() ? y - h2 : y + h2;
fd.setRGB(newX, newY, new Color(power, power, power).getRGB());
brightnessRGB and draw methods for the lazy:
public static int brightnessRGB(int rgb) {
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
return (r+g+b)/3;
}
private static void draw(BufferedImage img) {
JLabel picLabel = new JLabel(new ImageIcon(img));
JPanel jPanelMain = new JPanel();
jPanelMain.add(picLabel);
JFrame jFrame = new JFrame();
jFrame.add(jPanelMain);
jFrame.pack();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
I know, I'm a bit late, but I just did all that for my program. So, let it be here for those, who'll get here from googling.