I have to flip the image horizontally but when I put in my code it only flips half of so that it reflects halfway through the image.
What am I doing wrong?
public static void flipVertical(Pixel[][] imageArr)
{
int height = imageArr.length;
int width = imageArr[0].length;
for(int row = 0; row < height; row++)
{
for(int col = 0; col < width; col++)
{
Pixel p = imageArr[row][col];
imageArr[height - row - 1][col] = p;
}
}
Your code doesn't work currently because it is copying the flipped bottom half of the image into the top half without keeping the original data in the top half of the image. As such, when it processes the bottom half of the image, it is effectively copying the same data back to the bottom half again.
When you swap two values, a and b, you would need to use a temporary variable:
Pixel tmp = a;
a = b;
b = a;
If you do it like you have:
a = b; // After, a == b and b == b.
b = a;
then the second assignment is effectively a no-op, since a's value is already b.
As such, you need to update your inner loop to:
Pixel p = imageArr[row][col];
imageArr[row][col] = imageArr[height - row - 1][col];
imageArr[height - row - 1][col] = p;
Also, the outer for loop should be:
for(int row = 0; row < height/2; row++)
Otherwise you flip the image, and then flip it back again.
You can use the AffineTransform class and its method translate to flip the image horizontally. See the code here:
// Flip the image horizontally
tx = AffineTransform.getScaleInstance(-1, 1);
tx.translate(-image.getWidth(null), 0);
op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
image = op.filter(image, null);
The problem is that you're changing pixels that you will still need later. When row goes through the top half of the rows, you modify the pixels in the bottom half by copying them from the top half. When row later goes through the bottom half of the rows, you try to modify the pixels in the top half by copying them from the bottom half--but the first half of the algorithm already changed the bottom half. So you're not copying the original pixels when you set the top half.
There are at least two ways to solve this:
(1) Don't use the same array. Create a new array to hold the result, and copy after you're done.
(2) Let row go only up to half the height, and for each loop iteration, swap the pixels in imageArr[row][col] and imageArr[height-row-1][col], something like:
Pixel p1 = imageArr[row][col];
Pixel p2 = imageArr[height-row-1][col];
imageArr[row][col] = p2;
imageArr[height-row-1][col] = p1;
(Specifically, you want to do this only as long as row < height-row-1. When you reach a point where row >= height-row-1, then you have to exit the loop. Otherwise you're going to start undoing the work you've already done.)
What do you mean by flipping the image horizontally? It seems taht you want to swap it vertically... Swapping horizontally (resp. vertically) means mirroring it through vertical (resp. horizontal) axe.
Anyway. Two things : swap pixels correctly and limit your loop to half the dimension (once you swap two pixels, you don't need to swap them again) :
public static void flipHorizontal(Pixel[][] imageArr)
{
int height = imageArr.length;
int width = imageArr[0].length;
for(int row = 0; row < height; row++)
{
for(int col = 0; col < width/2; col++)
{ // swap two symmetric pixels
Pixel tmp = imageArr[row][col];
imageArr[row][col] = imageArr[row][width-col-1];
imageArr[row][width-col-1] = tmp;
}
}
}
Related
I'm trying to make a grid of squares that change their fill (from black to white and vice-versa) when clicked. I'm able to turn the entire grid on or off currently, but I'm unable to figure out how to specify which particular square should be toggled when the mouse clicks within its borders. I've created buttons using mouseX and mouseY coordinates before, but they were for specific objects that I could adjust manually. I can't figure out how to do this using for loops and arrays.
I've been told to create a boolean array and pass the value of that array to the grid array, but again, I don't know how to specify which part of the array it needs to go to. For example, how do I change the fill value of square [6][3] upon mousePressed?
Here is my code so far:
int size = 100;
int cols = 8;
int rows = 5;
boolean light = false;
int a;
int b;
void setup() {
size (800, 600);
background (0);
}
void draw() {
}
void mousePressed() {
light = !light;
int[][] box = new int[cols][rows];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
box[i][j] = i;
int a = i*100;
int b = j*100;
if (light == true) {
fill(255);
} else {
fill(0);
}
rect(a, b, 100, 100);
println(i, j);
}
}
}
First of all, you are currently recreating the entire board whenever the mouse is pressed. You must retain that info between mouse clicks, so make box a global array up there with the others. Further, it's sufficient to make it a boolean array if all you care about is the on/off state of each square:
boolean[][] isSquareLight = new boolean[cols][rows];
Instead of
if (light == true) {
you should then just check
if (isSquareLight[i][j] == true) {
(note that the == true is redundant).
Now, you've already written code that finds the coordinates for each box: You're passing it to rect!
rect(a, b, 100, 100);
All that is left to do is check whether the mouse is inside this rect, i.e. whether mouseX is between a and a+100 (and similar for mouseY) - if that's the case, then the user clicked in the box given by the current (i, j), so you can just negate isSquareLight[i][j] (before checking it like above) and it will work.
There are ways to calculate this without looping through the entire grid every time, but maybe the above helps you find the path yourself instead of just getting the code made for you.
PS: The int a; int b; at the top does nothing and can be removed. You are using the local variables a and b in your function, which is correct.
I'm new to OpenCV, but with a bit of luck and a lot of time I was able to hack together some code that detects individual cells in a chessboard like so:
The image frame is being stored in a Mat and the corners are being stored in a MatOfPoint2f.
Code to show how I'm using the matrices to draw the cells individually:
private void draw(final Mat frame) {
for (int x = 0; x < BOARD_SIZE - 1; x++)
for (int y = 0; y < BOARD_SIZE - 1; y++) {
final int index = x + y * BOARD_SIZE;
final Point topLeft = cornerPoints.get(index);
final Point bottomLeft = cornerPoints.get(index + BOARD_SIZE);
final Point topRight = cornerPoints.get(index + 1);
final Point bottomRight = cornerPoints.get(index + 1 + BOARD_SIZE);
// left line
Imgproc.line(frame, topLeft, bottomLeft, DEBUG_COLOR);
// right line
Imgproc.line(frame, topRight, bottomRight, DEBUG_COLOR);
// top line
Imgproc.line(frame, topLeft, topRight, DEBUG_COLOR);
// bottom line
Imgproc.line(frame, bottomLeft, bottomRight, DEBUG_COLOR);
}
}
How would I use the four points (the corners of the cells) to get the RGB values of the pixels inside of the each quadrilateral?
Create a mask from your vertices. You can use fillPoly for that.
Then iterate over pixels. If pixel(x,y) is valid in your mask, read RGB else continue. Restrict pixel iteration range using your extreme vertices.
Masking works. If you have lots of polygons, or not too much RAM, a point-in-polygon test may be more efficient, especially if you can guarantee that your quadrilaterals are convex. See this reference
I've been working on a Java program that reads an image, subdivides it into a definable number of rectangular tiles, then swaps the pixels of each tile with those of another tile, and then puts them back together and renders the image.
An explanation of the idea: http://i.imgur.com/OPefpjf.png
I've been using the BufferedImage class, so my idea was to first read all width * height pixels from its data buffer and save them to an array.
Then, according to the tile height and width, copy the entire pixel information of each tile to small arrays, shuffle those, and then write back the data contained in these arrays to their position in the data buffer. It should then be enough to create a new BufferedImage with the original color and sample models as well as the updated data buffer.
However, I got ominous errors when creating a new WriteableRaster from the updated data buffer, and the number of pixels didn't match up (I had suddenly gotten 24 instead of originally 8, and so forth), so I figured there is something wrong with the way I address the pixel information.
( Reference pages for BufferedImage and WriteableRaster )
I used the following loop to iterate through the 1D data buffer:
// maximum iteration values
int numRows = height/tileHeight;
int numCols = width/tileWidth;
// cut picture into tiles
// for each column of the image matrix
// addressing columns (1D)
for ( int column = 0; column < numCols; column++ )
{
// for each row of the matrix
// addressing cells (2D)
for ( int row = 0; row < numRows; row++ )
{
byte[] pixels = new byte[(tileWidth+1) * (tileHeight+1)];
int celloffset = (column + (width * row)); // find cell base address
// for each row inside the cell
// adressing column inside a tile (3D)
for ( int colpixel = 0; colpixel < tileWidth; colpixel++ )
{
// for each column inside the tile -> each pixel of the cell
for ( int rowpixel = 0; rowpixel < tileHeight; rowpixel++ )
{
// address of pixel in original image buffer array allPixels[]
int origpos = celloffset + ((rowpixel * tileWidth) + colpixel);
// translated address of pixel in local pixels[] array of current tile
int transpos = colpixel + (rowpixel * tileWidth);
// source, start, dest, offset, length
pixels[transpos] = allPixels[origpos];
}
}
}
}
Is there something wrong with this code? Or is there perhaps a much easier way to do this that I haven't thought of yet?
The code below edits the image in place. So no need to create new objects, which should simplify. If you need to keep the original, just copy it entirely first. Also, no need to save to separate arrays.
Since you said "shuffle" I assume you want to swap the tiles randomly. I made a function for that, and if you just call it many times you will end up with tiles swapped randomly. If you want a pattern or some other rule of how they are swapped, just call the other function directly with your chosen tiles.
I haven't used BufferedImage before, but looking at the documentation,
http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html
and this post,
Edit pixel values
It seems that an easy way is to use the methods getRGB and setRGB
int getRGB(int x, int y)
Returns an integer pixel in the default RGB color model
(TYPE_INT_ARGB) and default sRGB colorspace.
void setRGB(int x, int y, int rgb)
Sets a pixel in this BufferedImage to the specified RGB value.
I would try something like the following: (untested code)
Using random http://docs.oracle.com/javase/7/docs/api/java/util/Random.html
int numRows = height/tileHeight;
int numCols = width/tileWidth;
void swapTwoRandomTiles (BufferedImage b) {
//choose x and y coordinates randomly for the tiles
int xt1 = random.nextInt (numCols);
int yt1 = random.nextInt (numRows);
int xt2 = random.nextInt (numCols);
int yt2 = random.nextInt (numRows);
swapTiles (b,xt1,yt1,xt2,yt2);
}
void swapTiles(BufferedImage b, int xt1, int yt1, int xt2, int yt2) {
int tempPixel = 0;
for (int x=0; x<tileWidth; x++) {
for (int y=0; y<tileHeight; y++) {
//save the pixel value to temp
tempPixel = b.getRGB(x + xt1*tileWidth, y + yt1*tileHeight);
//write over the part of the image that we just saved, getting data from the other tile
b.setRGB ( x + xt1*tileWidth, y + yt1*tileHeight, b.getRGB ( x+xt2*tileWidth, y+yt2*tileHeight));
//write from temp back to the other tile
b.setRGB ( x + xt2*tileWidth, y + yt2*tileHeight, tempPixel);
}
}
}
// load pixels into an image
this.image = new BufferedImage(this.width,
this.height,
BufferedImage.TYPE_INT_RGB);
// get actual image data for easier pixel loading
byte[] iData = new byte[this.size - 54];
for(int i = 0; i < this.size - 54; i++) {
iData[i] = this.data[i+54];
}
// start from bottom row
for(int y = this.height-1; y >= 0; y--) {
for(int x = 0; x < this.width; x++) {
int index = (this.width*y + x) * 3;
int b = iData[index];
int g = iData[index+1];
int r = iData[index+2];
//System.out.format("R: %s\nG: %s\nB: %s\n\n", r, g, b);
// merge rgb values to single int
int rgb = ((r&0x0ff)<<16)|((g&0x0ff)<<8)|(b&0x0ff);
// build image from bottom up
this.image.setRGB(x, this.height-1-y, rgb);
}
}
I'm reading RGB values from a Bitmap. My iData byte array is correct, as I've checked it against a hex editor. However, when I run this loop, my output image is warped (see picture). I've been wracking my brain for hours trying to fix this, why is it happening?
Input image is a canadian flag.
output image:
I wasn't accounting for the zero-byte padding on the width, for which the formula is
WidthWithPadding = ceiling((w*colorDepth)/32)*32.
Images usually have width, depth (bits per pixel), and stride. Usually the stride is not just width*depth, but padded by some amount (often used to align each row to a 16-bit boundary for memory access reasons).
Your code does not appear to be accounting for the stride or any padding, which explains the offset as you go through the image. I'm surprised the colors don't get switched (suggesting the padding is the same size as a pixel), but those undefined values are causing the black stripe through the image.
Rather than multiplying (this.width*y + x) * 3 to get your index, you should use (this.stride*y + x) * 3 with the appropriate value for stride. The BufferedImage class doesn't seem to provide that in any obvious fashion, so you need to calculate or fetch that otherwise.
The general issue that's happening is that your code has conflated stride (distance between rows in bytes) with width (number of bytes in a row); in aligned data formats these are not necessarily the same thing. The general result of this is getting the sort of skew you're seeing, as the second row starts out on the last byte(s) of the first row, the third row starts out on the second-to-last byte(s) of the second row, and so on.
The first thing you need to do is calculate the stride of the data, and use that as the multiplication factor for the row. Here is a simplified loop that also is somewhat more efficient than multiplying on a per-pixel basis:
int stride = (this.width + 3) & ~3; // rounds up to the nearest multiple of 4
for (int y = 0; y < this.height; y++) {
int idx = y*stride;
for (int x = 0; x < this.width; x++) {
int b = iData[idx++];
int g = iData[idx++];
int a = iData[idx++];
// etc.
}
}
Note that the rounding trick above ((w + a - 1) & ~(a - 1)) only works for power-of-two values; a more general form of the rounding trick is:
int stride = (width + stride - 1)/stride*stride;
although it's very uncommon to find alignments that aren't a power of 2 in the first place.
I am attempting to write a Processing sketch that will take each row's center pixel's color and apply that color to the entire row. However, I am having trouble with even getting the pixels to change. It seems like the sketch doesn't even go through the for-loops where I am trying to change the pixels because it doesn't print out any of the statements except the end draw at the end of draw(). I just end up with an unmanipulated feed. Does anyone know why this isn't working?
Also, currently using Processing's standard Video library with Capture at the moment, but if there is a better library that I could utilize please let me know! Thanks!
UPDATE: Testing out my algorithm with an array of numbers, it seems like using an inner for loop isn't working like how I thought it should. The i of the outer-loop is only incremented once after the first time the inner for loop completes itself, and then it just exits the outer loop instead of starting the inner loop again. What's going on here?
import processing.video.*;
Capture feed; // webcam
int pixelCount = width * height; // total # of pixels
int center = width / 2; // value for center pixel
int widthPlus = width + 1; // value to go row-to-row
color c; // center pixel color
void setup(){
size(displayWidth, displayHeight);
feed = new Capture(this);
feed.start();
}
void draw(){
if (feed.available() == true){
feed.read();
}
image(feed, 0, 0);
feed.loadPixels(); // load pixels from webcam
/** Use to look at each row one at a time*/
for (int i = 0; i < pixelCount; i+=widthPlus){
println("Outer for-loop");
c = feed.pixels[i + center]; //get center pixel
/** Make each pixel in row the color of 'c' */
for (int j = i; j < width; j++){
println("Inner for-loop");
feed.pixels[j] = c; // set pixel to 'c'
println(i + " - " + j);
}
}
feed.updatePixels(); // update pixels from webcam
println("end draw");
}