I have an image and I am extracting a subimage to feed to my neural network. I am attempting to calculate the average output of all the subimages in that same neighborhood.
So if I have an original image (m X n pixels) and I found a subimage at (sub_x, sub_y) with size (sub_width and sub_height), I also need to extract subimages with the same size (sub_width and sub_height) at (sub_x + m, sub_y + n) where m and n go from 1 - 3.
I already have a working solution:
for (int subX = (x-3); subX < (x+4); subX++)
for (int subY = (y-3); subY < (y+4); subY++)
if ( (subX > 0) && (subY > 0) )
if ( ((subX + width) < img.getWidth()) && ((subY + height) < img.getHeight()) ){
counter++;
testingImage = img.getSubimage(subX, subY, width, height);
}
x,y, width, and height are all integers of the original subimage I found.
img is the original full sized BufferedImage.
I'm not too happy about the performance though. Is there a faster/smarter way to do this?
Here's one simple thing you can do: get rid of those conditions inside loops. Calculate your ranges first, just once and then run the loops without range checks.
int subXStart = max(x-3, 0);
int subYStart = max(y-3, 0);
int subXEnd = min(x+4, img.getWidth() - width);
int subYEnd = min(y+4, img.getHeight() - height);
for (int subX = subXStart; subX < subXEnd; subX++) {
for (int subY = subYStart; subY < subYEnd; subY++) {
counter++;
testingImage = img.getSubimage(subX, subY, width, height);
// run your neural net
}
}
You can also try switching the order of loops. The order that matches the memory ordering should be considerably faster.
Related
I am working with Camera2 API and want to detect captured image is blurry or clear, i used OpenCV for this but result is not satisfactory and it increases APK size 3 times, So is there any way to detect blurry?
Measuring image focus/blur involves iterating of the pixels of the bitmap, or at least a portion thereof.
While you don't need OpenCV to iterate over the pixels of a bitmap on Android, its not for the faint of heart. Doing so in a performant way would require you to drop into JNI native code, or perhaps a technology like RenderScript, as iterating over pixels in Java or Kotlin might prove too slow.
There are many algorithms and techniques for measuring focus, or sharpness, or contrast, this is one I've used with reasonable success.
Luma is the luminosity of a pixel, i.e. grayscale pixel value. You'll want to convert each pixel to a grayscale value for this focus measure. e.g. using the NTSC formula:
pixelLuma = (red * 0.299) + (green * 0.587) + (blue * 0.114)
Here is a suggested formula to measure focus score:
FocusScore = Max({Video_Gradient}) / {Gray_Level_Dynamic_Range} * {Pixel_Pitch}
Max{Video_Gradient} = a measure of the maximum luminosity difference between adjacent pixels (x,y) across the bitmap.
e.g.:
horizontally measure pixel[x] - pixel[x+1]
vertically measure pixel[y] - pixel[y+1]
{Gray_Level_Dynamic_Range} = difference between average of N lightest pixels and N darkest pixels across the bitmap. A typical value for N is 64, in my case working on images around 1200w x 500h. Smaller images should use smaller N.
{Pixel_Pitch} = 1 / DPI = 1/200 = 0.005
This will result in a score, higher values are more in focus. You can determine a reasonable threshold.
Here is a code snippet written in C:
width = width of bitmap
height = height of bitmap
pixels = an array of bytes of size (width * height) holding pixel luminosity values
VFOCUS_N = 64
int gradientHorizontal[256];
int *pGradientHorizontal = gradientHorizontal;
int gradientVertical[256];
int *pGradientVertical = gradientVertical;
int luminanceHistogram[256];
int *pLuminance = luminanceHistogram;
int maxGradient = 0;
for (int i = 0;i < 256;i++)
{
gradientHorizontal[i] = 0;
gradientVertical[i] = 0;
luminanceHistogram[i] = 0;
}
// pixel by pixel math...
for (nRow = 0; nRow < height-1; nRow++)
{
nRowOffset = nRow * width;
nNextRowOffset = (nRow+1) * width;
for (nCol = 0; nCol < width-1; nCol++)
{
int gC = pixels[nRowOffset + nCol];
int gH = abs(gC - pixels[nRowOffset + nCol + 1]);
int gV = abs(gC - pixels[nNextRowOffset + nCol]);
pLuminance[gC]++;
pGradientHorizontal[gH]++;
pGradientVertical[gV]++;
}
}
// find max gradient
for (int i = 255;i >= 0;i--)
{
// first one with a value
if ((gradientHorizontal[i] > 0) || (gradientVertical[i] > 0))
{
maxGradient = i;
break;
}
}
// calculate dynamic range
int rangeLow = 0;
int rangeHi = 0;
int p;
p = 0;
for (int i = 0;i < 256;i++)
{
if (luminanceHistogram[i] > 0)
{
if (p + luminanceHistogram[i] > VFOCUS_N)
{
rangeLow += (i * (VFOCUS_N - p));
p = VFOCUS_N;
break;
}
p += luminanceHistogram[i];
rangeLow += (i * luminanceHistogram[i]);
}
}
if (p)
rangeLow /= p;
p = 0;
for (int i = 255;i >= 0;i--)
{
if (luminanceHistogram[i] > 0)
{
if (p + luminanceHistogram[i] > VFOCUS_N)
{
rangeHi += (i * (VFOCUS_N - p));
p = VFOCUS_N;
break;
}
p += luminanceHistogram[i];
rangeHi += (i * luminanceHistogram[i]);
}
}
if (p)
rangeHi /= p;
float mFocusScore = (float)fmin((float)maxGradient / (fabs((float)rangeHi - (float)rangeLow) * 0.005), 100.00);
Low focus scores means a blurry image. Values close to or in excess of 100 indicate a sharp image, the code above caps the score at 100.
I'm building an application that uses OCR to read text from an image (using Tess4J for Google's Tesseract), but I want to ignore the tan-colored text and only read the grey.
In the image below, for instance, I only want to read "Ricki" and ignore "AOA".
http://i.imgur.com/daCuTbB.png
To accomplish this, I figured removing the tan color from the image before performing OCR was my best option.
/* Remove RGB Value for Group Tag */
int width = image.getWidth();
int height = image.getHeight();
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
//If pixel is between dark-tan value and light-tan value
if (pixels[i] > 0xFF57513b && pixels[i] < 0xFF6b6145) {
// Set pixel to black
System.out.println("pixel found");
pixels[i] = 0xFF000000;
}
}
image.setRGB(0, 0, width, height, pixels, 0, width);
But this code removes almost all of the grey text as well. You aren't able to simply compare hex color values for a range of values the way I have. Is there another way to approach detecting a range of colors? Or a better different approach to this problem?
haraldK pointed me in the right direction by mentioning converting RGB. Bit shifting to get individual r, g, and b int values from the hex value allowed me to compare the color within a range and black out a range of colors from the image.
int baser = 108; //base red
int baseg = 96; //base green
int baseb = 68; //base blue
int range = 10; //threshold + and - from base values
/* Set all pixels within +- range of base RGB to black */
for (int i = 0; i < pixels.length; i++) {
int a = (pixels[i]>>24) &0xFF; //alpha
int r = (pixels[i]>>16) &0xFF; //red
int g = (pixels[i]>>8) &0xFF; //green
int b = (pixels[i]>>0) &0xFF; //blue
if ( (r > baser-range && r < baser+range) &&
(g > baseg-range && g < baseg+range) &&
(b > baseb-range && b < baseb+range) ) {
pixels[i] = 0xFF000000; //Set to black
}
}
I have been business a histogram equalisation method. I've used this question as a foundation to build on. However I cannot get this code to run and Google isn't too helpful in helping me find the issue. I pass in a JPG BufferedImage object. I first display the image so I see what I'm working with and then process it. However it ALWAYS fails on the line int valueBefore=img.getRaster().getPixel(x, y,iarray)[0]; and I'm not sure why. The error I get is Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1 but I cannot see why it gives this error, the picture is there and filled with pixels!
public BufferedImage hisrogramNormatlisation(BufferedImage img) {
// To view image we're working on
JFrame frame = new JFrame();
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(new JLabel(new ImageIcon(img)));
frame.pack();
frame.setVisible(true);
int width =img.getWidth();
int height =img.getHeight();
int anzpixel= width*height;
int[] histogram = new int[255];
int[] iarray = new int[1];
int i =0;
// Create histogram
for (int x = 50; x < width; x++) {
for (int y = 50; y < height; y++) {
int valueBefore=img.getRaster().getPixel(x, y,iarray)[0];
histogram[valueBefore]++;
System.out.println("here");
}
}
int sum = 0;
float[] lut = new float[anzpixel];
for ( i=0; i < 255; ++i )
{
sum += histogram[i];
lut[i] = sum * 255 / anzpixel;
}
i=0;
for (int x = 1; x < width; x++) {
for (int y = 1; y < height; y++) {
int valueBefore=img.getRaster().getPixel(x, y,iarray)[0];
int valueAfter= (int) lut[valueBefore];
iarray[0]=valueAfter;
img.getRaster().setPixel(x, y, iarray);
i=i+1;
}
}
return img;
}
Error description:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at java.awt.image.ComponentSampleModel.getPixel(ComponentSampleModel.java:n)
at java.awt.image.Raster.getPixel(Raster.java:n)
at MainApp.hisrogramNormatlisation(MainApp.java: * line described *)
at MainApp.picture(MainApp.java:n)
at MainApp.<init>(Main.java:n)
at MainApp.main(Main.java:n)
The stack trace you posted says your out of range index is 1.
The exception isn't thrown where you think it is.
getPixel(int x, int y, int[] iarray) fills iarray with the intensity values of the pixel. If you are using an rgb image, there will be at least three intensity values for each channel, if you are using rgb with alpha there will be 4 intensity values. Your iarray is just of size 1, so when raster wants to access further elements to store the additional values an IndexOutOfBoundsException is thrown.
Increase the size of iarray and the exception will be gone.
Don't use getPixel(), but getSample().
So your code would be: final int valueBefore = img.getRaster().getSample(x, y, 0) ; or even histogram[img.getRaster().getSample(x, y, 0)]++ ;
Btw, you may want to check the image type first in order to determine the number of channels/bands and do this process for each channel.
I have a list of vertices and a list of regions (which are square/rectangle) shaped. Vertex has x and y coordinates, and a region has (x, y, height and width). How can I efficiently check which vertex lies in which region for every vertex/region?
EDIT:
This is the code I wrote to do this.
if (!g.getVertices().isEmpty()) {
for (int i = 0; i < g.getVertices().size(); i++) {
Vertex v = g.getVertices().get(i);
Point vertexPoint = new Point(v.getX(), v.getY());
for (int j = 0; j < g.getNumberOfRegions(); j++) {
int x = g.getRegions().get(j).getX();
int y = g.getRegions().get(j).getY();
int height = g.getRegions().get(j).getHeight();
int width = g.getRegions().get(j).getWidth();
Grid regionGrid = new Grid(j+1, x, y, height, width);
Rectangle regionRectangle = new Rectangle(x, y, height, width);
if (regionRectangle.contains(vertexPoint)) {
System.out.println("Vertex " + v + " lies inside region " + regionGrid.getRegionID());
}
}
}
}
EDIT 2: I used this to generate the regions, but I need a way to assign each region in the grid a regionID from left to right. For example:
1 - 2 - 3
4 - 5 - 6
7 - 8 - 9
for a 3x3 grid. At the moment it is in the following form:
1 - 1 - 1
2 - 2 - 2
3 - 3 - 3
for (int i = 0; i < rowValue; i++) {
for (int j = 0; j < columnValue; j++) {
Grid r = new Grid(0, 20 + i * size, 20 + j * size, size, size);
r.setRegionID(j + 1);
g.addRegion(r);
}
}
checking if a vertex is inside a square or a circle can be done in O(1). you can do it with library function or elementary math. so the works algorithm you can create is O(#vertices * #regions). you can try to optimise by sorting the vertices and regions by X-axis and then by Y-axis and try to eliminate checking that for sure return false. but seems that in pessimistic scenario you will still have O(#vertices * #regions) time.
You can probably use the Core Java libraries itself:
List<Rectangle2D.Double> rectangles = Arrays.asList(
new Rectangle2D.Double(0d, 0d, 100d, 100d),
new Rectangle2D.Double(100d, 0d, 100d, 100d),
new Rectangle2D.Double(0d, 100d, 100d, 100d),
new Rectangle2D.Double(100d, 100d, 100d, 100d));
Point2D.Double aPoint = new Point2D.Double(30d, 40d);
for (Rectangle2D.Double rectangle:rectangles){
if (rectangle.contains(aPoint)){
System.out.println(rectangle + " has the point " + aPoint);
}
}
Working with plane geometry is extremely easy while using JTS. You can try convert the objects you are using to JTS-specific.
I am working on a game in Android and I have Bitmap objects being drawn. They are rotated using the Matrix class. The problem I am having is being able to access the pixels of the rotated Bitmap. When I access them after rotation the pixels are still representing the unrotated version. Does anyone have any ideas? Maybe I can rotate the pixel array based on an arbitrary number of degrees? I have seen solutions that create a new bitmaps on the fly based on the newly rotated images, but I can't do this because of performance issues. Thanks!
Here is how I draw the bitmap currently (The bitmap is drawn using the matrix as its position)...
canvas.drawBitmap(bitmapPlayer, positionMatrix, null);
Here is how I create the positionMatrix variable...
Matrix m = new Matrix();
m.postRotate(degrees, bitmapCenterX, bitmapCenterY);
positionMatrix.set(m);
Here is how I access the pixels currently (This accesses the pixel array I created below)...
getPixel(x, y);
Here is how I build the pixel array that I have tried to modify...
// Build pixel 2d array
pixelData = new int[bitmapPlayer.getWidth()][bitmapPlayer.getHeight()];
for(int x=0; x<bitmapPlayer.getWidth(); x++) {
for(int y=0; y<bitmapPlayer.getHeight(); y++) {
pixelData[x][y] = bitmapPlayer.getPixel(x, y);
}
}
Here is how I have tried to manipulate the array...
public void rotatePixels(int degrees) {
int[][] newPixelData = new int[bitmapPlayer.getWidth()][bitmapPlayer.getHeight()];
double cos = Math.cos(Math.toRadians(degrees));
double sin = Math.sin(Math.toRadians(degrees));
for(int x=0; x<bitmapPlayer.getWidth(); x++) {
for(int y=0; y<bitmapPlayer.getHeight(); y++) {
if(pixelData[x][y] != 0) {
int currentX = x + getTopLeftX();
int currentY = y + getTopLeftY();
int nextX = (int)((currentX * cos) - (currentY * sin));
int nextY = (int)((currentX * sin) + (currentY * cos));
if(nextX >= 0 && nextX < bitmapPlayer.getWidth() && nextY >= 0 && nextY < bitmapPlayer.getHeight()) {
newPixelData[nextX][nextY] = 1;
}
}
}
}
this.pixelData = newPixelData;
}
Have you tried:
Matrix mat = new Matrix();
mat.postRotate(45);
Bitmap newBm = Bitmap.createBitmap(bitmapPlayer, 0, 0, bitmapPlayer.width(), bitmapPlayer.height(), mat, true);
and then accessing the pixels of the new Bitmap?
I think right now you just draw a rotated bitmap on the canvas, you're not actually rotating the bitmap
EDIT:
The way you do it in your orinigal post won't work because you start at (0,0) and work your way down the left column... Depending on the rotation degree you'd have to start at a different place and then go down a different column. i.e. a small ctrclkwise rotation and you start at the top right index.
Bitmap bm = someImage;
int startx, starty;
int degree = rotateDegree % 360; // counterclockwise
if (degree >= 0 && degree < 90) {
startx = bm.getWidth();
starty = 0;
} else if (degree >= 90 && degree < 180) {
startx = bm.getWidth();
starty = bm.getWidth();
} else if (degree >= 180 && degree < 270) {
startx = 0();
starty = bm.getWidth();
} else {
startx = 0;
starty = 0;
}
And then try traversing the image that way, starting at (startx, starty)
and adding or subtracting the correct angle (you can use some boolean to keep track
of whether you're adding or subtracting from x or y respectively). Let me know if this works,
and if it doesn't can you be a little more specific about where you think the problem might be?