I am using a median filter over sequences of images in order to get the effect of the sequences degrading over time. This means that for ever 'n' frame the code is operating over every pixel of the image 'n' times (on the 100th frame, apply the median filter 100 times before saving the result, and so on). This begins to take more time when I begin working in the 1000+ frame range. I am looking for a way, including suggestions, or other options outside of Processing, that may help to speed up the work I am doing. Any thoughts, help, or suggestions are appreciated... code is below.
Thank you,
M.
/** * median_filter_image_sequence.pde * version 0.02 * * Matthew Lord * 2012.01.28 * * This is code for a re-iterative median filter applied to a sequence of * images 'n' times. It is then saved to a folder. * * Originally inspired by the median filter in ToyViewer * http://www7a.biglobe.ne.jp/~ogihara/software/OSX/toyv-eng.html * and the software's ability to compare each image with the next after * each edit. * * Now with an offset and total frames option to pick up in the middle of * sequences - msl.2012.01.27 * **/
// String values to be filled in prior to run // images should be named numerically and with .jpg extension // path to initial image to operate on String img_path = "data/dirname/file_prefix"; // name of folder path for images String save_folder_name = "data/processed_imgs/dirname"; // Offset and totalframes values to be filled before running int offset = 0; // include what has been rendered up to (one less than where we begin) int totalframes = 0; // number to stop at // Arrays to hold (Alpha)RGB values for processing int[ ] AList = new int[ 0 ]; int[ ] RList = new int[ 0 ]; int[ ] GList = new int[ 0 ]; int[ ] BList = new int[ 0 ];
// load an image to operate on so long as there is one if (frameCount + offset <= totalframes) { img = loadImage(img_path + nf(frameCount + offset, 5) + ".jpg"); }
// to render the first "n" images while (iteration_count <= frameCount + offset - 1) { // load the pixels of the image img.loadPixels( ); // Create a new image same size as original to store the new data into medianImg = createImage(img.width, img.height, RGB); // process the img through the median filter medianImg = medianFilterImage(img, medianImg); // update the medianImg medianImg.updatePixels( ); // draw the median image to the screen image(medianImg, 0, 0); // assign the medianImg to continue the process img = medianImg; // increase the iteration value iteration_count++; // for testing and feedback if( iteration_count % 25 == 0 ) { println( "current iteration is: " + iteration_count ); } } // save the resulting frame saveFrame(save_folder_name + "/" + nf(frameCount + offset, 4) + ".####.tif"); // reset iteration count iteration_count = 0; // for testing and feedback println( "FRAME " + frameCount + " DONE!" ); }
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * FUNCTIONS * * Set the values for the following variables manually for each function * for the time being (until algorithm can be found): * rgbListMedian * matrixSize * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/
/** looking for an algorithm... int rlm_main = 4; int ms_main = 9;
int rlm_corner = rlm_main / 4; int ms_corner = ms_main / 2;
int rlm_border = rlm_main / 2; int ms_border = ms_main / (3 * 2); **/
void setArraySizeBody( ) { // for main body of image rgbListMedian = 4; matrixSize = 9; }
// MAKE THE OPERATIONAL ARRAYS void makeArrays(int x) { AList = new int[ x ]; RList = new int[ x ]; GList = new int[ x ]; BList = new int[ x ]; }
// CREATION OF VARIABLES & ARRAYS DEPENDENT UPON PIXEL OPERATION void makeCornerArrays( ) { // make corner arrays setArraySizeCorner( ); resetZ( ); makeArrays(matrixSize); } void makeBorderArrays( ) { // make border arrays setArraySizeBorder( ); resetZ( ); makeArrays(matrixSize); } void makeMainArrays( ) { // make main arrays setArraySizeBody( ); resetZ( ); makeArrays(matrixSize); }
// function for loading the position and ARGB List Arrays void ARGBLoadArrays(int x, int y, int kx, int ky) { // declare variables int pos, alphaVal, redVal, greenVal, blueVal; // pixel position pos = (y + ky) * width + (x + kx); // get color values alphaVal = int(alpha(img.pixels[ pos ])); redVal = int(red(img.pixels[ pos ])); greenVal = int(green(img.pixels[ pos ])); blueVal = int(blue(img.pixels[ pos ])); // place the RGB values into their respective arrays AList[ z ] = int(alphaVal); RList[ z ] = int(redVal); GList[ z ] = int(greenVal); BList[ z ] = int(blueVal); }
// takes in four arrays, sorts them, and defines a color // select the median value (-1) for each color array ARGB color getMedianColor(int[ ] a, int[ ] r, int[ ] g, int[ ] b, int m) { // color variable color c; // sort the Arrays a = sort(a); r = sort(r); g = sort(g); b = sort(b); // select the median value for each array c = color(r[ m ], g[ m ], b[ m ], a[ m ]); return c; }
// applies a median filter to an image and returns it PImage medianFilterImage(PImage img, PImage filteredImg) { int medVal; color medianColor; // CYCLE THROUGH ALL PIXELS // loop through every pixel in the y-direction for (int y = 0; y <= img.height - 1; y++) { // loop through every pixel in the x-direction for (int x = 0; x <= img.width - 1; x++) {
// operate on the upper left pixel if (y == 0 && x == 0) { makeCornerArrays( ); // make an array out of the following pixels // [ none ] [ none ] [ none ] // [ none ] [ 0, 0 ] [ 1, 0 ] // [ none ] [ 0, 1 ] [ 1, 1 ] for (int ky = 0; ky <= 1; ky++) { for(int kx = 0; kx <= 1; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the top row, main pixels if (y == 0 && (x > 0 && x < img.width - 1)) { makeBorderArrays( ); // make an array out of the following pixels // [ none ] [ none ] [ none ] // [ -1, 0 ] [ 0, 0 ] [ 1, 0 ] // [ -1, 1 ] [ 0, 1 ] [ 1, 1 ] for (int ky = 0; ky <= 1; ky++) { for (int kx = -1; kx <= 1; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the upper right pixel if (y == 0 && x == (img.width - 1)) { makeCornerArrays( ); // make an array out of the following [ x, y ] pixels // [ none ] [ none ] [ none ] // [ -1, 0 ] [ 0, 0 ] [ none ] // [ -1, 1 ] [ 0, 1 ] [ none ] for (int ky = 0; ky <= 1; ky++) { for (int kx = -1; kx <= 0; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the left border column pixels if ((y > 0 && y < (img.height - 2)) && x == 0) { makeBorderArrays( ); // make an array out of the following [ x, y ] pixels // [ none ] [ 0, -1 ] [ 1, -1 ] // [ none ] [ 0, 0 ] [ 1, 0 ] // [ none ] [ 0, 1 ] [ 1, 1 ] for (int ky = -1; ky <= 1; ky++) { for (int kx = 0; kx <= 1; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the right border column pixels if ((y > 0 && y < (img.height - 2)) && x == (img.width - 1)) { makeBorderArrays( ); // make an array out of the following [ x, y ] pixels // [ -1, -1 ] [ 0, -1 ] [ none ] // [ -1, 0 ] [ 0, 0 ] [ none ] // [ -1, 1 ] [ 0, 1 ] [ none ] for (int ky = -1; ky <= 1; ky++) { for (int kx = 0; kx <= 1; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the lower left pixel if (y == (img.height - 1) && x == 0) { makeCornerArrays( ); // make an array out of the following pixels // [ none ] [ 0, -1 ] [ 1, -1 ] // [ none ] [ 0, 0 ] [ 1, 0 ] // [ none ] [ none ] [ none ] for (int ky = -1; ky <= 0; ky++) { for (int kx = 0; kx <= 1; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the bottom row, main pixels if (y == (img.height - 1) && (x > 0 && x < img.width - 1)) { makeBorderArrays( ); // make an array out of the following pixels // [ -1, -1 ] [ 0, -1 ] [ 1, -1 ] // [ -1, 0 ] [ 0, 0 ] [ 1, 0 ] // [ none ] [ none ] [ none ] for (int ky = -1; ky <= 0; ky++) { for (int kx = -1; kx <= 1; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the lower right pixel if (y == (img.height - 1) && x == (img.width - 1)) { makeCornerArrays( ); // make an array out of the following [ x, y ] pixels // [ -1, -1 ] [ 0, -1 ] [ none ] // [ -1, 0 ] [ 0, 0 ] [ none ] // [ none ] [ none ] [ none ] for (int ky = -1; ky <= 0; ky++) { for (int kx = -1; kx <= 0; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } }
// operate on the main portion of the image // check that we are not on a top or bottom edge horizontal_edge = y > 0 && y < img.height - 1; // check that we are not on the bottom edge vertical_edge = x > 0 && x < img.width - 1; if (horizontal_edge && vertical_edge) { makeMainArrays( ); // make an array out of the following [ x, y ] pixels // [ -1, -1 ] [ 0, -1 ] [ 1, -1 ] // [ -1, 0 ] [ 0, 0 ] [ 1, 0 ] // [ -1, 1 ] [ 0, 1 ] [ 1, 1 ] for (int ky = -1; ky <= 1; ky++) { for (int kx = -1; kx <= 1; kx++) { ARGBLoadArrays(x, y, kx, ky); z++; } } } medVal = rgbListMedian - 1; // define the median color value medianColor = getMedianColor(AList, RList, GList, BList, medVal); // assign the RGB values to the new image pixel in the same // location as the original filteredImg.pixels[ y * img.width + x ] = color(medianColor); } } return filteredImg; }
I am working on a program which will apply a median filter to an image then save and load the result, then do this again repeatedly. My program is functional though the results are less than ideal because I am applying a 3x3 matrix to collect the values for one pixel's nearest neighbors and in order to keep the array from going out of bounds I am neglecting the first and last columns and rows. The results for this leave those pixels black which then alters the next iteration to where eventually I am left with a solid black frame.
To sum up, I would like to know how to handle these border pixels so that the returned image is a consistent alteration of the original.
Any help, advice, or direction is much appreciated.
Thank you,
Matthew
/** * Matthew Lord : 2010 / 11 . 30 * * using a variety of examples from: * "Processing" by Fry & Reas * * This code will process an image * */
import java.util.Arrays;
PImage img;
int matrixSize = 9; int rgbListMedian = 4; int offset = 1;
// SETUP void setup( ) { size( 320, 180 ); }
// DRAW void draw( ) {
// to render the first "n" images if( frameCount <= 620 ) {
// select an image if( frameCount <= 1 ) { // load the seed image img = loadImage( "testimage.jpg" ); } else if( frameCount > 1 ) { // assign the new image to the "img" variable img = loadImage( "processedimgs/testimage." + nf( frameCount - 1, 4 ) + ".tif" ); }
// load the pixels of the image img.loadPixels( );
// Create a new image to store the new data into // Create an opaque image of the same size as the original PImage medianImg = createImage( img.width, img.height, RGB );
// cycle through all pixels // Loop through every pixel in the image. for( int y = offset; y < img.height - offset; y++ ) {
for( int x = offset; x < img.width - offset; x++ ) {
int z = 0;
int[ ] AList = new int[ matrixSize ]; int[ ] RList = new int[ matrixSize ]; int[ ] GList = new int[ matrixSize ]; int[ ] BList = new int[ matrixSize ];
for( int ky = -offset; ky <= offset; ky++ ) {
for( int kx = -offset; kx <= offset; kx++ ) {
int pos = ( y + ky ) * width + ( x + kx );
int alphaVal = int( alpha( img.pixels[ pos ] ) ); int redVal = int( red( img.pixels[ pos ] ) ); int greenVal = int( green( img.pixels[ pos ] ) ); int blueVal = int( blue( img.pixels[ pos ] ) );
// place the RGB values into their respective arrays AList[ z ] = int( alphaVal ); RList[ z ] = int( redVal ); GList[ z ] = int( greenVal ); BList[ z ] = int( blueVal );