Experiments in Leica M8 and M9 Conversions

Discussion in 'Image Processing' started by Brian, Mar 28, 2015.

  1. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    I have been experimenting with using color contrast filters with my M8 and writing my own conversion software to work with the channels based on their altered spectral response. With a Yellow Y48 filter, the channels are mostly balanced. With an O56 filter, the Blue channel is at about 1/4th the sensitivity of Green. My code starts with an uncompressed DNG from Arvid's M8RAW2DNG, but I can adapt to to work with the M9. Files must be uncompressed.

    What the image looks like in color,

    16920711011_15e15bf299_b. L1015755s by fiftyonepointsix, on Flickr

    16930964225_5bc046f518_b. M1015769_S by fiftyonepointsix, on Flickr

    For this conversion, the lens has an Orange filter over it, and the post-processor converts it to two colors- then adds the two channels. Blue/Green are "equalized" and used uninterpolated for 3 of the 4 pixels in the Bayer cells, 4th is interpolated from green. The Red channel is interpolated, two channels added.

    16747498677_4065be5aa3_b. M1015790 by fiftyonepointsix, on Flickr
     
    • Like Like x 1
  2. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    I have a new routine for interpreting and modifying the Tags in the "Image File Directory". The function looks up the Tag and keeps it unchanged, changes as necessary, or eliminates it. This converts the color-DNG to a Monochrome-Linear DNG. "Long story Short", I can apply the same process to the M9 uncompressed files.

    16955243312_7ef793ae9e_b. M1015792 by fiftyonepointsix, on Flickr

    Another two-channel conversion.
     
    • Like Like x 1
  3. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    I modified the algorithm that equalizes the blue and green channel. The spectral response of the two have about the same shape, but Blue is less sensitive. It's not linear, and the prior algorithm worked on the average values of the two channels before merging them. This algorithm computes the histogram for Blue and Green, then equalizes the Blue values to the corresponding Green value of it's histogram. I thought of this last night, in a dream. So I have color/monochrome conversion dreams.

    SUBROUTINE MONOMOSAIC3( IMAGE, RED, GREEN, BLUE,
    1 MONOCHROME, COLUMNS, ROWS, FILE)
    IMPLICIT NONE
    CHARACTER* 12 FILE
    C DEMOSAIC ROUTINE OPTIMIZED FOR MONOCHROME CONVERSION.
    INTEGER* 4 COLUMNS, ROWS
    INTEGER* 2 IMAGE( COLUMNS, ROWS)
    INTEGER* 2 RED( COLUMNS, ROWS)
    INTEGER* 2 GREEN( COLUMNS, ROWS)
    INTEGER* 2 BLUE( COLUMNS, ROWS)
    INTEGER* 2 MONOCHROME( COLUMNS, ROWS)
    INTEGER* 4 COLUMN, ROW
    C+++ BEGIN DOS I/O FUNCTION CODES.
    C FILE I/O PARAMETERS AND CODES.
    INTEGER* 2 CRTNRM, CRTRD, CRTHDN, CRTSYS
    INTEGER* 2 OPNRD, OPNWRT, OPNRDW
    INTEGER* 2 FRMBGN, FRMCRN, FRMEND
    INTEGER* 2 BADFIL, NORMAL
    PARAMETER ( CRTNRM= 0, CRTRD= 1, CRTHDN= 2, CRTSYS= 3)
    PARAMETER ( OPNRD= 0, OPNWRT= 1, OPNRDW= 2)
    PARAMETER ( FRMBGN= 0, FRMCRN= 1, FRMEND= 2)
    PARAMETER ( BADFIL= 0, NORMAL= -1)
    C--- END DOS I/O FUNCTION CODES.
    INTEGER* 4 MAXPIXEL
    PARAMETER ( MAXPIXEL= Z'3FFF')
    C HISTOGRAM.
    INTEGER* 4 BLUEHIST( 0: MAXPIXEL), GREENHIST( 0: MAXPIXEL)
    C INTEGRATE THE CURVE.
    REAL* 8 BLUETOTAL( 0: MAXPIXEL), GREENTOTAL( 0: MAXPIXEL)
    C MAP BLUE TOTALS TO GREEN TOTALS, SHOULD EQUALIZE THE HISTOGRAM.
    REAL* 8 GAINCURVE( 0: MAXPIXEL)
    INTEGER* 2 BYTES, HANDLE, STATUS, LENGTH
    REAL* 8 SUMGREEN, SUMBLUE, AVERAGE_GREEN, AVERAGE_BLUE, GAIN
    INTEGER* 4 PIXELS, MINBLUE, MINGREEN, MAXBLUE, MAXGREEN
    INTEGER* 4 I, J, K, L
    INTEGER* 4 BIGVALUE
    INTEGER* 2 VALUE( 2)
    C EXTERNALS.
    REAL* 8 DFLOAT
    EQUIVALENCE ( BIGVALUE, VALUE)
    WRITE( *, *) ' COLUMNS= ', COLUMNS, ', ROWS= ', ROWS
    C EQUALIZE BLUE AND GREEN CHANNELS.
    SUMBLUE= 0.0D0
    SUMGREEN= 0.0D0
    MINBLUE= 32768
    MINGREEN= 32768
    MAXGREEN= 0
    MAXBLUE= 0
    PIXELS= COLUMNS* ROWS
    DO 1 I= 0, MAXPIXEL
    BLUEHIST( I)= 0
    GREENHIST( I)= 0
    BLUETOTAL( I)= 0.0D0
    GREENTOTAL( I)= 0.0D0
    GAINCURVE( I)= 0.0D0
    1 CONTINUE
    DO 2 J= 2, ROWS, 2
    DO 2 I= 2, COLUMNS, 2
    BLUEHIST( IMAGE( I, J))= BLUEHIST( IMAGE( I, J))+ 1
    GREENHIST( IMAGE( I- 1, J))= GREENHIST( IMAGE( I- 1, J))+ 1
    GREENHIST( IMAGE( I, J- 1))= GREENHIST( IMAGE( I, J- 1))+ 1
    IF( IMAGE( I, J) .LT. MINBLUE) MINBLUE= IMAGE( I, J)
    IF( IMAGE( I- 1, J) .LT. MINGREEN) MINGREEN= IMAGE( I- 1, J)
    IF( IMAGE( I, J- 1) .LT. MINGREEN) MINGREEN= IMAGE( I, J- 1)
    IF( IMAGE( I, J) .GT. MAXBLUE) MAXBLUE= IMAGE( I, J)
    IF( IMAGE( I- 1, J) .GT. MAXGREEN) MAXGREEN= IMAGE( I- 1, J)
    IF( IMAGE( I, J- 1) .GT. MAXGREEN) MAXGREEN= IMAGE( I, J- 1)
    2 CONTINUE
    BLUETOTAL( 0)= DFLOAT( BLUEHIST( 0))
    GREENTOTAL( 0)= DFLOAT( GREENHIST( 0))
    DO 3 I= 1, MAXPIXEL
    BLUETOTAL( I)= BLUETOTAL( I- 1)+ DFLOAT( BLUEHIST( I))
    GREENTOTAL( I)= GREENTOTAL( I- 1)+ DFLOAT( GREENHIST( I))
    3 CONTINUE
    DO 4 I= 1, MAXPIXEL
    BLUETOTAL( I)= BLUETOTAL( I)/ DFLOAT( COLUMNS* ROWS/ 4)
    GREENTOTAL( I)= GREENTOTAL( I)/ DFLOAT( COLUMNS* ROWS/ 2)
    4 CONTINUE
    DO 6 J= 1, MAXPIXEL
    C FOR EVERY BLUE INTEGRATED TOTAL, FIND THE GREEN VALUE THAT EXCEEDS IT.
    IF( BLUEHIST( J) .GT. 0) THEN
    I= J
    5 I= I+ 1
    IF( I .LT. MAXPIXEL .AND.
    1 BLUETOTAL( J) .GT. GREENTOTAL( I)) GO TO 5
    IF( J .GT. MINBLUE) THEN
    GAINCURVE( J)= DFLOAT( I- MINGREEN)/ DFLOAT( J- MINBLUE)
    END IF
    END IF
    6 CONTINUE
    IF( MAXBLUE .GT. MINBLUE) THEN
    GAIN= DFLOAT( MAXGREEN- MINGREEN)/
    1 DFLOAT( MAXBLUE- MINBLUE)
    ELSE
    GAIN= 1.0D0
    END IF
    WRITE( *, *) ' BLUE= ', MAXBLUE, MINBLUE
    WRITE( *, *) ' GREEN= ', MAXGREEN, MINGREEN
    WRITE( *, *) ' GAIN= ', GAIN
    CALL QINKEY( BYTES, HANDLE)
    DO 10 J= 2, ROWS, 2
    DO 10 I= 2, COLUMNS, 2
    IMAGE( I, J)= MINGREEN+
    1 INT( DFLOAT( IMAGE( I, J)- MINBLUE)* GAINCURVE( IMAGE( I, J)))
    IF( I .EQ. J) THEN
    WRITE( *, *) 'IMAGE( ', I, ',', J, ')= ', IMAGE( I, J)
    END IF
    IF( IMAGE( I, J) .GT. Z'4000') THEN
    IMAGE( I, J)= Z'4000'
    END IF
    10 CONTINUE
    DO 15 J= 1, ROWS- 1, 2
    DO 15 I= 1, COLUMNS- 1, 2
    C AVERAGE CELLS GOING ACROSS THE ROWS.
    C ODD NUMBERED ROWS ARE RGRGRGRGRGRGRG
    C EVEN NUMBERED ROWS ARE GBGBGBGBGBGBGB
    C RED PIXELS START THE COLUMN, AND ARE THE FIRST ROW IN EACH 2x2 GRID
    C INTERPOLATE ALL OF THE MISSING VALUES FOR EACH IMAGE PLANE.
    RED( I, J)= IMAGE( I, J)
    RED( I+ 1, J)= ( IMAGE( I, J)+ IMAGE( I+ 2, J))/2
    GREEN( I+ 1, J)= IMAGE( I+ 1, J)
    BIGVALUE= IMAGE( I- 1, J)+ IMAGE( I+ 1, J)
    BIGVALUE= ( BIGVALUE+ IMAGE( I, J- 1)+ IMAGE( I, J+ 1))/ 4
    GREEN( I, J)= VALUE( 1)
    C USE THE BLUE PIXEL AS A GREEN PIXEL SINCE WE EQUALIZED IT.
    GREEN( I+ 1, J+ 1)= IMAGE( I+ 1, J+ 1)
    GREEN( I, J+ 1)= IMAGE( I, J+ 1)
    15 CONTINUE
    C WE ARE DONE WITH GREEN. NOW WE NEED TO FILL IN RED.
    DO 20 J= 1, ROWS- 1, 2
    DO 20 I= 1, COLUMNS- 1, 2
    C COMPUTE RED VALUE THAT IS UNDER GREEN PIXELS IN GBGBGB ROWS.
    RED( I, J+ 1)= ( IMAGE( I, J)+ IMAGE( I, J+ 2))/ 2
    C AND UNDER BLUE CELLS.
    RED( I+ 1, J+ 1)= ( RED( I+ 1, J)+ RED( I+ 1, J+ 2))/2
    20 CONTINUE
    C THE VALUES ARE 14-BITS PER PLANE. WE WANT TO ADD THEM, THEN SCALE TO
    C 16-BITS.
    DO 25 J= 1, ROWS
    DO 25 I= 1, COLUMNS
    BIGVALUE= RED( I, J)
    BIGVALUE= BIGVALUE+ GREEN( I, J)
    MONOCHROME( I, J)= VALUE( 1)
    IMAGE( I, J)= MONOCHROME( I, J)
    25 CONTINUE
    RETURN
    END​

    16770781820_9e8a43ec2a_b. M1015792 by fiftyonepointsix, on Flickr
     
    • Like Like x 1
  4. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    This is the "Image File Directory" parser that turns the color-DNG file into a Monochrome-Linear file.

    BLOCK DATA DNGINIT
    C+++ BEGIN / CMDNGTAGS/
    INTEGER* 4 DNGTAGS
    PARAMETER ( DNGTAGS= 49)
    INTEGER* 2 IFD0KEEP, TAGCODES
    CHARACTER* 64 TAGNAMES
    COMMON/ CMDNGTAGS/ TAGNAMES( DNGTAGS),
    1 IFD0KEEP( DNGTAGS), TAGCODES( DNGTAGS)
    C--- END / CMDNGTAGS
    DATA IFD0KEEP/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1,
    1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
    1 1/
    DATA TAGCODES/
    1 Z'00FE', Z'0100', Z'0101', Z'0102', Z'0103',
    1 Z'0106', Z'010F', Z'0110', Z'0111', Z'0112',
    1 Z'0115', Z'0116', Z'0117', Z'011A', Z'011B',
    1 Z'011C', Z'0128', Z'0131', Z'013B', Z'828D',
    1 Z'828E', Z'8298', Z'8769', Z'882B', Z'9003',
    1 Z'920E', Z'920F', Z'9210', Z'9216', Z'C612',
    1 Z'C614', Z'C619', Z'C61A', Z'C61D', Z'C61F',
    1 Z'C620', Z'C621', Z'C622', Z'C623', Z'C624',
    1 Z'C628', Z'C62B', Z'C62C', Z'C62D', Z'C62F',
    1 Z'C632', Z'C635', Z'C65A', Z'C65B'/
    DATA TAGNAMES/
    1 'GENERAL INDICATOR', 'NUMBER COLUMNS', 'NUMBER OF ROWS',
    1 'BITS PER SAMPLE', 'COMPRESSION',
    1 'PHOTOMETRIC INTERPRETATION (MOSAIC/LINEAR)', 'MAKE', 'MODEL',
    1 'STRIPOFFSET (START OF IMAGE DATA)',
    1 'ORIENTATION ( 01= H, 02= V)', 'SAMPLES PER PIXEL',
    1 'ROWS PER STRIP', 'STRIP BYTE COUNT', 'X RESOLUTION',
    1 'Y RESOLUTION', 'PLANAR CONFIGURATION', 'RESOLUTION UNIT',
    1 'SOFTWARE, NAME AND VERSION', 'ARTIST',
    1 'CFAREPEATPATTERN', 'CFAPATTERN', 'COPYRIGHT',
    1 'POINTER TO EXIF IFD', 'SELF TIME MODE',
    1 'DATE_TIME_ORIG', 'X FOCAL PLANE RESOLUTION',
    1 'Y FOCAL PLANE RESOLUTION', 'FOCAL PLANE RESOLUTION UNIT',
    1 'TIFF STANDARD', 'DNG VERSION', 'UNIQUE CAMERA MODEL',
    1 'BLACKLEVELREPEAT', 'BLACK LEVEL', 'WHITE LEVEL',
    1 'DEFAULT CROP ORIGIN', 'DEFAULT CROP SIZE',
    1 'COLORMATRIX 1', 'COLORMATRIX 2', 'CAMERACALIBRATION',
    1 'CAMERACALIBRATION 2', 'AS SHOT NEUTRAL',
    1 'BASELINENOISE', 'BASELINESHARPNESS', 'BAYER GREEN SPLIT',
    1 'CAMERA SERIAL NUMBER', 'ANTIALIASSTRENGTH',
    1 'MAKERNOTESAFETY', 'CALIBRATION ILLUMINANT1',
    1 'CALIBRATION ILLUMINANT2'/
    END

    INTEGER* 4 FUNCTION IFDCONVERT( IFDTABLE, IFDTABLE4, IFDENTRIES)
    IMPLICIT NONE
    INTEGER* 2 IFDTABLE( 6, IFDENTRIES)
    INTEGER* 4 IFDTABLE4( 3, IFDENTRIES)
    C+++ BEGIN / CMDNGTAGS/
    INTEGER* 4 DNGTAGS
    PARAMETER ( DNGTAGS= 49)
    INTEGER* 2 IFD0KEEP, TAGCODES
    CHARACTER* 64 TAGNAMES
    COMMON/ CMDNGTAGS/ TAGNAMES( DNGTAGS),
    1 IFD0KEEP( DNGTAGS), TAGCODES( DNGTAGS)
    C--- END / CMDNGTAGS
    INTEGER* 4 OLDTAGS, NEWTAGS, TAGINDEX, TAGLIST, TAGSEARCH, INDEX
    LOGICAL* 4 FOUND
    C SEARCH THROUGH ALL OF THE TAGS PASSED IN.
    DO 5 OLDTAGS= 1, IFDENTRIES
    C IDENTIFY THE TAG FROM THE KNOWN LIST.
    TAGSEARCH= 0
    10 CONTINUE
    TAGSEARCH= TAGSEARCH+ 1
    FOUND= TAGCODES( TAGSEARCH) .EQ. IFDTABLE( 1, OLDTAGS)
    IF( ( .NOT. FOUND) .AND. TAGSEARCH .LT. DNGTAGS) GO TO 10
    IF( FOUND .AND. IFD0KEEP( TAGSEARCH) .NE. 0) THEN
    C KEEP THE TAG.
    WRITE( *, *) ' KEEPING TAG ', OLDTAGS
    NEWTAGS= NEWTAGS+ 1
    IF( TAGCODES( TAGSEARCH) .EQ. Z'0106') THEN
    C 6 0106: PHOTOMETRIC INTERP: 8023 FOR COLOR; 884C FOR LINEAR
    IFDTABLE( 5, OLDTAGS)= Z'884C'
    C 17 0128: RESOLUTION UNIT. 02. THE UNIT OF RESOLUTION FOR X AND FOR Y.
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'0128') THEN
    IFDTABLE( 5, OLDTAGS)= 1
    C 28 9210: FOCAL PLANE RESOLUTION UNIT, 0002. (LOC 0156 SET TO 1?)
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'9210') THEN
    IFDTABLE( 5, OLDTAGS)= 1
    C 30 C612: DNG VERSION: 4 BYTES 01 04 00 00 (CHANGE LOC 16F TO 01)
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'C612') THEN
    IFDTABLE( 5, OLDTAGS)= Z'0101'
    C 33 C61A: BLACK LEVEL: 1 WORD, 005C
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'C61A') THEN
    C IFDTABLE( 5, OLDTAGS)= 1
    C 34 C61D: WHITE LEVEL: 1 WORD, 3FFF
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'C61D') THEN
    IFDTABLE( 5, OLDTAGS)= Z'7FFF'
    END IF
    IFDTABLE( 1, NEWTAGS)= IFDTABLE( 1, OLDTAGS)
    IFDTABLE( 2, NEWTAGS)= IFDTABLE( 2, OLDTAGS)
    IFDTABLE( 3, NEWTAGS)= IFDTABLE( 3, OLDTAGS)
    IFDTABLE( 4, NEWTAGS)= IFDTABLE( 4, OLDTAGS)
    IFDTABLE( 5, NEWTAGS)= IFDTABLE( 5, OLDTAGS)
    IFDTABLE( 6, NEWTAGS)= IFDTABLE( 6, OLDTAGS)
    ELSE
    WRITE( *, *) ' ELIMINATE TAG ', OLDTAGS
    END IF
    5 CONTINUE
    DO 20 INDEX= NEWTAGS+ 1, IFDENTRIES
    WRITE( *, *) ' ZERO TAG ', INDEX
    IFDTABLE( 1, INDEX)= 0
    IFDTABLE( 2, INDEX)= 0
    IFDTABLE( 3, INDEX)= 0
    IFDTABLE( 4, INDEX)= 0
    IFDTABLE( 5, INDEX)= 0
    IFDTABLE( 6, INDEX)= 0
    20 CONTINUE
    IFDCONVERT= NEWTAGS
    RETURN
    END

    The Mosaic algorithm shown here is best for the O56, the Yellow Y48 filter- uses a routine that does the demosaic on the three channels as they are more balanced. The O56 is where things got into the two-channel possibility. I will look at a Yellow-3 Y48 and an R60.

    But that's enough code written for one day. Takes less time than you m ight think.​
     
    Last edited: Mar 28, 2015
    • Like Like x 2
  5. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
  6. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    This is with a Red filter over the 1934 5cm F2 Sonnar.

    16786578269_edc0467687_b. M1015819 by fiftyonepointsix, on Flickr

    Full size Jpegs loaded. The color DNG's are batch converted to Monochrome DNG's using the custom raw processor. For the RED filter, the Blue channel is first equalized to green, then they are multiplied *3 and added to Red. The Blue and Green channels are both sensitive in the red region.

    16350527724_217af2a6e7_b. M1015820 by fiftyonepointsix, on Flickr

    16946939866_fec59b22e3_b. M1015827 by fiftyonepointsix, on Flickr

    16786689339_dffd43d030_b. M1015835 by fiftyonepointsix, on Flickr

    This is what the original looks like from the camera,

    16977120422_9fe93132f5_b. L1015835 by fiftyonepointsix, on Flickr
     
    Last edited: Mar 30, 2015
  7. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    The R60 filter basically makes all of the pixels "Monochrome", just the response of the Blue and Green channel are lower than red. This is where having 14-bit pixels comes in, Blue and Green have been boosted to be equalized with Red. I'm still making two channels, then adding them. But the channels are close. What I do not see in the 100% crops: the jagged edges seen when using a simulated Red filter with Lightroom. The use of the optical Red filter trades-off boosting the response versus interpolating channels. Green is boosted by multiplying by 3, this was a rough estimate looking at the data sheet for spectral response. "Quick and Dirty", might optimize it later. But... I want to try something different... False color, might give us something like Infrared Ektachrome...

    C THE VALUES ARE 14-BITS PER PLANE. WE WANT TO ADD THEM, THEN SCALE TO
    C 16-BITS.
    DO 25 J= 1, ROWS
    DO 25 I= 1, COLUMNS
    BIGVALUE= RED( I, J)
    BIGVALUE= BIGVALUE+ GREEN( I, J)* 3
    IF( BIGVALUE .GT. Z'BFFF') BIGVALUE= Z'BFFF'
    IMAGE( I, J)= VALUE( 1)
    25 CONTINUE
    RETURN
    END
     
    Last edited: Mar 29, 2015
  8. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    Chased down an interpolation error that caused an "solarization" problem, and have a new section to balance the blue/green combined channel with Red.

    16375283413_533bfd8ceb_b. M1015846 by fiftyonepointsix, on Flickr

    This should work with the O56 and R60 filters.

    16809175939_44d7525a23_b. M1015861 by fiftyonepointsix, on Flickr

    Full-res Jpegs uploaded. Check the sharpness of the first image, and the intensity range of the second.
     
  9. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    These images are taken with a Red "R60" filter with a cutoff of 600nm.

    The Blue and Green channel are picking up a good bit of Infrared, but it is about 1/10th as strong as the Red channel.

    This is what the image looks like out of camera, RED dominates the image.

    17002960452_0a7e4958a9_b. L1015853 by fiftyonepointsix, on Flickr

    This is what I am doing by boosting the Blue and Green channels to the intensity level of the Red channel.

    17003490831_02a70444d5_b. C1015853 by fiftyonepointsix, on Flickr

    All of these are handheld, 4x filter factor for the RED filter.

    This is the image converted directly to Monochrome, Linear-DNG.

    16818189979_3239f2bfdf_b. M1015853 by fiftyonepointsix, on Flickr

    This should also work with the Visible-Blocking filter, the R72.
     
  10. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    I will be cleaning up comments in software and posting the key sections, some have expressed an interest in writing their own software.

    As I told a neighbor when she asked what I was doing, "With so many people owning digital cameras, you'd think more people would be writing their own software"... I blame Adobe, their spec for DNG is typical...

    I found this site very useful:

    http://www.digitalpreservation.gov/formats/content/tiff_tags.shtml

    For the DNG file generated by Arvid's M8RAW2DNG: the file has one "Image File Directory" that can be parsed for all the information. OR- the easy way, the image file is stored at the end of the file. It is 3968 pixels per row, 2646 rows, two bytes per pixel. 7936 bytes per red-green row, same for blue-green rows. 20,998,656 bytes per image. Read in the file and use those last bytes for the data. If you use "M8RAW2DNG -s -b00" that I use, the file offset is '06A6'X.

    The ".RAW" file that comes out of the camera is 3972 pixels per row, 2646 rows. Raw starts at File Offset '06C'x. The extra 4 pixels are for calibration.
     
  11. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    I changed the thread title.

    This is without a filter,

    16832305370_0d9f518569_b. L1015873 by fiftyonepointsix, on Flickr

    This is the in-camera JPEG, with a B&W 090 Red, a 5x filter factor.

    16833617439_553797fe29_b. L1015874 by fiftyonepointsix, on Flickr

    With this filter, Red is Red, Green is Green+Infrared, and Blue is Infrared.

    My Post Processor boosts Blue and Green to the level of RED.

    16832062288_eb273d76bf_b. I1015874_2 by fiftyonepointsix, on Flickr

    This is as close to Infrared Ektachrome as I need.
    28mm Elmarit, F4, shutter speeds 1/250th ~ 1/500th, all at ISO160.
     
  12. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    This is the Fortran subroutine that produces the Infrared Ektachrome file from the M8 with a red filter. This version exchanges Red and Blue channels, so you do not need to rotate the Hue in Photoshop.

    SUBROUTINE COLORMOSAIC( IMAGE, COLUMNS, ROWS)
    IMPLICIT NONE
    C IMAGE GENERATED USING EQUALIZATION.
    C 1) COMPUTE HISTOGRAM FOR ALL THREE CHANNELS.
    C 2) INTEGRATE AREA UNDER HISTOGRAM AND NORMALIZE.
    C 3) EQUALIZE BLUE AND GREEN TO RED.
    C 4) EXCHANGE BLUE AND RED CHANNELS.
    INTEGER* 4 COLUMNS, ROWS
    INTEGER* 2 IMAGE( COLUMNS, ROWS)
    INTEGER* 4 COLUMN, ROW
    C+++ BEGIN DOS I/O FUNCTION CODES.
    INTEGER* 4 MAXPIXEL
    PARAMETER ( MAXPIXEL= Z'3FFF')
    C HISTOGRAM. STRONGEST WILL BE SET THE THE CHANNEL THAT IS THE FIRST TO
    C REACH A THRESHOLD
    REAL* 8 INTEGRATION_CUTOFF
    PARAMETER ( INTEGRATION_CUTOFF= 0.95D0)
    INTEGER* 4 BLUECHANNEL, GREENCHANNEL, REDCHANNEL
    PARAMETER ( BLUECHANNEL= 1, GREENCHANNEL= 2, REDCHANNEL= 3)
    INTEGER* 4 HISTOGRAM( 0: MAXPIXEL, 3)
    INTEGER* 4 BLUEHIST( 0: MAXPIXEL), GREENHIST( 0: MAXPIXEL)
    INTEGER* 4 REDHIST( 0: MAXPIXEL), CUTOFF( 3), STRONGEST
    EQUIVALENCE ( HISTOGRAM( 0, BLUECHANNEL), BLUEHIST)
    EQUIVALENCE ( HISTOGRAM( 0, GREENCHANNEL), GREENHIST)
    EQUIVALENCE ( HISTOGRAM( 0, REDCHANNEL), REDHIST)
    C INTEGRATE THE CURVE.
    REAL* 8 IMAGETOTAL( 0: MAXPIXEL, 3)
    REAL* 8 BLUETOTAL( 0: MAXPIXEL), GREENTOTAL( 0: MAXPIXEL)
    REAL* 8 REDTOTAL( 0: MAXPIXEL)
    EQUIVALENCE( IMAGETOTAL( 0, BLUECHANNEL), BLUETOTAL)
    EQUIVALENCE( IMAGETOTAL( 0, GREENCHANNEL), GREENTOTAL)
    EQUIVALENCE( IMAGETOTAL( 0, REDCHANNEL), REDTOTAL)
    C MAP BLUE TOTALS TO GREEN TOTALS, SHOULD EQUALIZE THE HISTOGRAM.
    REAL* 8 GAINCURVE( 0: MAXPIXEL)
    INTEGER* 2 BYTES, HANDLE, STATUS, LENGTH, EXCHANGE
    REAL* 8 SUMGREEN, SUMBLUE, AVERAGE_GREEN, AVERAGE_BLUE, GAIN
    INTEGER* 4 PIXELS, MINBLUE, MINGREEN, MAXBLUE, MAXGREEN
    INTEGER* 4 MINRED, MAXRED
    INTEGER* 4 I, J, K, L
    C FORTRAN METHOD OF GETTING LOW-ORDER WORD FROM A 32-BIT VALUE.
    INTEGER* 4 BIGVALUE
    INTEGER* 2 VALUE( 2)
    C EXTERNALS.
    INTEGER* 2 MOD
    REAL* 8 DFLOAT, DMIN1
    EQUIVALENCE ( BIGVALUE, VALUE)
    WRITE( *, *) ' COLUMNS= ', COLUMNS, ', ROWS= ', ROWS
    C EQUALIZE BLUE AND GREEN CHANNELS TO RED. CAN CHANGE LATER TO USE
    C THE STONGEST CHANNEL.
    SUMBLUE= 0.0D0
    SUMGREEN= 0.0D0
    MINBLUE= 32768
    MINGREEN= 32768
    MINRED= 32768
    MAXGREEN= 0
    MAXBLUE= 0
    MAXRED= 0
    PIXELS= COLUMNS* ROWS
    C INITIALIZE ARRAYS.
    DO 1 I= 0, MAXPIXEL
    BLUEHIST( I)= 0
    GREENHIST( I)= 0
    REDHIST( I)= 0
    BLUETOTAL( I)= 0.0D0
    GREENTOTAL( I)= 0.0D0
    REDTOTAL( I)= 0.0D0
    GAINCURVE( I)= 0.0D0
    1 CONTINUE
    C COMPUTE HISTOGRAMS AND FIND MAX/MIN OF EACH CHANNEL.
    DO 2 J= 2, ROWS, 2
    DO 2 I= 2, COLUMNS, 2
    BLUEHIST( IMAGE( I, J))= BLUEHIST( IMAGE( I, J))+ 1
    GREENHIST( IMAGE( I- 1, J))= GREENHIST( IMAGE( I- 1, J))+ 1
    GREENHIST( IMAGE( I, J- 1))= GREENHIST( IMAGE( I, J- 1))+ 1
    REDHIST( IMAGE( I- 1, J- 1))= REDHIST( IMAGE( I- 1, J- 1))+ 1
    IF( IMAGE( I, J) .LT. MINBLUE) MINBLUE= IMAGE( I, J)
    IF( IMAGE( I- 1, J- 1) .LT. MINRED) MINRED= IMAGE( I- 1, J- 1)
    IF( IMAGE( I- 1, J) .LT. MINGREEN) MINGREEN= IMAGE( I- 1, J)
    IF( IMAGE( I, J- 1) .LT. MINGREEN) MINGREEN= IMAGE( I, J- 1)
    IF( IMAGE( I, J) .GT. MAXBLUE) MAXBLUE= IMAGE( I, J)
    IF( IMAGE( I- 1, J) .GT. MAXGREEN) MAXGREEN= IMAGE( I- 1, J)
    IF( IMAGE( I, J- 1) .GT. MAXGREEN) MAXGREEN= IMAGE( I, J- 1)
    IF( IMAGE( I- 1, J- 1) .GT. MAXRED) MAXRED= IMAGE( I- 1, J- 1)
    IF( IMAGE( I- 1, J- 1) .LT. MINRED) MINRED= IMAGE( I- 1, J- 1)
    2 CONTINUE
    C NOW INTEGRATE THE AREA UNDER THE HISTOGRAMS FOR EACH COLOR.
    BLUETOTAL( 0)= DFLOAT( BLUEHIST( 0))
    GREENTOTAL( 0)= DFLOAT( GREENHIST( 0))
    REDTOTAL( 0)= DFLOAT( REDHIST( 0))
    CUTOFF( REDCHANNEL)= -1
    CUTOFF( BLUECHANNEL)= -1
    CUTOFF( GREENCHANNEL)= -1
    WRITE( *, *) ' BLUE= ', MAXBLUE, MINBLUE
    WRITE( *, *) ' GREEN= ', MAXGREEN, MINGREEN
    WRITE( *, *) ' RED= ', MAXRED, MINRED
    DO 3 I= 1, MAXPIXEL
    BLUETOTAL( I)= BLUETOTAL( I- 1)+ DFLOAT( BLUEHIST( I))
    GREENTOTAL( I)= GREENTOTAL( I- 1)+ DFLOAT( GREENHIST( I))
    REDTOTAL( I)= REDTOTAL( I- 1)+ DFLOAT( REDHIST( I))
    3 CONTINUE
    C NORMALIZE THE CURVES.
    DO 4 I= 1, MAXPIXEL
    BLUETOTAL( I)= BLUETOTAL( I)/ DFLOAT( COLUMNS* ROWS/ 4)
    REDTOTAL( I)= REDTOTAL( I)/ DFLOAT( COLUMNS* ROWS/ 4)
    GREENTOTAL( I)= GREENTOTAL( I)/ DFLOAT( COLUMNS* ROWS/ 2)
    C LOOK FOR THE 95%.
    IF( BLUETOTAL( I) .GT. INTEGRATION_CUTOFF .AND.
    1 CUTOFF( BLUECHANNEL) .EQ. -1) THEN
    WRITE( *, *) 'BLUETOTAL( ', I, ')= ', BLUETOTAL( I)
    CUTOFF( BLUECHANNEL)= I
    END IF
    IF( GREENTOTAL( I) .GT. INTEGRATION_CUTOFF .AND.
    1 CUTOFF( GREENCHANNEL) .EQ. -1) THEN
    WRITE( *, *) 'GREENTOTAL( ', I, ')= ', GREENTOTAL( I)
    CUTOFF( GREENCHANNEL)= I
    END IF
    IF( REDTOTAL( I) .GT. INTEGRATION_CUTOFF .AND.
    1 CUTOFF( REDCHANNEL) .EQ. -1) THEN
    WRITE( *, *) 'REDTOTAL( ', I, ')= ', REDTOTAL( I)
    CUTOFF( REDCHANNEL)= I
    END IF
    4 CONTINUE
    C ADJUST THE BLUE CURVE TO MATCH RED.
    DO 20 J= 1, MAXPIXEL
    C FOR EVERY BLUE INTEGRATED TOTAL, FIND THE RED VALUE THAT EXCEEDS IT.
    IF( BLUEHIST( J) .GT. 0) THEN
    I= 1
    5 I= I+ 1
    IF( I .LT. MAXPIXEL .AND.
    1 BLUETOTAL( J) .GT. REDTOTAL( I)) GO TO 5
    GAINCURVE( J)= DFLOAT( I)/ DFLOAT( J)
    END IF
    20 CONTINUE
    C SCALE BLUE CHANNEL TO RED RESPONSE CURVE.
    K= MIN0( MINRED, MINBLUE)
    DO 35 J= 2, ROWS, 2
    DO 35 I= 2, COLUMNS, 2
    BIGVALUE= IMAGE( I, J)
    BIGVALUE= INT( DFLOAT( BIGVALUE)* GAINCURVE( BIGVALUE))
    IF( BIGVALUE .GT. Z'03FFF') BIGVALUE= Z'03FFF'
    IMAGE( I, J)= VALUE( 1)
    35 CONTINUE
    C EQUALIZE GREEN TO RED.
    DO 41 J= 1, MAXPIXEL
    C FOR EVERY GREEN INTEGRATED TOTAL, FIND THE RED VALUE THAT EXCEEDS IT.
    I= 1
    42 I= I+ 1
    IF( I .LT. MAXPIXEL .AND.
    1 GREENTOTAL( J) .GT. REDTOTAL( I)) GO TO 42
    GAINCURVE( J)= DFLOAT( I)/ DFLOAT( J)
    C WRITE( *, *) ' GAIN( ', J, ')= ', GAINCURVE( J)
    41 CONTINUE
    K= MIN0( MINRED, MINGREEN)
    DO 40 J= 1, ROWS- 1, 2
    DO 40 I= 1, COLUMNS- 1, 2
    C EXCHANGE BLUE AND RED.
    EXCHANGE= IMAGE( I, J)
    IMAGE( I, J)= IMAGE( I+ 1, J+ 1)
    IMAGE( I+ 1, J+ 1)= EXCHANGE
    C GREEN PIXEL ON RG ROW
    BIGVALUE= IMAGE( I+ 1, J)
    BIGVALUE= INT( DFLOAT( BIGVALUE)* GAINCURVE( BIGVALUE))
    IF( BIGVALUE .GT. Z'03FFF') BIGVALUE= Z'03FFF'
    IMAGE( I+ 1, J)= VALUE( 1)
    C GREEN VALUE OF THE GBGBGBGB ROW.
    BIGVALUE= IMAGE( I, J+ 1)
    BIGVALUE= INT( DFLOAT( BIGVALUE)* GAINCURVE( BIGVALUE))
    IF( BIGVALUE .GT. Z'03FFF') BIGVALUE= Z'03FFF'
    IMAGE( I, J+ 1)= VALUE( 1)
    40 CONTINUE
    WRITE( *, *) ' FINISHED COLORMOSAIC'
    RETURN
    END
     
    Last edited: Apr 3, 2015
  13. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    This function parses the IFD and gets the number of columns and rows, and gets the starting point of the image data.

    INTEGER* 4 FUNCTION IFDVERS( IFDTABLE, IFDTABLE4, IFDENTRIES)
    IMPLICIT NONE
    INTEGER* 2 IFDTABLE( 6, IFDENTRIES)
    INTEGER* 4 IFDTABLE4( 3, IFDENTRIES)
    C+++ BEGIN / CMDNGTAGS/
    INTEGER* 4 DNGTAGS
    PARAMETER ( DNGTAGS= 50)
    INTEGER* 2 IFD0KEEP, TAGCODES
    CHARACTER* 64 TAGNAMES
    INTEGER* 4 TCOLUMNS, TROWS, TBITS, TIMAGESTART, TSUBIFD
    INTEGER* 4 TXRES, TYRES, TCFAREPEAT, TCFAPATTERN
    COMMON/ CMDNGTAGS/ TAGNAMES( DNGTAGS), IFD0KEEP( DNGTAGS),
    1 TAGCODES( DNGTAGS), TCOLUMNS, TROWS, TBITS, TIMAGESTART,
    1 TSUBIFD, TXRES, TYRES, TCFAREPEAT, TCFAPATTERN
    C--- END / CMDNGTAGS
    INTEGER* 4 OLDTAGS, NEWTAGS, TAGINDEX, TAGLIST, TAGSEARCH, INDEX
    INTEGER* 4 TAGLENGTH
    INTEGER* 2 I, J
    LOGICAL* 4 FOUND
    INTEGER* 4 STRLEN
    NEWTAGS= 0
    C SEARCH THROUGH ALL OF THE TAGS PASSED IN.
    DO 5 OLDTAGS= 1, IFDENTRIES
    C IDENTIFY THE TAG FROM THE KNOWN LIST.
    TAGSEARCH= 0
    10 CONTINUE
    TAGSEARCH= TAGSEARCH+ 1
    FOUND= TAGCODES( TAGSEARCH) .EQ. IFDTABLE( 1, OLDTAGS)
    IF( ( .NOT. FOUND) .AND. TAGSEARCH .LT. DNGTAGS) GO TO 10
    IF( FOUND .AND. IFD0KEEP( TAGSEARCH) .NE. 0) THEN
    TAGLENGTH= STRLEN( TAGNAMES( TAGSEARCH))
    C 30 C612: DNG VERSION: 4 BYTES 01 04 00 00 (CHANGE LOC 16F TO 01)
    IF( TAGCODES( TAGSEARCH) .EQ. Z'C612') THEN
    IFDTABLE( 5, OLDTAGS)= Z'0101'
    C 33 C61A: BLACK LEVEL: 1 WORD, 005C
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'C61A') THEN
    IFDTABLE( 5, OLDTAGS)= Z'020'
    C 34 C61D: WHITE LEVEL: 1 WORD, 3FFF
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'C61D') THEN
    CFOR MOSAIC5 IFDTABLE( 5, OLDTAGS)= Z'BFFF'
    IFDTABLE( 5, OLDTAGS)= Z'3FFF'
    C NUMBER OF COLUMNS.
    C Z'0100', Z'0101', Z'0102'
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'0100') THEN
    TCOLUMNS= IFDTABLE( 5, OLDTAGS)
    WRITE( *, 100) OLDTAGS, TAGCODES( TAGSEARCH),
    1 TAGNAMES( TAGSEARCH)( 1: TAGLENGTH), TCOLUMNS
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'0101') THEN
    TROWS= IFDTABLE( 5, OLDTAGS)
    WRITE( *, 100) OLDTAGS, TAGCODES( TAGSEARCH),
    1 TAGNAMES( TAGSEARCH)( 1: TAGLENGTH), TROWS
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'0102') THEN
    TBITS= IFDTABLE( 5, OLDTAGS)
    WRITE( *, 100) OLDTAGS, TAGCODES( TAGSEARCH),
    1 TAGNAMES( TAGSEARCH)( 1: TAGLENGTH), TBITS
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'0111') THEN
    TIMAGESTART= IFDTABLE4( 3, OLDTAGS)
    WRITE( *, 100) OLDTAGS, TAGCODES( TAGSEARCH),
    1 TAGNAMES( TAGSEARCH)( 1: TAGLENGTH), TIMAGESTART
    ELSE IF( TAGCODES( TAGSEARCH) .EQ. Z'014A') THEN
    TSUBIFD= IFDTABLE4( 3, OLDTAGS)
    WRITE( *, 100) OLDTAGS, TAGCODES( TAGSEARCH),
    1 TAGNAMES( TAGSEARCH)( 1: TAGLENGTH), TSUBIFD
    END IF
    100 FORMAT( ' IFD ENTRY ', I3, ': ', Z4.4, ', "', A, '":', Z8)
    END IF
    5 CONTINUE
    C CALL QINKEY( I, J)
    RETURN
    END
     
  14. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    This is the code snippet that uses the routines, the DNG file has been read into byte array "BUFFER", BUFFER( 10) is the location of the IFD in the DNG file from m8raw2dng. Function IFDVERS fills in TIMAGESTART, TCOLUMNS, and TROWS.

    The M9 and M Monochrom store two images per DNG, the first is the thumbnail and second is main image. IFDVERS must be called twice, the second time after the position of the "SUBIFD" is revealed.

    NEWTAGS= IFDVERS( BUFFER( 10), BUFFER( 10), OLDTAGS)
    CALL COLORMOSAIC( BUFFER( TIMAGESTART), TCOLUMNS, TROWS)

    This is the direct output from the new routine that exchanges Red and Blue. No "Photoshop" except to resize.

    17025132765_fa30bd111b_o. I1015874 by fiftyonepointsix, on Flickr
     
    Last edited: Apr 3, 2015
  15. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    I took the M8 out today to Gunston Hall, then to the Playground.

    I think this is the only camera that can be used like this. I have two full-spectrum cameras, one monochrome, the other color.

    The M8 allows Red to be used for Visible-Red, Green to be used for Red+Infrared, and Blue to be used for Infrared only. This is similar to color Infrared film.

    FORTRAN Output Files...

    28mm Elmarit with the B&W 090 filter.

    16826256047_1d2d7e8237_o. I1015897 by fiftyonepointsix, on Flickr


    16847445039_0fd5166a72_o. I1015881 by fiftyonepointsix, on Flickr

    16826256277_5f46aaffd4_o. I1015880 by fiftyonepointsix, on Flickr
     
  16. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
  17. dalethorn

    dalethorn Guest

    This code is strictly for color filters - no other kind of filter? (excuse the dumb question, but that's what it looks like)
     
  18. Brian

    Brian Super Moderator Subscribing Member

    Apr 3, 2013
    These routines are for images taken with color filters over the lens, they manipulate the RGB channels, boosting intensity of channels effected by the color filters. The Spectral response of the RGB channels extend mush farther out than do traditional color separation filters. The monochrome conversion uses reduced interpolation. The Monochrome conversion method probably works best with Y52 and O56 filters. The second set of routines are used for "Infrared Ektachrome" type effects when a Red filter is used, I will also try with the O56. The latter should cut out Blue light resulting in only the Blue channel being mostly composed of Infrared. The routine simply equalizes the histograms of the three channels and exchanges Blur and Red for False color, as IR Ektachrome did.

    The first routines interpolate the image and convert to linear-monochrome. "monomosaic" series of routines. IFDCONVERT convertes the DNG file from mosaic to linear.

    The second set of routines are for the Infrared.

    The two are very related, I need to clean up the latest Monochrome conversion routine and will repost.

    I will also look for a compiler that runs under Win7. Right now: I am using Microway FORTRAN-77, PharLap Extended DOS, and win98 booted into real-mode DOS. I love running DOS programs that take 500MBytes of memory... It's FAST! Lightning Fast! This is why I probably stuck with Film for personal use for all these years. I spent the 1980s writing code for multi-spectral imagers. My DOS computer is much faster than the Floating Point Systems FPS-120b array processor. Which caught on fire running one of my codes. Overloaded the power supplies. My Boss understood, and we had gotten way more than our $130K out of it by then.
     
    Last edited: Apr 5, 2015
  19. dalethorn

    dalethorn Guest

    I've always used the WinXP virtual PC under Windows 7 (have not tried Windows 8). Normally that XP box won't allow XP to 'see' the main computer as drives E, F, G etc., but I found a way to use Terminal Services from that XP window that does allow that.