Last active
August 29, 2015 14:03
-
-
Save keot/bf0b59156ed8c165541c to your computer and use it in GitHub Desktop.
MapCutter: cuts terrain strips into tiles for displaying in a Rapid Serial Visual Presentation (RSVP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* MapCutter | |
* Cuts terrain strips into tiles for displaying in a Rapid Serial Visual Presentation (RSVP). | |
* A component of the Assisting Search and Rescue through Visual Attention thesis appendix. | |
* http://cas.ee.ic.ac.uk/people/jpm04/asartva/ | |
* Created by James Mardell <james.mardell@imperial.ac.uk> / CC-BY-NC-ND-3.0 | |
*/ | |
#include <iostream> | |
#include <Magick++.h> | |
#include <iomanip> | |
#include <sstream> | |
#include <fstream> | |
#include <string> | |
#include <cctype> // int toupper(int) | |
#include <cstdlib> // int atoi(char*), int exit(int) | |
#include <cmath> // float ceilf(float), long int lrintf(float) | |
#include <sys/stat.h> // int stat(filename, *astruct stat) | |
using namespace std; | |
using namespace Magick; | |
#define MAP_WIDTH 15360 | |
#define MAP_HEIGHT 768 | |
#define SCREEN_WIDTH 1024 | |
#define SCREEN_HEIGHT 768 | |
#define OVERLAP_PERCENT 10.0f | |
#define ANSI_BOLD "\e[1m" | |
#define ANSI_NORMAL "\e[0m" | |
#define DEBUG false | |
inline int iceilf(const float f) | |
{ | |
return lrintf(ceilf(f) ); | |
} | |
unsigned int transformTileNumber(const unsigned int col, const unsigned int row, const unsigned int seg) | |
{ | |
unsigned int res; | |
if (col % 2 == 0) { | |
// odd column | |
res = (col * seg) + row; | |
} else { | |
// even column | |
res = (col * seg) + ((seg-1) - row); | |
} | |
return res; | |
} | |
int main (int argc, char * const argv[]) | |
{ | |
// Check argument quantity | |
if (argc != 4 + 1) { | |
cerr << "Usage: " << ANSI_BOLD << argv[0] << ANSI_NORMAL << " [map image] [segmentation degree] [output prefix] [output extension]" << endl; | |
cerr << '\t' << "map image:" << '\t' << '\t' << "Path and filename of input map image (must be greater than 15360x768)" << endl; | |
cerr << '\t' << "segmentation degree:" << '\t' << ANSI_BOLD << '1' << ANSI_NORMAL << '-' << ANSI_BOLD << '6' << ANSI_NORMAL << endl; | |
cerr << '\t' << "output prefix:" << '\t' << "Path to save output files and the file prefix" << endl; | |
cerr << '\t' << "output extension:" << '\t' << "File extension for output files (determines format)" << endl; | |
exit(1); | |
} | |
// Parse arguments | |
const string map_image = argv[1]; | |
const unsigned int segmentation_degree = atoi(argv[2]); | |
const string output_prefix = argv[3]; | |
const string output_extension = argv[4]; | |
// Verify arguments | |
{ | |
bool pass = false; | |
struct stat object_info; | |
// check that map_data exists | |
if (stat(map_image.c_str(), &object_info) != 0) { // zero is true | |
cerr << "Error: Map image '" << map_image << "' is not valid." << endl; | |
// check that segmentation degree is a number between 1 and 5 | |
} else if ( (segmentation_degree < 1) || (segmentation_degree > 6) ) { | |
cerr << "Error: Segmentation degree '" << segmentation_degree << "' is not a number between 1 and 6." << endl; | |
// check that the output prefix is valid | |
} else if (output_prefix.size() == 0) { | |
cerr << "Output prefix '" << output_prefix << "' is not valid." << endl; | |
// check that the output prefix is valid | |
} else if (output_extension.size() != 3) { | |
cerr << "Output extension '" << output_extension << "' is not valid. Try " << ANSI_BOLD << "jpg" << ANSI_NORMAL << " or " << ANSI_BOLD << "png" << ANSI_NORMAL << "." << endl; | |
// end of verification | |
} else pass = true; | |
if (!pass) exit(1); | |
} | |
// Segmentation properties | |
const unsigned int tiles_length = segmentation_degree * 15; | |
const unsigned int tiles = segmentation_degree * tiles_length; | |
// Iterator dependents | |
unsigned int x, y; | |
float offset_x, offset_y, overlap_x, overlap_y; | |
unsigned int row, col; | |
unsigned int tile_w, tile_h; | |
int crop_offset_x, crop_offset_y; | |
// Calculate tile offsets and dimensions | |
offset_x = (float)MAP_WIDTH / (float)tiles_length; | |
offset_y = (float)MAP_HEIGHT / (float)segmentation_degree; | |
overlap_x = 0.0f; | |
overlap_y = 0.0f; | |
tile_w = iceilf(offset_x); | |
tile_h = iceilf(offset_y); | |
// Create tiles | |
if (!DEBUG) cout << "Cropping" << flush; | |
for (unsigned int tile = 0; tile < tiles; tile++) { | |
// Load image | |
Image map; | |
try { | |
map.read(map_image.c_str() ); | |
} catch (Exception &e) { | |
cerr << "Error reading map image file '" << map_image << "'." << endl << '\t' << e.what() << endl; | |
exit(1); | |
} | |
col = tile % tiles_length; | |
row = tile / tiles_length; | |
x = iceilf(col * offset_x); | |
y = iceilf(row * offset_y); | |
crop_offset_x = 0; | |
crop_offset_y = 0; | |
// Sort out edge cases | |
if (row == 0) { | |
// top-edge | |
y = 0; | |
} else if (row == segmentation_degree - 1) { | |
// bottom-edge | |
y = MAP_HEIGHT - tile_h; | |
} | |
if (col == 0) { | |
// left-edge | |
x = 0; | |
} else if (col == tiles_length - 1) { | |
// right-edge | |
x = MAP_WIDTH - tile_w; | |
} | |
if (DEBUG) cerr << row << 'x' << col << ':' << ' ' << tile_w << 'x' << tile_h << '+' << x << '+' << y << endl; | |
Image output(Geometry(tile_w, tile_h), ColorGray(0.5f)); | |
map.crop(Geometry(tile_w, tile_h, x, y) ); | |
output.composite(map, crop_offset_x, crop_offset_y, AtopCompositeOp); | |
stringstream file_name; | |
file_name << output_prefix << setw(4) << setfill('0') << transformTileNumber(col, row, segmentation_degree) << '.' << output_extension; | |
try { | |
output.write(file_name.str().c_str() ); | |
} catch (Exception e) { | |
cerr << "Error writing output file '" << file_name.str() << "'." << endl << '\t' << e.what() << endl; | |
} | |
if (!DEBUG) cout << "." << flush; | |
} // for | |
if (!DEBUG) cout << "done!" << endl; | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment