Reading and displaying satellite images (bil files)

ESRI bil file description can be seen on the following website: ESRI BIL file description
Bil format consists of two files: header (hdr) and binary files (bil). The file format is not unified. Sometimes parameter names and values are separated with tab or space characters. Sometimes the parameter names are various, so a complete bil header parser is not a simple task. In this example demonstrates one simple case only.
The header contains georeferencing data: ulxmap, ulymap (starting coordinates), xdim, ydim (grid size), number of rows, number of columns, number of bands, number of bits (colour depth), byteorder (Intel or Motorola type), bil type information (ESRI or ENVI). Knowing the header parameters, the binary reading can be implemented. Since bil files can contain more than 3 bands, one band can be displayed as a greyscale picture, or a RGB picture from any three bands.

Parsing ESRI bil header


        // reading bil header parameters only, and fill in properties
        public void readBilHeader(string FileName)
        {
            _fileName = FileName;
            string fName = Path.ChangeExtension(FileName, ".hdr");
            //string fName =System.IO.Path.GetDirectoryName(FileName)+ "\\" + System.IO.Path.GetFileNameWithoutExtension(FileName) + ".hdr";

            using (FileStream fs = new FileStream(fName, FileMode.Open, FileAccess.Read))
            {
                StreamReader sr = new StreamReader(fs);
                string line = "";

                while (!sr.EndOfStream) //reading header file (FileName) line by line, and getting parameters by getBilParameters
                {
                    line = sr.ReadLine();
                    if (line != "")
                    {
                        getBilParameters(line);
                    }
                }
            }
        }
		
		//get bil parameters from the header file line by line
        private void getBilParameters(string param)
        {
            string p = param.ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim();

            if (p.Contains("arc/info"))
            {
                _biltype = "Arc/Info";
                _source = param;
            }

            string[] l = p.Split(' '); //p is two dimensional string array. First component is the name :l[0], and the second one is the value: l[1]

            if (l[0] == "byteorder") { _byteorder = l[1]; }
            if (l[0] == "nbits") { _nbits = Convert.ToInt16(l[1]); }//string converted to integer
            if (l[0] == "xdim") { _xdim = Convert.ToDouble(l[1].Replace(".", ",")); } //string converted to double. Replace is needed to convert English number format to Hungarian
            if (l[0] == "ydim") { _ydim = Convert.ToDouble(l[1].Replace(".", ",")); }
            if (l[0] == "ncols") { _ncols = Convert.ToInt16(l[1]); }
            if (l[0] == "nrows") { _nrows = Convert.ToInt16(l[1]); }
            if (l[0] == "nbands") { _nbands = Convert.ToInt16(l[1]); }
            if (l[0] == "ulxmap") { _ulxmap = Convert.ToDouble(l[1].Replace(".", ",")); }
            if (l[0] == "ulymap") { _ulymap = Convert.ToDouble(l[1].Replace(".", ",")); }
        }

Reading bil binary content


        public byte[] getOneBandBytes(int whichBand)
        {
            byte[] byteOut=new byte[_ncols * _nrows];


            using (FileStream fs = new FileStream(_fileName, FileMode.Open, FileAccess.Read))
            {
                BinaryReader br = new BinaryReader(fs);

                int ind = 0;
                int startPos=0;

                for (int i = 0; i < _nrows; i++)
                {
                    startPos = (_nbands * i + whichBand) * _ncols; //  _nbands * _ncols * i  + whichBand * _ncols
                    fs.Position=startPos;
                    for (int j = 0; j < _ncols; j++)
                    {
                        byteOut[ind] = br.ReadByte();
                        ind++;
                    }
                }
               
            }
          return byteOut;
        }


        //this routine converts values of one band, which stored in a byte array (ByIn). It comes from getOneBandBytes (whichBand) routine.
        public Bitmap OneBandtoBitmap(byte[] byIn)
        {
         
            Bitmap bmp = new Bitmap(_ncols, _nrows, PixelFormat.Format24bppRgb);

            //getting one band values from the byteIn and convert it to rgb grayscale
                int res = (_ncols) % 4;
                /* the byte lenght of every rows has to be divided by 4 without remainder. 
                If not, rows have to be completed with zeros until the byte length becomes zero remainder */
                int stride = (_ncols + res) * 3;
                byte[] byOut = new byte[stride * _nrows];
                int ind = 0;

                for (int i = 0; i < _nrows; i++)
                {
                    ind += res;

                    for (int j = 0; j < _ncols; j++)
                    {
                        byte b = byIn[i * _ncols+j];
                        for (int k = 0; k < 3; k++)
                        {
                            byOut[ind] = b;
                            ind++;
                        }
                    }
                }
                imageByteArray imba = new imageByteArray();
                bmp = imba.ByteArrayToBitmap(byOut, _ncols, _nrows);
                return bmp;
        }