A program to convert a palettized TIFF to an ENVI classification image
Single band TIFF files with a color palette are converted to 3-band True Color RGB when imported into ENVI. This Help Article provides example code for a user-defined function that can import these palettized TIFF images as ENVI classification images.
Currently, ENVI will open a TIFF file with a palette as a true color RGB image. There are cases when the desired image type is a classification image. The file type can be changed in ENVI by editing the header and changing the file type to 'ENVI Classification'. This requires that the user interactively sets the number of classes and assigns the class colors manually. The below code will reclassify the RGB values into unique classes based on the unique RGB triplets in the dataset and output an ENVI classification image with the same visual effect as the palettized TIFF.
------------------------------------------------
; Take the histogram of a 2D palettized color image with a disorderly,
; possibly redundant and possibly partially unused color table, plus
; the R, G and B color intensity vectors of that table and return three
; things:
; - through parameter 'paletteIntsIn' return the True Color values of
; the original color table
; - through parameter 'collapsedPaletteInts' return the True Color values
; of an orderly, non-redundant color table
; - through the function's return value return a 3 x 256 color table
; which can be used as an ENVI classification image "lookup table"
Function collapse_colors, hist, r, g, b, paletteIntsIn, collapsedPaletteInts
; Pack RGB triplets into True Color int values
paletteIntsIn = ishft(long(b), 16) + ishft(long(g), 8) + r
; Get rid of color table values that are not used in 'data'.
irrelevantColors = where(hist eq 0, nIrrelevantColors)
if nIrrelevantColors gt 0 then paletteIntsIn[irrelevantColors] = 0
; Remap the colors in use into the beginning indexes of a new
; palette. Sorting is required to get rid of redundant color indexes.
SortedPal = sort(paletteIntsIn)
collapsedPaletteInts = paletteIntsIn[sortedPal[uniq(paletteIntsIn[sortedPal])]]
nClasses = n_elements(collapsedPaletteInts)
; Unpack the True Color values into RGB triplet values
enviLookup = bytarr(3, nClasses)
enviLookup[0,*] = collapsedPaletteInts and '0000FF'x
enviLookup[1,*] = ishft(collapsedPaletteInts, -8) and '0000FF'x
enviLookup[2,*] = ishft(collapsedPaletteInts, -16)
return, enviLookup
end
;++++++++++++++++++++++++++++++++++++++++++++++++
; Convert a 2D palettized color classification image stored as
; TIFF to ENVI classification image format
pro palettized_tiff_to_envi_class
compile_opt idl2
file = envi_pickfile(filter='*.tif;*.tiff', $
title='Select RGB TIFF to convert to ENVI classification')
if (file eq '') then return
envi_open_file, file, /no_realize, r_fid=fid
; Get important header info
envi_file_query, fid, ns=ns, nl=nl, dims=dims
inherit = envi_set_inheritance(fid, dims, /spatial)
;prompt for output file name
base = widget_auto_base(title='Select output Classification file name')
wo = widget_outf(base, uvalue='outf', /auto)
result = auto_wid_mng(base)
if (result.accept eq 0) then return
fname=result.outf
; Tile for the benefit of very large TIFF's. Tiles will be full width of the image ("ns")
; by the max number of scanlines ("nLinesPerTile") that can be stored in 4 MB
; or less
maxTileSize = 2L ^ 22 ; 4 Mb
nLinesPerTile = maxTileSize / ns
nTiles = ceil(float(nl) / nLinesPerTile)
envi_report_init, ['Input File: ' + file_basename(file)], BASE=wStatus, $
TITLE='Making histogram of ' + strtrim(ns * nl / (2L ^ 20), 2) + ' Mb Image'
envi_report_inc, wStatus, nTiles
hist = lonarr(256) ; initialize
currentLine = 0
for tileIndex = 0, nTiles-1 do begin
nextLine = (currentLine + nLinesPerTile) < nl
currentTile = read_tiff(file, sub_rect=[0, currentLine, ns, (nextLine-currentLine)-1])
hist += histogram(currentTile)
currentTile = 0
currentLine = nextLine
envi_report_stat, wStatus, tileIndex+1, nTiles
endfor
envi_report_init, BASE=wStatus, /FINISH
; Do one small read to get color table info
void = read_tiff(file, rIn, gIn, bIn, sub_rect=[0,0,1,1])
; Collapse the disorderly, possibly partially unused and possibly
; redundant color table into a minimum-sized orderly, non-redundant
; color table
enviLookup = collapse_colors(hist, rIn, gIn, bIn, paletteIntsIn, $
collapsedPaletteInts)
nClasses = n_elements(collapsedPaletteInts)
; Read in TIFF data (in horizontal tiles, if necessary), remap the
; data to the new color table indexes that are used by 'enviLookup'
; and output to a flat binary ENVI '.img' file
openw, unit, fname, /get_lun
envi_report_init, ['Input File: ' + file_basename(file), $
'Output file: ' + fname], BASE=wStatus, $
TITLE='Converting ' + strtrim(ns * nl, 2) + ' Pixels'
envi_report_inc, wStatus, nTiles
currentLine = 0
for tileIndex = 0, nTiles-1 do begin
nextLine = (currentLine + nLinesPerTile) < nl
currentTile = read_tiff(file, sub_rect=[0, currentLine, ns, nextLine-currentLine])
h = histogram(currentTile, REVERSE_INDICES=r)
; Remap the current color indexes into their newly-mapped values in
; the 'enviLookup' output color table
; Don't handle first original color index unless it is ***not*** black.
; This routine forces the first color table index to be black!!!
startingIndex = paletteIntsIn[0] eq 0 ? 1L : 0L
for i = startingIndex, nClasses-1 do begin
currentClassSubscripts = $
where(paletteIntsIn eq collapsedPaletteInts[i], nColorIndexes)
for j = 0, nColorIndexes-1 do begin
c = currentClassSubscripts[j] ; Get the color index to match
if r[c] ne r[c+1] then currentTile[r[r[c]:r[c+1]-1]] = i
endfor
endfor
writeu, unit, currentTile
r = 0 ; Free memory
currentTile = 0
currentLine = nextLine
envi_report_stat, wStatus, tileIndex+1, nTiles
endfor
free_lun, unit
envi_report_init, BASE=wStatus, /FINISH
; Set up the class information for the header
classes = 'Class '+ strtrim(bindgen(nClasses-1)+1, 2)
class_names = ['Unclassified', classes]
; Generate the ENVI header with the new information
file_type = envi_file_type('ENVI Classification')
envi_setup_head, fname=fname, ns=ns, nl=nl, nb=1, interleave=0, $
data_type=1, file_type=file_type, class_names=class_names, $
lookup=ENVIlookup, num_classes=nClasses, inherit=inherit, /write, /open
envi_file_mng, id=fid, /remove
END
Review on 12/31/2013 MM