i am writing a program that will read the element in a convolute and the program will then multiple each element in the convolute with the kernel given. I will need to take the convolute array from other class as well as kernel.
But i am getting this Error message: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
The error will be specified in the comment section with capitalised sentence, which is in my second for loop
The classes (kernel and the convolute) will be provided down after my code.
Thank You!
public static void main(String[] args)
{
Kernel kernel = new Kernel();
Convolute convolute = new Convolute();
int[][] matrixA = convolute.MATRIX_A;
int[][] kernelVertical = kernel.VERTICAL;
int[][] resultArray = new int[4][4];
int tempMatrix = 0, sumMatrix = 0; // declare temporary matrix and sumMatrix to take all the sum of each product array
int matrixRowA = 0, matrixColumnA = 0; // declare variable for matrix row and column
int convoluteRowControl = 2, convoluteColumnControl = 2; // this variable is to control moving of the kernel through the convolute
while(convoluteRowControl < matrixA.length) // while loop stops when convoluteRowControl points to 6
{
for(int kernelRow = 0; kernelRow < kernelVertical.length; kernelRow++) // this loop will stop when kernelRow is 3
{
for(int kernelColumn = 0; kernelColumn < kernelVertical[kernelRow].length; kernelColumn++) // this loop will stop when kernelColumn is 3
{
tempMatrix = matrixA[matrixRowA][matrixColumnA] * kernelVertical[kernelRow][kernelColumn]; // ERROR IS HERE!!
sumMatrix += tempMatrix; // sum all of the matrix calculated
matrixColumnA++; // re-initialize matrixColumnA to move to the next column element in matrixA
}
matrixRowA++; // re-initialize matrixRowA to move to the next row element in matrixA
matrixColumnA = convoluteColumnControl - 2; // re-initialize matrixColumnA back to the original specified range
}
for(int row = 0; row < resultArray.length; row++) // this loop is used to store sumMatrix calculated in the above loop to each element of the array
{ // loop stops at 5
for(int column = 0; column < resultArray[row].length; column++)
{
resultArray[row][column] = sumMatrix; // store sumMatrix into each element of the array
}
}
++convoluteColumnControl; // re-initialize convoluteColumnControl so that the kernel can move to the next column
if(matrixColumnA < 5) // if kernel get to the last column of its size, reset the row and move to the next column again
{ // this will stop when kernel get to the final column of the convolute
matrixRowA = convoluteRowControl - 2; // reset row back to the the top kernel
matrixColumnA = convoluteColumnControl - 2; // set new starting limit for the column in the kernel
}
if(matrixColumnA == 5) // when matrixColumnA is 5 (which means the final column of the convolute) this IF is executed
{ // to set the new row for the kernel
++convoluteRowControl; // convolteRowControl increase by 1 to set a new row limit for the kernel in convolution
matrixRowA = convoluteRowControl - 2; // reset matrixRowA equivalent to the starting of the new kernel
}
}
}
public class Convolute
{
/*
* ARRAY_A contains a 6x6 matrix
*/
public static final int[][] MATRIX_A =
{
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0}
};
}
public class Kernel
{
/*
* HORIZONTAL - A kernel that detects horizontal lines.
*/
public static final int[][] HORIZONTAL =
{
{ 1, 1, 1},
{ 0, 0, 0},
{-1, -1, -1}
};
/*
* VERTICAL - A kernel that detects vertical lines.
*/
public static final int[][] VERTICAL =
{
{ 1, 0, -1},
{ 1, 0, -1},
{ 1, 0, -1}
};
}
I have a method in Python that makes use of OpenCV to remove the background from an image. I want the same functionality to work with android's version of OpenCV but I just cant seem to wrap my head around how the arrays work and how I can process them.
This is what I have so far in Java :
private Bitmap GetForeground(Bitmap source){
source = scale(source,300,300);
Mat mask = Mat.zeros(source.getHeight(),source.getWidth(),CvType.CV_8U);
Mat bgModel = Mat.zeros(1,65,CvType.CV_64F);
Mat ftModel = Mat.zeros(1,65,CvType.CV_64F);
int x = (int)Math.round(source.getWidth()*0.1);
int y = (int)Math.round(source.getHeight()*0.1);
int width = (int)Math.round(source.getWidth()*0.8);
int height = (int)Math.round(source.getHeight()*0.8);
Rect rect = new Rect(x,y, width,height);
Mat sourceMat = new Mat();
Utils.bitmapToMat(source, sourceMat);
Imgproc.grabCut(sourceMat, mask, rect, bgModel, ftModel, 5, Imgproc.GC_INIT_WITH_RECT);
int frameSize=sourceMat.rows()*sourceMat.cols();
byte[] buffer= new byte[frameSize];
mask.get(0,0,buffer);
for (int i = 0; i < frameSize; i++) {
if (buffer[i] == 2 || buffer[i] == 0){
buffer[i] = 0;
}else{
buffer[i] = 1 ;
}
}
byte[][] sourceArray = getMultiChannelArray(sourceMat);
byte[][][] reshapedMask = ReshapeArray(buffer, sourceMat.rows(), sourceMat.cols());
return source;
}
private byte[][][] ReshapeArray(byte[] arr, int rows, int cols){
byte[][][] out = new byte[cols][rows][1];
int index=0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
out[i][j][0] = arr[index];
index++;
}
}
return out;
}
public static byte[][] getMultiChannelArray(Mat m) {
//first index is pixel, second index is channel
int numChannels=m.channels();//is 3 for 8UC3 (e.g. RGB)
int frameSize=m.rows()*m.cols();
byte[] byteBuffer= new byte[frameSize*numChannels];
m.get(0,0,byteBuffer);
//write to separate R,G,B arrays
byte[][] out=new byte[frameSize][numChannels];
for (int p=0,i = 0; p < frameSize; p++) {
for (int n = 0; n < numChannels; n++,i++) {
out[p][n]=byteBuffer[i];
}
}
return out;
}
The python code I want to recreate :
image = cv2.imread('Images/handheld.jpg')
image = imutils.resize(image, height = 300)
mask = np.zeros(image.shape[:2],np.uint8)
bgModel = np.zeros((1,65),np.float64)
frModel = np.zeros((1,65),np.float64)
height, width, d = np.array(image).shape
rect = (int(width*0.1),int(height*0.1),int(width*0.8),int(height*0.8))
cv2.grabCut(image, mask, rect, bgModel,frModel, 5,cv2.GC_INIT_WITH_RECT)
mask = np.where((mask==2) | (mask == 0),0,1).astype('uint8')
image = image*mask[:,:,np.newaxis]
I have no idea how to convert the last two lines of the python code. If there is a way to just run python clean on an android device within my own project that would also be awesome.
At this point, you should consider talking a look to SL4A project which would allow you run your Python code on Android through java app.
Here are interesting links :
https://github.com/damonkohler/sl4a
https://norwied.wordpress.com/2012/04/11/run-sl4a-python-script-from-within-android-app/
http://jokar-johnk.blogspot.com/2011/02/how-to-make-android-app-with-sl4a.html
Let's see both the commands and try to convert them to Java API calls. It may not be simple 2 line in code.
mask = np.where((mask==2) | (mask == 0),0,1).astype('uint8')
In the above command, we are creating a new image mask which has uint data type of pixel values. The new mask matrix would have value 0 for every position where previous mask has a value of either 2 or 0, otherwise 1. Let's demonstrate this with an example:
mask = [
[0, 1, 1, 2],
[1, 0, 1, 3],
[0, 1, 1, 2],
[2, 3, 1, 0],
]
After this operation the output would be:
mask = [
[0, 1, 1, 0],
[1, 0, 1, 1],
[0, 1, 1, 0],
[0, 1, 1, 0],
]
So this above command is simply generating a binary mask with only 0 and 1 values. This can replicated in Java using Core.compare() method as:
// Get a mask for all `1` values in matrix.
Mat mask1vals;
Core.compare(mask, new Scalar(1), mask1vals, Core.CMP_EQ);
// Get a mask for all `3` values in matrix.
Mat mask3vals;
Core.compare(mask, new Scalar(3), mask3vals, Core.CMP_EQ);
// Create a combined mask
Mat foregroundMask;
Core.max(mask1vals, mask3vals, foregroundMask)
Now you need to multiply this foreground mask with the input image, to get final grabcut image as:
// First convert the single channel mat to 3 channel mat
Imgproc.cvtColor(foregroundMask, foregroundMask, Imgproc.COLOR_GRAY2BGR);
// Now simply take min operation
Mat out;
Core.min(foregroundMask, image, out);
I tried using ANN_MLP in OpenCV 3.0 to train a model for a simple XOR operation (i.e. 00->0, 01->1, 10->1, 11->0). But it returned NaN when I called nn.predict. What's wrong with the code? Here is my Java code:
package jm.app;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.TermCriteria;
import org.opencv.ml.ANN_MLP;
import org.opencv.ml.Ml;
import org.opencv.ml.StatModel;
public class Main {
static{System.loadLibrary(Core.NATIVE_LIBRARY_NAME);}
public static void main(String[] args) {
Mat trainData = new Mat(4, 2, CvType.CV_32FC1);
trainData.put(0, 0, 0);
trainData.put(0, 1, 0);
trainData.put(1, 0, 0);
trainData.put(1, 1, 1);
trainData.put(2, 0, 1);
trainData.put(2, 1, 0);
trainData.put(3, 0, 1);
trainData.put(3, 1, 1);
Mat trainLabels = new Mat(4, 1, CvType.CV_32FC1);
trainLabels.put(0, 0, 0);
trainLabels.put(1, 0, 1);
trainLabels.put(2, 0, 1);
trainLabels.put(3, 0, 0);
ANN_MLP nn = ANN_MLP.create();
nn.setActivationFunction(ANN_MLP.SIGMOID_SYM);
nn.setTrainMethod(ANN_MLP.BACKPROP);
nn.setBackpropMomentumScale(0.1);
nn.setBackpropWeightScale(0.1);
nn.setTermCriteria(new TermCriteria(TermCriteria.MAX_ITER, (int)100000, 0.000001));
Mat layers = new Mat(1, 3, CvType.CV_32SC1);
layers.put(0, 0, 2);
layers.put(0, 1, 3);
layers.put(0, 2, 1);
nn.setLayerSizes(layers);
nn.train(trainData, Ml.ROW_SAMPLE, trainLabels);
Mat testData = new Mat(1, 2, CvType.CV_32FC1);
testData.put(0, 0, 1);
testData.put(0, 1, 1);
Mat testLabels = new Mat(1, 1, CvType.CV_32FC1);
float res = nn.predict(testData, testLabels, ANN_MLP.RAW_OUTPUT);
Util.printMat(testLabels);
Mat layer1 = nn.getWeights(0);
Mat layer2 = nn.getWeights(1);
Mat layer3 = nn.getWeights(2);
Util.printMat(layer1);
Util.printMat(layer2);
Util.printMat(layer3);
}
}
package jm.app;
import org.opencv.core.Mat;
public class Util {
public static void printMat(Mat mat){
System.out.println();
System.out.print(mat.rows() + " * " + mat.cols());
for(int i = 0; i < mat.rows(); i++){
System.out.println();
for(int j = 0; j < mat.cols(); j++){
System.out.print(mat.get(i, j)[0] + " ");
}
}
System.out.println();
}
}
And the output is:
1 * 1
NaN
1 * 4
2.0 -1.0 2.0 -1.0
3 * 3
-0.417962425638773 -0.11805564491195578 0.7527567170648859
0.40930192249590086 -0.24876980957807385 -0.2929439299929529
0.6025307693048867 0.2936134607392147 -0.10605986687856579
4 * 1
0.5558049015443158
0.4766362469511742
0.3713056187114578
-0.24058588929784652
So I have two questions:
1. Why the "testLabel" got a NaN value?
2. Why the "layer1" is a 1*4 matrix? What did the "layer1" do here?
How long does it last to solve the knights tour problem with backtracking on an 8x8 board? Because my algorithm already computes somehow too long and it seems, like it wont finish. But when I try a 6x6, or 5x5 board, it finishes successfully.
the code:
class KnightsTour{
private boolean[][] board;
private int count, places;
private static final Point[] moves = new Point[]{
new Point(-2, -1),
new Point(-2, 1),
new Point(2, -1),
new Point(2, 1),
new Point(-1, -2),
new Point(-1, 2),
new Point(1, -2),
new Point(1, 2)
};
public KnightsTour(int n) {
board = new boolean[n][n];
places = n*n;
count = 0;
}
public boolean ride(int x, int y) {
board[x][y] = true;
count++;
if (count == places) {
return true;
}
for (Point p : moves) {
int nextX = x + p.x;
int nextY = y + p.y;
if (nextX < 0 || nextX >= board.length || nextY < 0 || nextY >= board.length || board[nextX][nextY]) {
continue;
}
if (ride(nextX, nextY)) {
return true;
}
}
board[x][y] = false;
count--;
return false;
}
}
I came across the same problem. Everything runs smoothly till n=7 and suddenly it takes forever to calculate for n=8. I hope this helps someone :)
The problem lies with the order in which you are checking for the moves. You are using :
xMove[8] = { -2, -2, 2, 2, -1, -1, 1, 1}
yMove[8] = { -1, 1, -1, 1, -2, 2, -2, 2}
If you plot these vectors in the 2D plane, they are haphazardly placed. In other words, they are not ordered in either a clockwise or an anti-clockwise manner. Consider this instead :
xMove[8] = { 2, 1, -1, -2, -2, -1, 1, 2 }
yMove[8] = { 1, 2, 2, 1, -1, -2, -2, -1 }
If you plot these vectors, they are neatly arranged in an anticlockwise circle.
Somehow this causes the recursion to run much quickly for large values of n. Mind you, it still takes forever to calculate for n=9 onwards.
I have retrieved all pixels from a png image which looks like this...
I started to get all filled lines horizontal to get the start end pixel of each line .... as I scan horizontal I get also the vertical lines in horizontal layer.... I guess it is possible to code all manually but it takes time.... my question is has anyone experience with opencv,imageJ or other to tell if any of this libs could solve the problem... I am also open for any algorithm suggestion.... (using Java)....
-> The biggest problem is the thickness of the lines between 1 and 4 pixels otherwise I could easily retrieve the joint points
Here is a possible solution using image morphology. The code below is in C++ since I only have little experience with Java.
To solve your problem, you need:
Thinning - to reduce thick lines to one-pixel width lines.
Hit-or-miss transform - for finding patterns in binary image i.e the corner and joint points.
The bad news is both operations is not yet supported in OpenCV as of version 2.4.3. The good news is I have implemented both operations, the code is available on my blog:
void thinning(cv::Mat& im)
void hitmiss(cv::Mat& src, cv::Mat dst, cv::Mat& kernel)
I will be using my thinning() and hitmiss() functions and your test image.
After loading the image, convert it to single-channel binary image.
cv::Mat im = cv::imread("D1Xnm.png");
cv::Mat bw;
cv::cvtColor(im, bw, CV_BGR2GRAY);
cv::threshold(~bw, bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Since the width of the lines vary from 1 to 4 pixels perform thinning to get one-width lines.
thinning(bw);
From the thinned image, notice that there are perfect and not perfect joint points as shown in the figure below.
To cover both the perfect and imperfect joint points, we need the following kernels for the hit-or-miss transform.
std::vector<cv::Mat> k;
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 0,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 0,-1,-1 ));
cv::Mat dst = cv::Mat::zeros(bw.size(), CV_8U);
for (int i = 0; i < k.size(); i++)
{
cv::Mat tmp;
hitmiss(bw, tmp, k[i]);
dst |= tmp;
}
Open the original image to make the result clearer.
The joint points successfully located, draw it on the original image.
std::vector<std::vector<cv::Point> > cnt;
cv::findContours(dst.clone(), cnt, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::drawContours(im, cnt, -1, CV_RGB(255,0,0), 10);
For the sake of completeness, here is the full code. With some efforts you can port it to Java.
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
void thinningIteration(cv::Mat& im, int iter)
{
cv::Mat marker = cv::Mat::zeros(im.size(), CV_8UC1);
for (int i = 1; i < im.rows; i++)
{
for (int j = 1; j < im.cols; j++)
{
uchar p2 = im.at<uchar>(i-1, j);
uchar p3 = im.at<uchar>(i-1, j+1);
uchar p4 = im.at<uchar>(i, j+1);
uchar p5 = im.at<uchar>(i+1, j+1);
uchar p6 = im.at<uchar>(i+1, j);
uchar p7 = im.at<uchar>(i+1, j-1);
uchar p8 = im.at<uchar>(i, j-1);
uchar p9 = im.at<uchar>(i-1, j-1);
int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
(p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
(p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
(p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0)
marker.at<uchar>(i,j) = 1;
}
}
im &= ~marker;
}
void thinning(cv::Mat& im)
{
im /= 255;
cv::Mat prev = cv::Mat::zeros(im.size(), CV_8UC1);
cv::Mat diff;
do {
thinningIteration(im, 0);
thinningIteration(im, 1);
cv::absdiff(im, prev, diff);
im.copyTo(prev);
}
while (cv::countNonZero(diff) > 0);
im *= 255;
}
void hitmiss(cv::Mat& src, cv::Mat& dst, cv::Mat& kernel)
{
CV_Assert(src.type() == CV_8U && src.channels() == 1);
cv::Mat k1 = (kernel == 1) / 255;
cv::Mat k2 = (kernel == -1) / 255;
cv::normalize(src, src, 0, 1, cv::NORM_MINMAX);
cv::Mat e1, e2;
cv::erode(src, e1, k1, cv::Point(-1,-1), 1, cv::BORDER_CONSTANT, cv::Scalar(0));
cv::erode(1 - src, e2, k2, cv::Point(-1,-1), 1, cv::BORDER_CONSTANT, cv::Scalar(0));
dst = e1 & e2;
}
int main()
{
cv::Mat im = cv::imread("D1Xnm.png");
if (im.empty())
return -1;
cv::Mat bw;
cv::cvtColor(im, bw, CV_BGR2GRAY);
cv::threshold(~bw, bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
thinning(bw);
std::vector<cv::Mat> k;
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 0,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 0,-1,-1 ));
cv::Mat dst = cv::Mat::zeros(bw.size(), CV_8U);
for (int i = 0; i < k.size(); i++)
{
cv::Mat tmp;
hitmiss(bw, tmp, k[i]);
dst |= tmp;
}
std::vector<std::vector<cv::Point> > cnt;
cv::findContours(dst.clone(), cnt, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::drawContours(im, cnt, -1, CV_RGB(255,0,0), 10);
cv::imshow("src", im);
cv::imshow("bw", bw*255);
cv::imshow("dst", dst*255);
cv::waitKey();
return 0;
}
Take a look at OpenCV squares.c demo, or one of the threads below:
Detect square using opencv2.4 (C)
OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection (C++)
OpenCV C++/Obj-C: Advanced square detection (C++)
You can use morphological operations on the binary image you got. In matlab you may play with bwmorph
bw = I == 0; % look at dark lines in image I
[y x] = find( bwmorph( bw, 'branchpoints' ) );
will give you x y coordinates of junction points.