So I'm having the following problem: I have a method that breaks a big matrix into smaller blocks of the same size. After I do some operations on the blocks, I want to reconstruct the big matrix in the right order, but I'm going wrong at it somehow.
The following code reconstructs correctly a 4x4 matrix that breaks into 2x2, but for any other dimensions, it's not working properly.
public long[][] blocksToMatrix(List<long[][]> blocks, int blockDimension, int width, int height ){
long[][] yuvMatrix = new long[height][width];
int heightPos = 0;
int widthPos = 0;
for (int i = 0; i < blocks.size(); i++) {
long[][] yuvBlock = blocks.get(i);
int heightPosTemp = heightPos;
for (int j = 0; j < blockDimension * blockDimension; j++) {
yuvMatrix[heightPos][widthPos] = yuvBlock[j / blockDimension][j % blockDimension];
widthPos++;
if (widthPos >= width){
widthPos = (i * blockDimension) % width;
heightPos++;
}
if (widthPos == ((i + 1) * blockDimension) % width){
widthPos = (i * blockDimension) % width;
heightPos++;
}
}
if (heightPos == height ){
heightPos = heightPosTemp;
}
else {
heightPos = (i * blockDimension) % height;
}
widthPos = ((i + 1) * blockDimension) % width;
}
return yuvMatrix;
}
The method I used to break the matrix:
public List<long[][]> matrixToBlocks(long[][] yuvMatrix, int blockDimension, int width, int height){
int blocksSize = width / blockDimension * (height / blockDimension);
List<long[][]> blocks = new ArrayList<long[][]>();
for (int i = 0; i < blocksSize; i++) {
long[][] subBlock = new long[blockDimension][blockDimension];
int heightPos = (blockDimension * (i / blockDimension)) % height;
int widthPos = (blockDimension * i) % width;
if (widthPos + blockDimension > width) {
widthPos = 0;
}
for (int row = 0; row < blockDimension; row++) {
for (int col = 0; col < blockDimension; col++) {
subBlock[row][col] = yuvMatrix[heightPos + row][col + widthPos];
}
}
blocks.add(subBlock);
}
return blocks;
}
The way I tested it:
public static void testareMatBlo(int height, int width, int blockdim){
long[][] test = new long[height][width];
int val = 1;
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
test[i][j] = val;
val++;
}
}
List<long[][]> blocks = matrixToBlocks(test, blockdim, width, height);
long[][] matrix = blocksToMatrix(blocks, blockdim, width, height);
if (Arrays.deepEquals(test, matrix)){
System.out.println("YES");
}
else {
System.out.println("NO");
}
}
This works:
testareMatBlo(4, 4, 2);
But anything else doesn't. Can anyone explain what I did wrong?
I didn't thoroughly read your code for matrixToBlocks(...)
but all those calculations like int blocksSize = width / blockDimension * (height / blockDimension);
are very likely to introduce hard to spot errors - and you actually don't need them:
public static List<long[][]> matrixToBlocks(long[][] yuvMatrix, int blockDimension){
//Check matrix and block dimension match
if( yuvMatrix.length == 0 || yuvMatrix.length % blockDimension != 0
|| yuvMatrix[0].length == 0 || yuvMatrix[0].length % blockDimension != 0 ) {
throw new IllegalArgumentException("whatever message you like");
}
List<long[][]> blocks = new ArrayList<long[][]>();
//Iterate over the blocks in row-major order (down first, then right)
for( int c = 0; c < yuvMatrix.length; c += blockDimension ) {
for( int r = 0; r < yuvMatrix[c].length; r += blockDimension ) {
long[][] subBlock = new long[blockDimension][blockDimension];
//Iterate over the block in row-major order
for(int bc = 0; bc < blockDimension; bc++ ) {
for(int br = 0; br < blockDimension; br++ ) {
subBlock[bc][br]=yuvMatrix[c+bc][r+br];
}
}
blocks.add(subBlock);
}
}
return blocks;
}
That method doesn't look shorter but it is: discounting the preliminary check yours is missing there are only 8 actual lines of code compared to 13 in your code. That's not the point however. What's more important is that the logic is easier since there are only a few calculations involved (like c+bc
).
You might think this is inefficient but it isn't: you're accessing each element only once and thus even though there are 4 nested loops the overall complexity is still O(n) with n being the size of the matrix.
Constructing the matrix back is equally easy. The major thing you need to take care of is the ordering of the blocks: if you create them in row-major order (blocks below each other are next to each other in the list) you need to recreate the matrix in the same way:
public static long[][] blocksToMatrix( List<long[][]> blocks, int width, int height ) {
long[][] yuvMatrix = new long[width][height];
int c = 0;
int r = 0;
for( long[][] block : blocks ) {
int blockWidth = block.length;
int blockHeight = block[0].length;
for( int bc = 0; bc < block.length; bc++ ) {
for( int br = 0; br < block[bc].length; br++ ) {
yuvMatrix[c + bc][r + br] = block[bc][br];
}
}
//calculate the next offset into the matrix
//The blocks where created in row-major order so we need to advance the offset in the same way
r += blockHeight;
if( r >= height ) {
r = 0;
c += blockWidth;
}
}
return yuvMatrix;
}