Skip to content

Commit

Permalink
Support text file in classify-images (#42)
Browse files Browse the repository at this point in the history
* support txt file in classify-images

* type fix
  • Loading branch information
danvk authored Sep 22, 2024
1 parent 2f4981c commit edde21c
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 15 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ classification. The example above could be written as:
This will bring up a web server with a UI for assigning one of those two labels
to each image on your local file system. The results will go in `output.csv`.

Alternatively, you can also pass in a text file containing the paths to images.
In this case, the images can be URLs, rather than paths to local files.

For more details, run `classify-images --help`.

## Tips & Tricks
Expand Down
26 changes: 17 additions & 9 deletions classify-images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,28 @@ interface CLIArgs {
max_width?: number;
}

const program = new Command() as (Command & CLIArgs);
const program = new Command();
program
.version('2.1.1')
.usage('[options] /path/to/images/*.jpg')
.usage('[options] /path/to/images/*.jpg | images.txt')
.option('-o, --output <file>',
'Path to output CSV file (default output.csv)', 'output.csv')
.option('-l, --labels <csv>',
'Comma-separated list of choices of labels', list, ['Yes', 'No'])
.option('-w, --max_width <pixels>',
'Make the images this width when displaying in-browser', parseInt)
.parse(process.argv)
.parse()

if (program.args.length == 0) {
console.error('You must specify at least one image file!\n');
program.help(); // exits
}
const options = program.opts<CLIArgs>();
console.log(options.labels);

if (fs.existsSync(program.output)) {
if (fs.existsSync(options.output)) {
console.warn(dedent`
Output file ${program.output} already exists.
Output file ${options.output} already exists.
Its contents will be assumed to be previously-generated labels.
If you want to start from scratch, either delete this file,
rename it or specify a different output via --output`);
Expand All @@ -60,16 +62,22 @@ if (fs.existsSync(program.output)) {
const csvInfo = temp.openSync({suffix: '.csv'});
const templateInfo = temp.openSync({suffix: '.html'});

fs.writeSync(csvInfo.fd, 'path\n' + program.args.join('\n') + '\n');
const images = program.args;
if (images.length === 1 && images[0].endsWith('.txt')) {
fs.writeSync(csvInfo.fd, 'path\n');
fs.writeSync(csvInfo.fd, fs.readFileSync(images[0], 'utf8'));
} else {
fs.writeSync(csvInfo.fd, 'path\n' + images.join('\n') + '\n');
}
fs.closeSync(csvInfo.fd);

// Add keyboard shortcuts. 1=first button, etc.
const buttonsHtml = (program.labels as string[]).map((label, idx) => {
const buttonsHtml = options.labels.map((label, idx) => {
const buttonText = `${label} (${1 + idx})`;
return `<button type="submit" data-key='${1+idx}' name="label" value="${label}">${escape(buttonText)}</button>`;
}).join('&nbsp;');

const widthHtml = program.max_width ? ` width="${program.max_width}"` : '';
const widthHtml = options.max_width ? ` width="${options.max_width}"` : '';
const undoHtml = dedent`
</form>
<form action="/delete-last" method="POST" style="display: inline-block">
Expand All @@ -86,6 +94,6 @@ html += dedent`
fs.writeSync(templateInfo.fd, html);
fs.closeSync(templateInfo.fd);

const args = ['localturk', '--static-dir', '.', templateInfo.path, csvInfo.path, program.output];
const args = ['localturk', '--static-dir', '.', templateInfo.path, csvInfo.path, options.output];
console.log('Running ', args.join(' '));
child_process.spawn(args[0], args.slice(1), {stdio: 'inherit'});
15 changes: 9 additions & 6 deletions localturk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ interface CLIArgs {
writeTemplate: boolean;
}

const program = new Command() as (Command & CLIArgs);
const program = new Command();

program
.version('2.1.1')
Expand All @@ -54,9 +54,12 @@ program
'Serve images in random order, rather than sequentially. This is useful for ' +
'generating valid subsamples or for minimizing collisions during group localturking.')
.option('-w, --write-template', 'Generate a stub template file based on the input CSV.')
.parse(process.argv);
.parse();

const {args, randomOrder, writeTemplate} = program;
const options = program.opts<CLIArgs>();

const {args} = program;
const {randomOrder, writeTemplate} = options;
if (!((3 === args.length && !writeTemplate) ||
(1 === args.length && writeTemplate))) {
program.help();
Expand All @@ -68,10 +71,10 @@ if (writeTemplate) {
}

const [templateFile, tasksFile, outputsFile] = args;
const port = program.port || 4321;
const port = options.port || 4321;
// --static-dir is particularly useful for classify-images, where the template file is in a
// temporary directory but the image files could be anywhere.
const staticDir = program['staticDir'] || path.dirname(templateFile);
const staticDir = options['staticDir'] || path.dirname(templateFile);

type Task = {[key: string]: string};
let flash = ''; // this is used to show warnings in the web UI.
Expand All @@ -85,7 +88,7 @@ async function renderTemplate({task, numCompleted, numTotal}: TaskStats) {
// Note: these two fields are not available in mechanical turk.
fullDict['ALL_JSON'] = utils.htmlEntities(JSON.stringify(task, null, 2));
fullDict['ALL_JSON_RAW'] = JSON.stringify(task);
for (var [k, v] of Object.entries(program.var)) {
for (var [k, v] of Object.entries(options.var)) {
fullDict[k] = utils.htmlEntities(v as string);
}
const userHtml = utils.renderTemplate(template, fullDict);
Expand Down
3 changes: 3 additions & 0 deletions sample/image-urls.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://upload.wikimedia.org/wikipedia/commons/thumb/8/85/Tour_Eiffel_Wikimedia_Commons_%28cropped%29.jpg/432px-Tour_Eiffel_Wikimedia_Commons_%28cropped%29.jpg
https://upload.wikimedia.org/wikipedia/commons/d/d6/Liberty_and_Ellis_Island.jpg
https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Shea_Stadium_%282009%29.jpg/1024px-Shea_Stadium_%282009%29.jpg

0 comments on commit edde21c

Please sign in to comment.