Very simple thing I'm trying to do here. I would like to have 2 images on top of one another. When i use my mouse event dragged and clicked on the top image, the area of the top level image selected will fade and make the lower image visible.
The way I see it, there are 2 ways I can do this:
I can make the top image Transparent over time (within the selected area)
or
I can delete the pixels individually in a spray can style fashion. Think the spray can tool from MS paint back in the day.
Heres some very basic code that i started which just lays the images on top of eachother
PImage sand;
PImage fossil;
void setup()
{
size(400,400);
background(255,255,0);
frameRate(30);
fossil = loadImage("foss.jpg");
sand = loadImage("sand.jpeg");
}
void draw()
{
image(fossil, 0, 0, width,height);
image(sand, 0, 0, width,height);
smooth();
if (mousePressed) {
fill(0);
tint(255,127); //the opacity function
} else {
fill(255);
}
}
So has anyone any comments on these 2 ways of creating opacity or perhaps there an easier way I've overlooked?
Perhaps I wasn't clear in my Spec as the 2 comments below are asking for clarification.
In its simplest terms, I have 2 images on top of each other. I would like to be able to make some modification to the top level image which would make the bottom image visible. However I need to make this modification to only part of the top level image.
I would like to know which is the better option. To make part of the top image become transparent using tint() or to delete the pixels from the top layer.
Then I will proceed with that approach. Any indication as to how to do it is also appreciated.
I hope this clears up any confusion.
If you simply want to crossfade between images, it can be with tint() as you code suggest. You were in fact quite close:
PImage sand;
PImage fossil;
void setup()
{
size(400, 400);
fossil = loadImage("CellNoise.jpg");
sand = loadImage("CellVoronoi.jpg");
}
void draw()
{
//tint from 255 to 0 for the top image
tint(255,map(mouseX,0,width,255,0));
image(fossil, 0, 0, width, height);
//tint from 0 to 255 for the bottom image - 'cross fade'
tint(255,map(mouseX,0,width,0,255));
image(sand, 0, 0, width, height);
}
For the "spray can style " erosion you can simply copy pixels from a source image into the destination image. It's up to you how you loop through pixels (how many, what order, etc.) to get the "spray" like effect you want, but here's a basic example of how to use the copy() function:
PImage sand,fossil;
int side = 40;//size of square 'brush'
void setup()
{
size(400, 400);
fossil = loadImage("CellNoise.jpg");
sand = loadImage("CellVoronoi.jpg");
}
void draw()
{
image(fossil, 0, 0, 400, 400);
if(mousePressed) {
for(int y = 0 ; y < side ; y++){
for(int x = 0; x < side; x++){
//copy pixel from 'bottom' image to the top one
//map sketch dimensions to sand/fossil an dimensions to copy from/to right coords
int srcX = (int)map(mouseX+x,0,width+side,0,sand.width);
int srcY = (int)map(mouseY+y,0,height+side,0,sand.height);
int dstX = (int)map(mouseX+x,0,width+side,0,fossil.width);
int dstY = (int)map(mouseY+y,0,height+side,0,fossil.height);
fossil.set(dstX, dstY, sand.get(srcX,srcY));
}
}
}
}
Note what I am simply looping to copy a square (40x40 in my case), but you can find other fun ways to loop and get different effects.
Have fun!
Related
I love experimenting with operating on images pixel by pixel using pixels[]. However, I’d like to work on a matrix of 100x100 pixels but have the display be several times larger than 100x100 on my screen.
Is there an efficient and straightforward way to zoom, magnify, scale, resize, or etc… to allow me to work with pixels[] just… bigger?
I was disappointed to see that scale() doesn’t work with pixel arrays.
My first idea to scale by a factor of S is by iterating through my pixel array and drawing a board of SxS rects with the fill of each pixel’s color val. However, this is computationally intensive, and I can’t get my frameRate above 5 or so.
Ah great idea #sorifiend. Since I was calling loadPixels() immediately after setting the window size() and leaving it blank (rather than loading pixels from a image and putting it in the window,) I hadn't thought of that. I found you can use createImage() to make an empty image, which resizes crisply with noSmooth(); on! Thanks for the start.
Here's my code for posterity:
void setup() {
i = createImage(100, 100, RGB);
background(0);
size(800, 800);
i.loadPixels();
noSmooth();
}
void draw() {
i.loadPixels();
for (int x = 0; x < i.width; x++) {
for (int y = 0; y < i.height; y++) {
if (random(0,max(i.width,i.height)) < y) {
i.pixels[x + y*i.height] = color(255);
} else {
i.pixels[x + y*i.height] = color(0);
}
}
}
i.updatePixels();
image(i,0,0,width,height);
}
I would like to build a kind of image morphing tool in Processing. Similar to what you can see in this link:
https://giphy.com/gifs/painting-morph-oil-c8ygOpL64UDuw
My first step to achieve this was to build a two-dimensional grid of pixels. The pixels are filled with colour. The fill colour is created by reading colour from an image (PImage img1;) with the get(); function. This is how I recreated an image with my pixels. In the second step, I thought I would use the lerp(); function to give the respective pixels the colour of a second image (PImage img2;) - I thought this would create the desired morph effect. But I was wrong! The whole thing works - but the effect is only that a fade-in takes place between the two images. And no morphing. What exactly happens to pixels while this morph effect? How could I recreate it in Processing?
float pixel;
float pixelsize;
PImage img1;
PImage img2;
float counter;
void setup() {
size(1080, 1080);
pixel = 100;
pixelsize = width/pixel;
noStroke();
img1 = loadImage("0.jpg");
img2 = loadImage("1.jpg");
counter = 0;
}
void draw() {
background(255);
for (int y = 0; y < pixel; y++) {
for (int x = 0; x < pixel; x++) {
color c1 = img1.get(int(pixelsize*x), int(pixelsize*y));
color c2 = img2.get(int(pixelsize*x), int(pixelsize*y));
color from = c1;
color to = c2;
color interA = lerpColor(from, to, counter);
pushMatrix();
translate(pixelsize*x, pixelsize*y);
fill(interA);
rect(0, 0, pixelsize, pixelsize);
popMatrix();
}
}
counter= counter + 0.01;
}
Indeed it is not a straight forward task.
You're approach is not a bad start: it would result in a nice crossfade between the two images.
Bare in mind get() can be costly on the CPU.
You can however use the pixels[]:
PImage img1;
PImage img2;
// transition image
PImage imgT;
void setup() {
size(1080, 1080);
img1 = loadImage("0.jpg");
img2 = loadImage("1.jpg");
// copy the 1st image (copies width/height as well)
imgT = img1.get();
}
void draw() {
background(255);
// map transition amount to mouse X position
float t = map(mouseX, 0, width, 0.0, 1.0);
// make all pixels readable
imgT.loadPixels();
// lerp each pixel
for(int i = 0 ; i < imgT.pixels.length; i++){
imgT.pixels[i] = lerpColor(img1.pixels[i], img2.pixels[i], t);
}
// update all pixels in one go
imgT.updatePixels();
// display result
image(imgT, 0, 0);
}
Implementing a full morph image is non-trivial.
I can recomend two options to make use of existing algorithms, however these options are also not beginner friendly:
ImageMagick implements shepards distortion and there is a java library that interfaces with imagemagick: im4java. Note that you'd need to download the precompiled java library and drop the .jar file on top of your sketch and processing the output might take time: probably not feasible for realtime (however it should be possible to save individual frames to disk and assemble them as a gif/movie/etc.)
Using OpenCV: there's an OpenCV Face Morph tutorial with source code in c++ or Python and there is a Processing OpenCV library. It would be a matter of porting the c++/Python OpenCV calls to the Java OpenCV API.
I'm a total beginner so forgive me if this is probably silly or improper of me to ask.
I'm trying to make my own virtual oscillograph in processing. I don't really know how to explain it, but I want to "zoom out" from where I am getting the peaks in waveforms, which is the window size. I'm not sure what I'm doing wrong here or what's wrong with my code. I've tried changing the buffer size, and changing the multiplier for x/y. My sketch is adapted from a minim example Sketch.
All Help is greatly appreciated.
import ddf.minim.*;
Minim minim;
AudioInput in;
int frames;
int refresh = 7;
float fade = 32;
void setup()
{
size(800, 800, P3D);
minim = new Minim(this);
ellipseMode(RADIUS);
// use the getLineIn method of the Minim object to get an AudioInput
in = minim.getLineIn(Minim.STEREO);
println (in.bufferSize());
//in.enableMonitoring();
frameRate(1000);
background(0);
}
void draw()
{
frames++; //same saying frames = frames+1
if (frames%refresh == 0){
fill (0, 32, 0, fade);
rect (0, 0, width, height);
}
float x;
float y;
stroke (0, 0);
fill (0,255,0);
// draw the waveforms so we can see what we are monitoring
for(int i = 0; i < in.bufferSize() - 1; i++)
{
x = width/2 + in.left.get(i) * height/2;
y = height/2- in.right.get(i) * height/2;
ellipse(x, y, .5, .5);
}
}
Thanks
Edit: you don't need push and pop matrix here. Guess my understanding of it is lacking too. You can just use translate.
You can use matrices to create a camera object, there is tons of material out there that you can read up on to understand the math behind this and implement it anywhere.
However, there might be an easier solution here. You can use pushMatrix and popMatrix in combination with translate. Push and popping the matrix will manipulate the matrix stack - you create a new "frame" where you can play around with translations, then pop back the original frame (so you don't get lost by applying new translations on each frame).
push the matrix, translate the z coordinate once before drawing everything you want zoomed out, pop the matrix. You can set up a variable for the translation so that you can control this with your mouse.
Here's a crude example (I don't have all those libraries so couldn't add it to your code):
float scroll = 0;
float scroll_multiplier = 10;
void setup()
{
size(800, 800, P3D);
frameRate(1000);
background(0);
}
void draw()
{
background(0);
//draw HUD - things that don't zoom.
fill(255,0,0);
rect(400,300,100,100);
//We don't want to mess up our coordinate system, we push a new "frame" on the matrix stack
pushMatrix();
//We can now safely translate the Y axis, creating a zoom effect. In reality, whatever we pass to translate gets added to the coordinates of draw calls.
translate(0,0,scroll);
//Draw zoomed elements
fill(0,255,0);
rect(400,400,100,100);
//Pop the matrix - if we don't push and pop around our translation, the translation will be applied every frame, making our drawables dissapear in the distance.
popMatrix();
}
void mouseWheel(MouseEvent event) {
scroll += scroll_multiplier * event.getCount();
}
Situation: I have a canvas on an Android game, I have some objects (I will keep it as simple as possible):World(where are storaged all Laser and Block objects), Block and Laser. I can draw all this objects in the canvas.
I would like to 'hide' them behind a black 'background', and then draw a blurry 'transparent' circle, so all objects are hidden behind the black background, except the objects behing the circle.
I have thought about it, but I can't think of an approach to do this.
Images:
This is my actual situation:
This is the expected:
Do something like this:
public void drawBitmapsInCanvas(Canvas c){
c.drawBitmap(block, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
c.drawBitmap(block2, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
c.drawBitmap(laser, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
c.drawColor(Color.BLACK);//this hides everything under your black background.
c.drawBitmap(circle, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
}
If you want transparency:
Paint paint =new Paint();
paint.setARGB(120,0,0,0); //for the "120" parameter, 0 is completely transparent, 255 is completely opaque.
paint.setAntiAlias(true);
c.drawBitmap(bmp,Rect r,Rect rr, paint);
or if you are trying to change the opacity of individual pixels, the approach is a bit more complicated (I have not tested the code, but you get the gist of it):
public static final Bitmap getNewBitmap(Bitmap bmp, int circleCenterX,
int circleCenterY,int circleRadius){
//CIRCLE COORDINATES ARE THE DISTANCE IN RESPECT OF (0,0) of the bitmap
//, not (0,0) of the canvas itself. The circleRadius is the circle's radius.
Bitmap temp=bmp.copy(Bitmap.Config.ARGB_8888, true);
int[]pixels = new int[temp.getWidth()*temp.getHeight()];
temp.getPixels(pixels,0 ,temp.getWidth(),0,0,temp.getWidth(), temp.getHeight());
int counter=0;
for(int i=0;i<pixels.length;i++){
int alpha=Color.alpha(pixels[i]);
if(alpha!=0&&!((Math.pow(counter/temp.getWidth()-circleCenterY,2.0)+
Math.pow(counter%temp.getWidth()-circleCenterX,2.0))<Math.pow(circleRadius,2.0))){
//if the pixel itself is not completely transparent and the pixel is NOT within range of the circle,
//set the Alpha value of the pixel to 0.
pixels[i]=Color.argb(0,Color.red(pixels[i]),Color.green(pixels[i]),Color.blue(pixels[i]));
}
counter++;
}
temp.setPixels(pixels,0, temp.getWidth(),0,0,temp.getWidth(),temp.getHeight());
return temp;
}
and then draw temp.
I'm not completely sure what you are trying to ask, so you may have to modify as necessary.
If you try the second answer of qwertyuiop5040, you will get a ver low - perfomance when you try to apply it to a large image. Let's say a 1000*800 pixels image. Then you will have a loop:
for (int i = 0 ; i < 1000*800; i++)
You could create an image that's a black rectangle with a transparent hole in it. The hole would be the circle that you can see through, and the image would be rendered over the spot you want to be visible. Then, you can draw four black rectangles around the image to cover the rest of the screen.
I have the following problem. I have a charting program, and it's design is black, but the charts (that I get from the server as images) are light (it actually uses only 5 colors: red, green, white, black and gray).
To fit with the design inversion does a good job, the only problem is that red and green are inverted also (green -> pink, red -> green).
Is there a way to invert everything except those 2 colors, or a way to repaint those colors after inversion?
And how costly are those operations (since I get the chart updates pretty often)?
Thanks in advance :)
UPDATE
I tried replacing colors with setPixel method in a loop
for(int x = 0 ;x < chart.getWidth();x++) {
for(int y = 0;y < chart.getHeight();y++) {
final int replacement = getColorReplacement(chart.getPixel(x, y));
if(replacement != 0) {
chart.setPixel(x, y, replacement);
}
}
}
Unfortunetely, the method takes too long (~650ms), is there a faster way to do it, and will setPixels() method work faster?
Manipulating a bitmap is much faster if you copy the image data into an int array by calling getPixels only once, and don't call any function inside the loop. Just manipulate the array, then call setPixels at the end.
Something like that:
int length = bitmap.getWidth()*bitmap.getHeight();
int[] array = new int[length];
bitmap.getPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
for (int i=0;i<length;i++){
// If the bitmap is in ARGB_8888 format
if (array[i] == 0xff000000){
array[i] = 0xffffffff;
} else if ...
}
}
bitmap.setPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
If you have it available as BufferedImage, you can access its raster and edit it as you please.
WritableRaster raster = my_image.getRaster();
// Edit all the pixels you wanna change in the raster (green -> red, pink -> green)
// for (x,y) in ...
// raster.setPixel(x, y, ...)
my_image.setData(raster);
OK seen that you're really only using 5 colors it's quite easy.
Regarding performances, I don't know about Android but I can tell you that in Java using setRGB is amazingly slower than getting back the data buffer and writing directly in the int[].
When I write "amazingly slower", to give you an idea, on OS X 10.4 the following code:
for ( int x = 0; x < width; x++ ) {
for ( int y = 0; y < height; y++ ) {
img.setRGB(x,y,0xFFFFFFFF);
}
}
can be 100 times (!) slower than:
for ( int x = 0; x < width; x++ ) {
for ( int y = 0; y < height; y++ ) {
array[y*width+x] = 0xFFFFFFFF;
}
}
You read correctly: one hundred time. Measured on a Core 2 Duo / Mac Mini / OS X 10.4.
(of course you need to first get access to the underlying int[] array but hopefully this shouldn't be difficult)
I cannot stress enough that the problem ain't the two for loops: in both cases it's the same unoptimized for loops. So it's really setRGB that is the issue here.
I don't know it works on Android, but you probably should get rid of setRGB if you want something that performs well.
A quick way would be to use AvoidXfermode to repaint just those colors you want changed - you could then switch between any colors you want. You just need to do something like this:
// will change red to green
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));
Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);
// rinse, repeat for other colors
You may need to play with the tolerance for the AvoidXfermode, but that should do what you want a lot faster than a per-pixel calculation. Also, make sure your chart image is in ARGB8888 mode. By default, Android tends to work with images in RGB565 mode, which tends to mess up color calculations like you want to use - to be sure, you can make sure your image is both in ARGB8888 mode and mutable by calling Bitmap chart = chartFromServer.copy(Config.ARGB_8888, true); before you setup the Xfermode.
Clarification: to change other colors, you wouldn't have to re-load the images all over again, you would just have to create other Paints with the appropriate colors you want changed like so:
// changes green to red
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));
// changes white to blue
Paint change2 = new Paint();
change2.setColor(Color.BLUE);
change2.setXfermode(new AvoidXfermode(Color.WHITE, 245, AvoidXfermode.Mode.TARGET));
// ... other Paints with other changes you want to apply to this image
Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);
c.drawRect(0, 0, width, height, change2);
//...
c.drawRect(0, 0, width, height, changeN);