UP | HOME
Jump to my Home Page Send me a message Check out stuff on GitHub Check out my photography on Instagram Check out my profile on LinkedIn Check out my profile on reddit Check me out on Facebook

Bari Wolf Meta Data

Author: Mitch Richling
Updated: 2023-10-11

Copyright 2023 Mitch Richling. All rights reserved.

Table of Contents

1. FAQ

1.1. Misc

1.1.1. Q: What is PhilaJ?

1.1.2. Q: What is with the crazy DPI of 2398?

When set to 2400 DPI, my scanner produces images that are 2410 DPI horizontally by 2398 DPI vertically at the center of the platen. The vertical DPI is consistent across the entire platen, but the horizontal DPI is variable. So I scan everything on the middle third of my scanner platen. Then I pull the images into ImageJ/Fiji and use PhilaJ to resize the images so the pixels are square – which produces a uniform 2398 DPI image. This process also creates the 300 DPI thumbnails.

1.1.3. Q: Isn't 2400 DPI excessive?

The "cost" of scanning at 2400 DPI isn't high – it takes a tiny bit longer to scan and disk space is cheap. The benefit to linear measurement accuracy is well worth the cost in my estimation. If I didn't have access to lots of storage space I might feel differently. It also makes fly-specking easier.

1.1.4. Q: Are your perforation measurements really accurate to 3 decimal places?

No.

They are good to about 1 decimal place, and most of the error is human derived.

1.1.5. Q: Can I use your color measurements with my scanner to identify shades?

No.

With regard to color my scanner is very precise but also not very accurate. On the accuracy front, my scanner is not color calibrated making measurements difficult to translate to other scanners. On the precision front, I take periodic measurements of a "standard" target to insure that the measurement variance is minimal – in fact the drift has been so small as to be difficult to measure. So my color measurements are very useful for me in that they allow me to compare shades, but probably not very useful to you. Sorry about that.

1.2. Organizational Questions

1.2.1. Q: Do you use a DB to track your collection?

Not for my Bari Wolf collection – everything is in org-mode files. Stamp data are located in album.org & stock.org while a few auxiliary data tables may be found in data.org. In addition, some elisp code may be found in code.org, and some macros may be found in macros.org. For details about how to download this stuff, see the question Q: May I download your data and/or code?

1.2.2. Q: May I download your data and/or code?

Yes. The org-mode files, generated HTML, and images are linked downloads page. If you just want to import into Excel one of the auxiliary data tables in data.org, then you can copy a table from the HTML version of data.org and paste it directly into Excel.

If you publish anything using this material, then please also publish with it a link to this site: https://www.mitchr.me/SS/philBariWolf/index.html

1.2.3. Q: What are those weird titles? The ones that look like "bwID...."?

Those are inventory IDs. I assign one to each item in my collection. They all look like this:

bwID<TAG><ITEM_NUMBER>p<PLATE_POSITION>
    |    |            ||
    |    |            |+- This is the position number on the plate.  It is 3 digits, zero padded if required.  Only exists if "p" is given.
    |    |            +-- The "p" is optional.  It is used to indicate that a plate position is known, and follows.
    |    +--------------- This is a three digit item number -- zero padded if required.
    +-------------------- This is a descriptive tag that identifies the type of object.

1.2.4. Q: What do the inventory ID tags mean?

For info about IDs in general, see the question Q: What are those weird titles? The ones that look like "bwID…."?.

Prefix Description
esy Essay
p2s Printed on both sides
samp Overprint: Specimens
pdue Overprint: Postage Due
repb overprint: REPUBBLICA
ovrc overprint: Red Cross
misbg Missing background
misfg Missing foreground
bh Background Horizontal
dbl Double impression
idbl Double impression with one inverted
drkw Color: dark wolf
drkb Color: dark burelage
shade Color: shade
sgl Typical examples
imph imperforate horizontally
impv Imperforate_Vertically
imp Imperforate
imps Imperforate One Side
ps Perforate One Side
ruf Rough Perforations
pfrk Perforation Freaks
qbu Block of 4 with all burelage – Not Watermarked
qbw Block of 4 with all burelage – Watermarked
pf Plate Flaw
sheetw Watermarked Full Sheet
sheetu Unwatermarked Full Sheet

1.2.5. Q: How do you keep track of the last number used for each prefix?

1.2.6. Q: How do you keep track of the physical locations of items?

I keep track of where things are physically located by assigning each inventory ID a location code that looks like this:

Ap##r#c#
| |  | |
| |  | +- Column number ..... One digit
| |  +--- Row number ........ One digit
| +------ Page number ....... Two digits
+-------- The album/book .... "C" for my main collection & "S" for my stock book.

The data.org file has a table mapping inventory ID to physical location.

1.2.7. Q: What about when you move things around in your album?

I have a script for that:

#!/usr/bin/env ruby



require 'fileutils'
require 'set'
require 'optparse'
require 'optparse/time'

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
# Print stuff to STDOUT immediatly -- important on windows
$stdout.sync = true

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
debug         = false;
takeAction    = true;
printRules    = false;
opts = OptionParser.new do |opts|
  opts.banner = "Usage: rearrangeAlbum.rb [options] <rules>"
  opts.separator ""
  opts.separator "Options:"
  opts.on("-h",  "--help",          "Show this message")          { puts opts; exit           }
  opts.on("-d",  "--debug",         "Debug")                      { |v| debug = true;         }
  opts.on("-p",  "--print",         "Print Rules")                { |v| printRules = true;    }
  opts.on("-n",  "--dry-run",       "Don't do anything")          { |v| takeAction = false;   }
  opts.separator "                                                                              "
  opts.separator "  Move Rules                                                                  "
  opts.separator "   - Ap#r#c#=Ap#r#c# ... Replace first position with second                     "
  opts.separator "   - Ap#r#c#/Ap#r#c# ... Swap positions                                         "
  opts.separator "   - Ap#r#=Ap#r# ....... Replace first row with second                          "
  opts.separator "   - Ap#r#/Ap#r# ....... Swap rows positions                                    "
  opts.separator "   - Ap#r#c#+ .......... Increment column numbers (insert a column after p#r#c#)"
  opts.separator "   - Ap#r#+ ............ Increment row numbers (insert a row after p#r#)        "
  opts.separator "   - Ap#+ .............. Increment page numbers (insert a page after p#)        "
  opts.separator ""
end
opts.parse!(ARGV)
ruleStrings = ARGV.clone

# Files with content to process  -- we only edit one file... ;)
orgFiles = Dir.glob('data.org');
if (debug) then
  puts("DEBUG: ORG FILES: #{orgFiles.inspect}");
end

# Known locations
locRe = Regexp.new('(([CS])p(\d{1,3})r(\d{1,2})c(\d{1,2}))');
knownLocs = Set.new;
orgFiles.each do |fileName|
    open(fileName, "rb") do |iFile|
      fileData = iFile.read()
      fileData.scan(locRe) do |mDat|
        knownLocs.add(mDat[0]);
      end
    end
end
if (debug) then
  puts("DEBUG: KNOWN LOCATIONS: #{knownLocs.inspect}");
end

moveSwapRule  = Regexp.new('^([CS])p(\d{1,3})r(\d{1,2})(c(\d{1,2})){0,1}(=|/)([CS])p(\d{1,3})r(\d{1,2})(c(\d{1,2})){0,1}$');
incrementRule = Regexp.new('^([CS])p(\d{1,3})(r(\d{1,2})|r(\d{1,2})c(\d{1,2})){0,1}([+-])$');
moveRules = Hash.new;
ruleStrings.each do |ruleString|
  if (mDat=ruleString.match(moveSwapRule)) then
    lhsA = mDat[1];
    lhsP = mDat[2];
    lhsR = mDat[3];
    lhsC = mDat[5];
    opr  = mDat[6];
    rhsA = mDat[7];
    rhsP = mDat[8];
    rhsR = mDat[9];
    rhsC = mDat[11];
    if ((lhsC.nil? || rhsC.nil?) && ( !(lhsC.nil? && rhsC.nil?))) then
      puts("ERROR: Malformed rule: '#{ruleString}'");
      exit(1);
    end
    knownLocs.each do |loc|
      locMatchDat = loc.match(locRe);
      locA = locMatchDat[2];
      locP = locMatchDat[3];
      locR = locMatchDat[4];
      locC = locMatchDat[5];
      if ((lhsA == locA) && (lhsP == locP) && (lhsR == locR) && (lhsC.nil? || (lhsC == locC))) then
        if (moveRules.member?(loc)) then
          puts("ERROR: Move klobbers '#{loc}': '#{ruleString}'");
          exit(1);
        end
        moveRules[loc] = "#{rhsA}p#{rhsP}r#{rhsR}c#{rhsC || locC}";
      end
      if (opr == "/") then
        if ((rhsA == locA) && (rhsP == locP) && (rhsR == locR) && (rhsC.nil? || (rhsC == locC))) then
          if (moveRules.member?(loc)) then
            puts("ERROR: Move klobbers '#{loc}': '#{ruleString}'");
            exit(1);
          end
          moveRules[loc] = "#{lhsA}p#{lhsP}r#{lhsR}c#{lhsC || locC}";
        end
      end
    end
  elsif (mDat=ruleString.match(incrementRule)) then # inc rule
    lhsA = mDat[1];
    lhsP = mDat[2];
    lhsR = mDat[4] || mDat[5];
    lhsC = mDat[6];
    opr  = mDat[7];

    lhsP = lhsP.to_i;
    lhsR = (lhsR.nil? ? lhsR : lhsR.to_i);
    lhsC = (lhsC.nil? ? lhsC : lhsC.to_i);
    delt = (opr == "+" ? +1 : -1);

    knownLocs.each do |loc|
      locMatchDat = loc.match(locRe);
      locA = locMatchDat[2];
      locP = locMatchDat[3].to_i;
      locR = locMatchDat[4].to_i;
      locC = locMatchDat[5].to_i;
      newA = locA;
      newP = locP;
      newR = locR;
      newC = locC;
      if (!lhsC.nil?) then
        newC = ( ((locC >= lhsC) && (locR == lhsR) && (locP == lhsP) && (locA == lhsA)) ? newC+delt : newC);
      elsif (!lhsR.nil?) then
        newR = ( ((locR >= lhsR) && (locP == lhsP) && (locA == lhsA)) ? newR+delt : newR);
      else
        newP = ( ((locP >= lhsP) && (locA == lhsA)) ? newP+delt : newP);
      end
      if ((newP != locP) || (newR != locR) || (newC != locC)) then
        if ((newP == 0) || (newR == 0) || (newC == 0)) then
          puts("ERROR: Move to zero! '#{loc}': '#{ruleString}'");
          exit(1);
        end
        if (moveRules.member?(loc)) then
          puts("ERROR: Move klobbers '#{loc}': '#{ruleString}'");
          exit(1);
        end
        moveRules[loc] = "#{newA}p#{'%02d' % newP}r#{newR}c#{newC}";


      end
    end
  else
    puts("ERROR: Move rule invalid: '#{ruleString}'");
    exit(1);
  end
end

# All the move rules
if (printRules || debug) then
  moveRules.each do |from, to|
    if (debug) then
      puts("DEBUG: MOVE RULE: '#{from}' -> '#{to}'");
    else
      puts("INFO: MOVE RULE: '#{from}' -> '#{to}'");
    end
  end
end

# Move regular expression -- matches all the "from" IDs
moveRulesRe = Regexp.new('(' + moveRules.keys.join('|') + ')');
if (debug) then
  puts("DEBUG: MOVE RULES RE: '#{moveRulesRe.inspect}'");
end

# Backup file name ext
backupExt = ".bak-" + Time.now.strftime('%Y%m%d%H%M%S')
puts("INFO: BACKUP EXT: '#{backupExt}'");

# Make sure none of the backup files already exists
orgFiles.each do |file|
  backupFileName = file + backupExt;
  if (FileTest.exist?(backupFileName)) then
    puts("ERROR: Backup file already exists: '#{backupFileName}'");
    exit(1);
  end
end

# Make backups (org)
orgFiles.each do |file|
  backupFileName = file + backupExt;
  if (takeAction) then
    if (debug) then
      puts("DEBUG: Make backup: '#{file}'");
    end
    FileUtils.copy(file, backupFileName);
    if ( !(FileTest.exist?(backupFileName))) then
      puts("ERROR: Backup file could not be created: '#{backupFileName}'");
      exit(1);
    end
  else
    if (debug) then
      puts("DEBUG: (SKIP) Make backup: '#{file}'");
    end
  end
end

# Remove origional files
orgFiles.each do |file|
  if (takeAction) then
    FileUtils.rm(file);
    if (debug) then
      puts("DEBUG: Delete original: '#{file}'");
    end
    if (FileTest.exist?(file)) then
      puts("ERROR: Failed to remove origional: '#{file}'");
      exit(1);
    end
  else
    if (debug) then
      puts("DEBUG: (SKIP) Delete original: '#{file}'");
    end
  end
end

# Process org files
orgFiles.each do |file|
  backupFileName = file + backupExt;
  if (takeAction) then
    fileChanged = false;
    open(backupFileName, "rb") do |iFile|
      fileData = iFile.read();
      newFileData = fileData.gsub(moveRulesRe, moveRules);
      fileChanged = (fileData != newFileData);
      open(file, "wb") do |oFile|
        oFile.write(newFileData)
      end
    end
    puts("INFO: edit: #{file.inspect} #{(fileChanged == false ? '(NO CHANGE)' : '(CHANGED)')}");
    if (debug) then
      puts("DEBUG: Process Org File: '#{file}'");
    end
  else
    puts("INFO: SKIP edit: #{file.inspect}");
    if (debug) then
      puts("DEBUG: (SKIP) Process Org File: '#{file}'");
    end
  end
end

1.2.8. Q: What about when you want to change the name of a stamp image?

I have a script for that too:

#!/usr/bin/env ruby

require 'set'
require 'fileutils' 
require 'optparse'
require 'optparse/time'

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
# Print stuff to STDOUT immediatly -- important on windows
$stdout.sync = true

puts(ARGV.inspect)

#---------------------------------------------------------------------------------------------------------------------------------------------------------------
patternFrom   = nil;
patternTo     = nil;
patternFilter = nil;
contentFilter = nil;
debug         = false;
takeAction    = true;
noKlober      = true;
magicFromRe   = false;
opts = OptionParser.new do |opt|
  opt.banner = "Usage: renameFile.rb <options>"
  opt.separator ""
  opt.separator "Options:"
  opt.on("-h",        "--help",          "Show this message")         { puts opt; exit;            }
  opt.on("-f regex",  "--from regex",    "From regex")                { |v| patternFrom = v;       }
  opt.on("-t regex",  "--to string",     "To String")                 { |v| patternTo = v;         }
  opt.on("-f regex",  "--filter regex",  "Filter regex")              { |v| patternFilter = v;     }
  opt.on("-e regex",  "--edit regex",    "Edit files matching regex") { |v| contentFilter = v;     }
  opt.separator "         Only operate on files matching filter                                    "
  opt.on("-d",        "--debug",         "Debug")                     { |v| debug = true;          }
  opt.on("-n",        "--dry-run",       "Don't do anything")         { |v| takeAction = false;    }
  opt.on("-k",        "--klobber",       "Clobber files if required") { |v| noKlober = false;      }
  opt.on("-m",        "--magic",         "Magically re-ize --from")   { |v| magicFromRe = false;   }
  opt.separator "         SAME THING:                                                              "
  opt.separator "          - ./renameFile.rb         --from 'p1s(.)r1c1' --to 'bwIDfoo001-\\1'     "
  opt.separator "          - ./renameFile.rb --magic --from 'p1s2r1c1'   --to 'bwIDfoo001-\\2'     "
  opt.separator "                                                                                  "
  opt.separator "Rename files:                                                                     "
  opt.separator "  If provided, --filter specifies eligible files that might be renamed.  If not   "
  opt.separator "  provided, then all files in the current working directory are considered        "
  opt.separator "  eligible.  If a eligible file matches the --from regex, the matching part is    "
  opt.separator "  replaced by the --to string.  Any files that match the --content regex are      "
  opt.separator "  edited so that any string matching one of the renamed files is transformed into "
  opt.separator "  the the new name.  Note files matching --content regex are NEVER eligible for   "
  opt.separator "  being renamed.                                                                  "
  opt.separator "                                                                                  "
end
opts.parse!(ARGV)

if (patternTo.nil?) then
  puts("ERROR: Missing argument: --to!");
  exit(1);
end

if (patternFrom.nil?) then
  puts("ERROR: Missing argument: --from!");
  exit(1);
end

if (debug) then
  puts("DEBUG: Options: patternFrom   #{patternFrom.inspect}  ");
  puts("DEBUG: Options: patternTo     #{patternTo.inspect}    ");
  puts("DEBUG: Options: patternFilter #{patternFilter.inspect}");
  puts("DEBUG: Options: contentFilter #{contentFilter.inspect}");
  puts("DEBUG: Options: debug         #{debug.inspect}        ");
  puts("DEBUG: Options: takeAction    #{takeAction.inspect}   ");
  puts("DEBUG: Options: noKlober      #{noKlober.inspect}     ");
  puts("DEBUG: Options: magicFromRe   #{magicFromRe.inspect}  ");
end

oldStyleLocNumRe = /^p([0-9]+)s([X0-9])r([0-9])c([0-9])$/;

if (magicFromRe && (tmp = patternFrom.match(oldStyleLocNumRe))) then
  patternFrom = "p(#{tmp[1]})s([X0-9])r(#{tmp[3]})c(#{tmp[4]})";
end
patternFrom = Regexp.new(patternFrom);

if (debug) then
  puts("DEBUG: patternFrom: #{patternFrom.inspect}");
  puts("DEBUG: patternTo: #{patternTo.inspect}");
end

# Find files that we need to move
filesToMove = Hash.new();
targetFiles = Hash.new();
Dir.foreach('.') do |fileName| 
  if ( !(fileName.match(/(^\.|\.\.|.bak-[0-9]+)$/))) then
    if (patternFilter.nil? || fileName.match(patternFilter)) then
      if (fileName.match(patternFrom)) then
        if ( contentFilter.nil? || ( !(fileName.match(contentFilter)))) then
          newFileName = fileName.gsub(patternFrom, patternTo);
          filesToMove[fileName] = newFileName;
          if (FileTest.exist?(newFileName)) then
            if (noKlober) then
              puts("ERROR: File would get clobbered: '#{newFileName}' by '#{fileName}'!");
              exit(1);
            else 
              puts("WARNING: File will get clobbered: '#{newFileName}' by '#{fileName}'!");
            end
          end
          if (targetFiles.member?(newFileName)) then
            puts("ERROR: File would get clobbered: '#{newFileName}' mapped to both '#{fileName}' and '#{targetFiles[newFileName]}'!");
            exit(1);
          end
          targetFiles[newFileName] = fileName;
        else
          if ( !(patternFrom.nil?)) then
            puts("WARNING: File not being renamed because it matched --content regex: '#{fileName}'!");
          end
        end
      end
    end
  end
end
if (filesToMove.empty?) then
  puts("ERROR: No files found mattching pattern");
  exit(1);
end
if (debug) then
  puts("DEBUG: filesToMove: #{filesToMove.inspect}");
end

# Create regex we use for editing org files later
targetRegex = Regexp.union(*filesToMove.keys)
if (debug) then
  puts("DEBUG: targetRegex: #{targetRegex.inspect}");
end

# Find files we *might* need to edit
filesToEdit = Set.new();
if ( !(contentFilter.nil?)) then
  Dir.foreach('.') do |fileName| 
    if (fileName.match(contentFilter)) then
      filesToEdit.add(fileName);
    end
  end
end
  if (debug) then
  puts("DEBUG: filesToEdit: #{filesToEdit.inspect}");
end

# Need a nice backup extention
backupTim = Time.now.strftime('%Y%m%d%H%M%S');
backupExt = ".bak-#{backupTim}"
puts("INFO: Backup Time Stamp: #{backupTim}");
if (debug) then
  puts("DEBUG: backupExt: #{backupExt.inspect}");
end

# Backup all the files we might modify or move
allFiles = filesToMove.keys + filesToEdit.to_a;
if (debug) then
  puts("DEBUG: allFiles: #{allFiles.inspect}");
end
allFiles.each do |fileName|
  backupFileName = fileName + backupExt;
  if (FileTest.exist?(backupFileName)) then
    puts("ERROR: Backup file already exists: '#{backupFileName}'!");
    exit(1);
  end
  if (takeAction) then
    FileUtils.cp(fileName, backupFileName);
    if (debug)
      puts("DEBUG: backup file: #{fileName.inspect} => #{backupFileName.inspect}");
    end
    if ( !(FileTest.exist?(backupFileName))) then
      puts("ERROR: Backup file could not be created: '#{backupFileName}'!");
      exit(1);
    end
  else
    if (debug)
      puts("DEBUG: SKIP backup file: #{fileName.inspect} => #{backupFileName.inspect}");
    end
  end
end

filesToMove.each do |fileName, newFileName|
  backupFileName = fileName + backupExt;
  if (takeAction) then
    puts("INFO: move: #{fileName.inspect} => #{newFileName.inspect}");
    FileUtils.rm(fileName);
    if (debug)
      puts("DEBUG: remove origional file: #{fileName.inspect}");
    end
    if (FileTest.exist?(fileName)) then
      puts("ERROR: failed to remove origional file: '#{fileName}'!");
      exit(1);
    end
    FileUtils.cp(backupFileName, newFileName);
    if (debug)
      puts("DEBUG: cp backup to new file name: #{backupFileName.inspect} => #{newFileName.inspect}");
    end
    if ( !(FileTest.exist?(newFileName))) then
      puts("ERROR: Failed to copy back to new file name: '#{fileName}' => '#{newFileName}'!");
      exit(1);
    end
  else
    puts("INFO: SKIP move: #{fileName.inspect} => #{newFileName.inspect}");
    if (debug)
      puts("DEBUG: SKIP cp backup file to new name: #{backupFileName.inspect} => #{newFileName.inspect}");
    end
  end
end

filesToEdit.each do |fileName|
  if (takeAction) then    
    changed = nil;
    leftovr = nil;
    backupFileName = fileName + backupExt;
    open(backupFileName, "rb") do |inFile|
      fileData = inFile.read;
      changed = fileData.gsub!(targetRegex, filesToMove)
      leftovr = fileData.match(patternFrom)
      open(fileName, "wb") do |outFile|
        outFile.write(fileData);
      end
    end
    changed = (changed.nil? ? '(NO CHANGE)' : '(CHANGED)');
    leftovr = (leftovr.nil? ? ''            : " -- WARNING: Still matches #{patternFrom.inspect}");
    puts("INFO: edit: #{fileName.inspect} #{changed} #{leftovr}");
    if (debug)
      puts("DEBUG: edit file: #{fileName.inspect}");
    end
  else
    puts("INFO: SKIP edit: #{fileName.inspect}");
    if (debug)
      puts("DEBUG: SKIP edit file: #{fileName.inspect}");
    end
  end
end

1.2.9. Q: Those scripts are great, but what about when things go wrong?

The scripts make backups before they do anything. I have to admit I have needed to restore enough that I have a script to make it easy to restore from those backup files. Here it is:

#!/bin/sh

BKPFX="$1"

for f in *.bak-$1; do 
  if [ -e "$f" ]; then
    nf=$(echo $f | sed "s/\.bak-$1$//")
    echo cp $f $nf
    cp $f $nf
  fi
done

1.2.10. Q: How do you keep track of stamp image files?

I use a naming convention to keep track of stamp images:

Name Description
INVID-1-scan_2398dpi.png Side one raw scan data
INVID-2-scan_2398dpi.png Side two raw scan data
INVID-1_2398dpi.png Side one
INVID-2_2398dpi.png Side two
INVID-1-pflaws_2398dpi.png Side one plate flaws
INVID-2-pflaws_2398dpi.png Side two plate flaws
INVID-X-front-back_2398dpi.png Side one and side two superimposed
INVID-1-overprint_2398dpi.png Side one overprint
INVID-1-faults_2398dpi.png Side one Faults
INVID-1-cancel_2398dpi.png Side one cancel
INVID-1-design_2398dpi.png Side one cropped to design + 1mm border
INVID-1-expertmark_2398dpi.png Side one expert marks (like signatures)
INVID-2-expertmark_2398dpi.png Side two expert marks (like signatures)
INVID-1-stack.tif Stack of side one design files
INVID-1.tif Side one raw scan
INVID-1.tif Side two raw scan

The "INVID" is an inventory number or an inventory number followed by a position (like "p144"). The master images are 2398dpi, but secondary images are generated at "300dpi" and "90dpi".

I also have a script that identifies image files not used by any org-mode file:

#!/usr/bin/env ruby

require 'set'
require 'fileutils' 

# Find every png file in the current working directory that is not on
# the ignore list, and list the files not found in any org file in the
# current working directory.

debug  = false;
ignore = Regexp.union(/^STAMP-MIA-[12]_.*\.png$/,                                # MIA images for missing stamps
                      /^background_T[1234]_t\.png$/,                             # Source images
                      /^background_types\.png$/,                                 # Source image
                      /^bwIDsheet[uw]001p[0-9]{3}-1-design_(300|2398)dpi\.png$/, # Larger "design" images
                      /^bwIDsheet[uw]001p[0-9]{3}-1_(90|300)dpi\.png$/,          # Smaller "stamp" images
                      /^PFTHM-HWW[0-1][0-9]_300dpi\.png$/,                       # Larger HM plate flaw images
                      /^bwID.*-[12]-pflaws_90dpi.png$/,                          # 90 DPI previews of plate flaws
                      /^bwID.*-2_90dpi.png$/,                                    # 90 DPI previews of side 2
                      /^bwID.*-1-overprint_90dpi.png$/,                          # 90 DPI previews of overprints
                      /^bwID.*-1-cancel_90dpi.png$/,                             # 90 DPI previews of cancels
                      /^bwID.*-[12]-expertmark_90dpi.png$/,                      # 90 DPI previews of expert marks
                      /^bwID.*-[12]-faults_90dpi.png$/,                          # 90 DPI previews of faults
                      /^bwID.*-X-front-back_90dpi.png$/                          # 90 DPI previews of front/back composites
                     );

# Find all the PNG files
allPNGfiles = Set.new();
Dir.foreach('.') do |fileName| 
  if (fileName.match(/\.png$/)) then
    allPNGfiles.add(fileName);
  end
end
if (allPNGfiles.empty?) then
  puts("ERROR: No PNG files found");
  exit(1);
end
if (debug) then
  puts("DEBUG: allPNGfiles: #{allPNGfiles.inspect}");
end

# Filter out the ignored ones
# We do this as a separate step so we can see the delta
filteredPNGfiles = Set.new();
allPNGfiles.each do |fileName| 
  if ( !(fileName.match(ignore))) then
    filteredPNGfiles.add(fileName)
  end
end
if (debug) then
  puts("DEBUG: filteredPNGfiles: #{allPNGfiles.inspect}");
end

# Find files we need to search
filesToSearch = Set.new();
Dir.foreach('.') do |fileName| 
  if (fileName.match(/\.*.org$/)) then
    filesToSearch.add(fileName);
  end
end
if (debug) then
  puts("DEBUG: filesToSearch: #{filesToSearch.inspect}");
end

# Figure out which png files were used
usedPNGfiles = Set.new();
filesToSearch.each do |fileName|
  open(fileName, "rb") do |inFile|
    fileData = inFile.read;
    filteredPNGfiles.each do |fn|
      if (fileData.match(fn)) then
        usedPNGfiles.add(fn);
      end
    end
  end
end

# Compute the set difference, and display unused files.
filteredPNGfiles.subtract(usedPNGfiles).each do |fileName|
  puts(fileName)
end

1.3. Creating images & stuff

1.3.1. Q: How do you make the "design" cropped images?

I use PhilaJ. See the following question: Q: What is PhilaJ?.

1.3.2. Q: How do you make plate flaw images?

I use PhilaJ. See the following question: Q: What is PhilaJ?.

1.3.3. Q: How do you make those 300 & 90 DPI thumbnails?

I use PhilaJ. See the following question: Q: What is PhilaJ?.

They can also be created on the command line from the 300 DPI images like this:

for f in bwID*_300dpi.png; do
  nfn=${f/300/90}
  if [ ! -e $nfn -o $f -nt $nfn ]; then
    convert $f -resize 30% ${f/300/90}
  fi
done

1.3.4. Q: How do you create those sideways image labels?

for a in 'EXPERT MARK' 'FAULT' 'BACK' 'CANCEL' 'COMPOSITE'     \
         'FRONT' 'OVERPRINT' 'HANDSTAMP' 'PLATE FLAWS FRONT' 'OBLIQUE'     \
         'PLATE FLAWS BACK' 'SHEET VISUAL INDEX' 'CERT'; do
  fn='ANNO-'`echo "$a" | tr ' ' '_'`'.png'
  if [ ! -e $fn ]; then
    echo MAKE $fn
    convert -size 350x50 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "$a" -rotate 270 $fn
  fi
done

1.3.5. Q: How do you create the dummy stamp images?

fn='STAMP-MIA-1_300dpi.png'
convert -size 340x380   xc:black -pointsize 30 -gravity center -fill red -annotate 0 "MIA" $fn
fn='STAMP-MIA-1_90dpi.png'
convert -size 103x115   xc:black -pointsize 30 -gravity center -fill red -annotate 0 "MIA" $fn
fn='STAMP-MIA-1_2398dpi.png'
convert -size 2730x3060 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "MIA" $fn
fn='STAMP-MIA-2_300dpi.png'
convert -size 340x380   xc:black -pointsize 30 -gravity center -fill red -annotate 0 "MIA" $fn
fn='STAMP-MIA-2_90dpi.png'
convert -size 103x115   xc:black -pointsize 30 -gravity center -fill red -annotate 0 "MIA" $fn
fn='STAMP-MIA-2_2398dpi.png'
convert -size 2730x3060 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "MIA" $fn

1.3.6. Q: How do you create the section images?

for a in 'Printed Both Sides' 'All Four Burelage Types' \
         'Interesting Plate Flaws' 'Large Multiples' 'Overprints & Handstamps' \
         'Color Varieties - Shades' 'Color Varieties - Dark Wolf' 'Color Varieties - Dark Burelage' \
         'Missing Foreground' 'Missing Background' 'Double Impressions' 'Inverted Double Impressions' \
         'Colors & Shades' 'Typical Examples' 'Perforation Oddities' \
         'Perforation Oddities - Imperforate Horizontally' 'Perforation Oddities - Imperforate Vertically' \
         'Perforation Oddities - Imperforate One Side' 'Perforation Oddities - Imperforate' \
         'Perforation Oddities - Rough Perforations' 'Essay' 'Perforation Oddities - Freaks' \
         'Perforation Oddities - Perforate One Side' 'Background Horizontal'; do
  fn='HEAD-'`echo "$a" | tr ' ' '_'`'.png'
  if [ -e $fn ]; then
    echo SKIP $fn
  else
    echo MAKE $fn
    convert -size 1224x50 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "$a" $fn
   fi
done

1.3.7. Q: How do you create the separator bar image?

if [ -e HEAD-BREAK.png ]; then
  echo SKIP HEAD-BREAK.png
else
  echo MAKE HEAD-BREAK.png
  convert -size 1024x50 xc:black HEAD-BREAK.png
fi

1.3.8. Q: How do you create stamp Sheet Labels?

for a in $(seq 10 10 150); do
  pos=$(printf '%03d' $a)
  fn="SHEET150V-$pos.png"
  convert -size 90x104 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "$pos" $fn
done
for a in $(seq 12 12 168); do
  pos=$(printf '%03d' $a)
  fn="SHEET168V-$pos.png"
  convert -size 90x104 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "$pos" $fn
done
for a in $(seq 1 12); do
  pos=$(printf '%03d' $a)
  fn="SHEET168H-$pos.png"
  convert -size 90x104 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "$pos" $fn
done
for a in $(seq 1 10); do
  pos=$(printf '%03d' $a)
  fn="SHEET150H-$pos.png"
  convert -size 90x104 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "$pos" $fn
done
convert -size 90x104 xc:black SHEET150X-XXX.png
convert -size 90x104 xc:black SHEET168X-XXX.png

1.3.9. Q: How do you stamp album page and row labels?

for a in $(seq 1 9); do
  row=$(printf '%d' $a)
  fn="ROW-$row.png"
  if [ -e $fn ]; then
    echo SKIP $fn
  else
    echo MAKE $fn
    convert -size 90x104 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "ROW\n$row" $fn
  fi
done
for a in $(seq 1 30); do
  page=$(printf '%02d' $a)
  fn="PAGE-$page.png"
  if [ -e $fn ]; then
    echo SKIP $fn
  else
    echo MAKE $fn
    convert -size 1224x50 xc:black -pointsize 30 -gravity center -fill red -annotate 0 "PAGE: $a" $fn
  fi
done

1.3.10. Q: How to you overlay position numbers on stamp images?

# For sheet001
for f in bwIDsheetw001p*-1-design_90dpi.png; do
  p=$(echo $f | sed 's/^.*001p//; s/-.*$//')
  nf=PNO-$f
  if [ -e $nf ]; then
    echo SKIP $nf
  else
    echo MAKE $nf
    convert $f -pointsize 50 -gravity center -fill red -annotate 0 $p $nf
  fi
done
#for sheet002
for f in bwIDsheetu001p*-1-design_90dpi.png; do
 p=$(echo $f | sed 's/^.*002p//; s/-.*$//')
 nf=PNO-$f
  if [ -e $nf ]; then
    echo SKIP $nf
  else
    echo MAKE $nf
    convert $f -pointsize 50 -gravity center -fill red -annotate 0 $p $nf
  fi
done

1.3.11. Q: How do you make an image of a sheet from individual stamps?

Note the stamps must be cropped correctly for this to work – they all need to be about the same size.

magick montage -mode concatenate -tile 10x15 -resize 50% bwIDsheetw001p*-1-design_90dpi.png bwIDsheetw001-1_90dpi.png
magick montage -mode concatenate -tile 12x14 -resize 50% bwIDsheetu001p*-1-design_90dpi.png bwIDsheetu001-1_90dpi.png

1.3.12. How were the background types images created

I took an image of a stamp with just a background, and annotated the important bits. Then I flipped it around to create the four orientations. This crated the high resolution versions. Then I created the thumbnails like this:

for f in background_T?.png; do
  f_preview=${f/.png/_p.png}
  if [ -e "$f_preview" ]; then
    echo SKIP PREVIEW $f
  else
    echo MAKE PREVIEW $f
    convert $f -resize 20% $f_preview
  fi;
  f_thumb=${f/.png/_t.png}
  if [ -e "$f_thumb" ]; then
    echo SKIP THUMB $f
  else
    echo MAKE THUMB $f
    convert $f -resize 5% $f_thumb
  fi;
done

1.3.13. Your raw scans are .tif files, but they show up as .png in the data directory. How?

I convert them after I process the images. I don't do that with ImageJ/Fiji, but with ImageMagick from the command line.

time ls *.tif | grep -v stack.tif | xargs -P 8 -I{} sh -c 'echo {}; convert {} $(echo {} | sed "s/\.tif$/-scan.png/;")';

2. To Do

I don't plan on ever being "done" with this project, but here are a few of the higher priority things on my list:

  • Items with missing data:
    • bwIDmisbg004-2_2398dpi.png: Add expert mark image
    • bwIDdbl005-2_2398dpi.png: Add expert mark image
    • bwIDidbl001-2_2398dpi.png: Add expert mark image
    • bwIDdrkw001-2_2398dpi.png: Add expert mark image
    • bwIDimp003-2_2398dpi.png: Add expert mark image
    • bwIDimp001-2_2398dpi.png: Add expert mark image
    • bwIDsheetw001p089-1_2398dpi.png: Rescan
    • bwIDimps001-2_2398dpi.png: Add sheet location
    • bwIDsgl071-1_2398dpi.png: Add cancel image
  • Plate flaws stuff
    • Add little thumbnails to my plate flaw table
    • Visual guide to plate flaws
      • Sections with plate flaw number
        • Has a list of basic plate flaw data along with thumbnail
        • All thumbnails of stamps with the same flaw – which link to plate flaw image
  • Data to add
    • Paper thickness data
    • Centering data
    • Scarcity and pricing data
    • Plate flaws

3. Change Log

3.1. 2023-10-11

  • Added bwIDp2s019 (page 4, row 5, column 1)
  • Added bwIDps001 (page 10, row 2, column 1)
  • Moved items on page 4, row 3 up to row 2 (rearrangeAlbum.rb -p Cp04r3=Cp04r2)
  • Additional notes on printing variations
  • Better Bush cross reference for printing variations
  • Made section images and page images wider to accomidate wider page

3.2. 2023-10-04

  • Moved items on page 4 down one row (rearrangeAlbum.rb -p Cp04r2+)
  • Moved items on page 3, row 5 to page 4 row 1 (rearrangeAlbum.rb -p Cp03r5=Cp04r1)
  • Moved items on page 3, row 3 & 4 down one row (rearrangeAlbum.rb -p Cp03r3+)
  • Added new category of stamps: ps – Perforate One Side
  • Added new category of stamps: bh – Background Horizontal
  • Fixed some typos in the album-org table
  • Added new stamp: bwIDp2s018 to page 3, row 3
  • Added new stamp: bwIDbh001 to page 2, row 2
  • New printed both sides variety: dWdB0WnB
  • Updated "My Potential Plate Flaw List" to include sheet type instead of watermark

3.3. 2023-09-17

  • Added bwIDpfrk001 to album
  • Moved everything on page 11 to 12, and everything on page 10 to 11
  • Created new catalog number category: bwIDpfrk for freak perfs
  • Add new plate flaw: pf0038
  • Put all the code in git
  • Converted scan data from TIFF to PNG

3.4. 2023-07-24

  • Added bwIDimpv004 to album
  • Moved bwIDimpv003 to stock
  • Added bwIDidbl003 to album
  • Added bwIDp2s017 to album
  • Fixed bug with sheet index links

3.5. 2023-03-26

  • Fixed a ton of misspellings & a few factual errors. Thank you, Carl Scheriani!

3.6. 2023-02-19

  • Added paper thickness measurements to bwIDesy001 & bwIDesy002
  • Added paper thickness measurement notes to notes.html

3.7. 2023-02-18

  • Added bwIDesy002

3.8. 2022-12-05

3.9. 2022-12-01

  • Added bwIDsgl078
    • Updated measurements & graphs
  • bwIDsamp008: Added plate flaw section
  • bwIDmisbg006: Added plate flaw section
  • bwIDsamp008:
    • Corrected overprint selection & image
    • Fixed plate flaw preview label
  • Cleaned up ANNO- & HEAD- code
  • Removed unused ANNO- & HEAD-images
  • Removed unused stamp images
  • Removed temporary images
  • Added measurement data for perfs & paper size. measurements.html
  • Fixed a bug with the download page
  • Added a history section to the notes.html file

3.10. 2022-11-30

  • Many burelage tables are now fully automated based on DATA tables. Woot!
    • Sheet image with 150 & 168 overlapped showing types with sheets in color
    • Sheet image with 150 & 168 overlapped showing blocks of 4 with all four burelage types
    • Sheet image with 150 & 168 overlapped showing horizontal strips of 4 with all four burelage types
    • Sheet image with 150 & 168 overlapped showing vertical strips of 4 with all four burelage types
    • Table showing information for blocks of 4 with all four burelage types
    • Table showing information for horizontal strips of 4 with all four burelage types
    • Table showing information for vertical strips of 4 with all four burelage types
  • Added examples showing how to find sheet location given a block of stamps with known burelage types
  • Added side illuminated, rough perforation pic to notes.org
  • Added full sized image links to perforation examples in notes.org

3.11. 2022-11-29

3.12. 2022-11-26

  • Some clean-up – album vs stock book
  • Added section about rough perforations in notes.org

3.13. 2022-11-24

  • Renamed sheet001 and sheet002 to sheetw001 and sheetu001
  • Renamed bwIDqbw* to bwIDqbu*

3.14. 2022-11-23

  • Added: bwIDpf006, bwIDpf007, bwIDpf008, bwIDsgl070, bwIDsgl071, bwIDsgl072, bwIDsgl073, bwIDsgl074, bwIDsgl075, bwIDsgl076, bwIDsgl076, bwIDsgl077, bwIDruf004, bwIDshade013, bwIDshade014, bwIDshade015
  • Started work on Burelage code.

3.15. 2022-11-21

  • Several updates to the FAQ
  • New macro for color measurement results – allows me to have all the data in the org file, and only present the bits we want in HTML.
  • New measurements for color that include "Jue" – "Hue" mapped to [-180, 180]. The "J" is pronounced like "H" – in an honor of the way it sounds in some Spanish words. With this change, I think I have a good way to document shades.
  • Fixed virtalbum.org – it was sorting incorrectly.

3.16. 2022-11-20

  • Answered several new questions in the FAQ.
  • Cleaned up and published the unlinkedImages.rb script.
  • Added code to separate the "main collection" from "stock".
  • The script rearrangeAlbum.rb now can move pages/rows/columns to the left.
  • Move to 1250 px wide layout.
  • Using a macro for catalog perforations now – more accurate and less typing.
  • Added downloads page.

3.17. 2022-11-17

  • Tons of code changes in the background
  • Added a 168 sheet! Woot!
  • data is now in data.org
  • Raw scan data is included now
  • Added plate flaw lists from FM & HWW.

3.18. 2022-11-16

  • Added: bwIDesy001, bwIDp2s013, bwIDp2s014, bwIDp2s015, bwIDmisfg004
  • Added two pages to start of album.
  • notes.org now uses DB elisp library in album.org.
  • Added "REPUBBLICA" and "Red Cross" sections to notes.

3.19. 2022-11-15

  • Added: bwIDqbn004, bwIDqbn005, bwIDqbn006, bwIDimps002, bwIDimps003, bwIDsamp007, bwIDsamp008, bwIDsamp009, bwIDsamp010, bwIDovrc001, & bwIDdbl006.
  • Moved around samp stamps to make room on page.

3.20. 2022-11-11

  • Added scan of back of bwIDimps001
  • Added scan of back of bwIDpf005
  • Added "paper cropped" scans for sheet001.
  • Added "paper cropped" images for burelage quads from sheet001.
  • sheet001 stamp links now go to "paper cropped" images.
  • Did scans for remainder of collection

3.21. 2022-11-09

  • Changed plate flaw docs to new format
  • Added several new plate flaws to document
  • Added burelage types and identified plate location for bwIDpf005
  • Added plate flaw image for bwIDimps001
  • Decided on naming convention for "design cropped images"
  • Renamed all the "design cropped images"

3.22. 2022-11-08

  • Added bwIDpf005
  • Added bwIDimps001
  • Added two new pages after page 7
  • Moved bwIDruf001, bwIDruf002, bwIDruf003 to page 8

3.23. 2022-11-07

  • Plate flaws
    • Updated position lists for pf0004 pf0005
    • Added pf0009 – need to document rest of these in my collection
  • Scanned and measured (both in cache directory)
    • Imperf at top
    • Block of four with odd plate flaw

3.24. 2022-11-06

3.25. 2022-11-05

  • Initial upload of content
  • Collection Status:
    • I have about 2/3 of my total collection is scanned so far
    • Most of them have perf measurements
    • Lots of plate flaws to document
    • Paper measurements are missing
    • Design measurements are missing
  • DB Status:
    • I have data tables organized for the collection and plate flaws
    • I have much more data entry for plate flaws & stamp data
    • I'm using the DB to generate the index, plate flaws, and sheet images
    • The plan is to use it for individual stamp records eventually