Skip to content

Instantly share code, notes, and snippets.

@keot
Last active August 29, 2015 14:03
Show Gist options
  • Save keot/bf0b59156ed8c165541c to your computer and use it in GitHub Desktop.
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)
/*
* 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