///////////////////////////////////////////////////////////////////// // (c) Gatan Inc. ///////////////////////////////////////////////////////////////////// // This script displays a complex image encoded as colour-image, using // HUE values for the phase and BRIGHTNESS for the modulus value. // The script is split into multiple methods which may be installed as // a library for use elsewhere // There are methods for single number conversion and equivalent methods // using images as parameters. // // RGB -> HSL algorithm by: // "Fundamentals of Interactive Computer Graphics" // by Foley and van Dam (c 1982, Addison-Wesley). Chapter 17 ///////////////////////////////////////////////////////////////////// // last modified 31-July-2015 BS /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /* Methods to convert RGB values into HSL values */ /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ void RGB2HSL(number R, number G, number B, number &H, number &S, number &L) { H = 0; S = 0; L = 0; // STEP 1: Convert the RGB values to the range 0-1 // RGB images are 0.255 integer. Convert to 0..1 float, R = R/255; ; G = G/255; B = B/255; if ((r<0)||(r>1)) Throw("Error in RGB->HSL") if ((g<0)||(g>1)) Throw("Error in RGB->HSL") if ((b<0)||(b>1)) Throw("Error in RGB->HSL") // STEP 2: Find min and max values of R, B, G number minCol = minimum(R,G,B) number maxCol = maximum(R,G,B) if (minCol>maxCol) Throw("Error in RGB->HSL") // STEP 3: L=(minCol+maxCol)/2 L = (minCol+maxCol)/2 // STEP 4a: Calculate S // L< 0.5 ==> S=maxCol-minCol)/(maxCol+minCol) // L>=0.5 ==> S=maxCol-minCol)/(2-maxCol-minCol) if (L<0.5) S=(maxCol-minCol)/(maxCol+minCol) else S=(maxCol-minCol)/(2-maxCol-minCol) // STEP 4b: Test for gray-value (S=0). This is the case for minCol=maxCol. Set H=0 as well (is undefined) if (minCol==maxCol) S=0 // STEP 5: Calculate H based on which color is maxCol // If R=maxcolor, H = (G-B)/(maxcolor-mincolor) // If G=maxcolor, H = 2.0 + (B-R)/(maxcolor-mincolor) // If B=maxcolor, H = 4.0 + (R-G)/(maxcolor-mincolor) if (R==maxCol) H= (G-B)/(maxCol-minCol) else if (G==maxCol) H= 2 + (B-R)/(maxCol-minCol) else if (B==maxCol) H= 4 + (R-G)/(maxCol-minCol) if (minCol==maxCol) H= 0 if (H<0) H+=6 if (H>6) H-=6 H = H/6 // Rescale to the 0..255 range L=L*255; S=S*255; H=H*255; H=round(H); S=round(S); L=round(L); if ((H<0||H>255)) Throw("Error in RGB->HSL") if ((S<0||S>255)) Throw("Error in RGB->HSL") if ((L<0||L>255)) Throw("Error in RGB->HSL") } rgbNumber RGB2HSL(rgbnumber RGBnum) { number r=red(RGBnum) number g=green(RGBnum) number b=blue(RGBnum) number h,s,l RGB2HSL(r,g,b,h,s,l) return rgb(h,s,l) } void RGB2HSL(image R, image G, image B, image &H, image &S, image &L) { R.ConvertToFloat() G.ConvertToFloat() B.ConvertToFloat() H = r*0; S = r*0; L = r*0 // STEP 1: Convert the RGB values to the range 0-1 // RGB images are 0.255 integer. Convert to 0..1 float, R = R/255; ; G = G/255; B = B/255; if (sum(tert(r<0||r>1,1,0))!=0) Throw("Error in RGB->HSL") if (sum(tert(g<0||g>1,1,0))!=0) Throw("Error in RGB->HSL") if (sum(tert(b<0||b>1,1,0))!=0) Throw("Error in RGB->HSL") // STEP 2: Find min and max values of R, B, G image minCol = minimum(R,G,B) image maxCol = maximum(R,G,B) if (sum(tert(minCol>maxCol,1,0))!=0) Throw("Error in RGB->HSL") // STEP 3: L=(minCol+maxCol)/2 L = (minCol+maxCol)/2 // STEP 4a: Calculate S // L< 0.5 ==> S=maxCol-minCol)/(maxCol+minCol) // L>=0.5 ==> S=maxCol-minCol)/(2-maxCol-minCol) S = tert(L<0.5,(maxCol-minCol)/(maxCol+minCol),(maxCol-minCol)/(2-maxCol-minCol)) // STEP 4b: Test for gray-value (S=0). This is the case for minCol=maxCol. Set H=0 as well (is undefined) S = tert(minCol==maxCol,0,S) // STEP 5: Calculate H based on which color is maxCol // If R=maxcolor, H = (G-B)/(maxcolor-mincolor) // If G=maxcolor, H = 2.0 + (B-R)/(maxcolor-mincolor) // If B=maxcolor, H = 4.0 + (R-G)/(maxcolor-mincolor) h=0 H = tert(R==maxCol, (G-B)/(maxCol-minCol),H) H = tert(G==maxCol,2 + (B-R)/(maxCol-minCol),H) H = tert(B==maxCol,4 + (R-G)/(maxCol-minCol),H) H = tert(minCol==maxCol,0,H) H=tert(H<0,H+6,tert(H>6,H-6,H) ) H = H/6 // Rescale to the 0..255 range L=L*255; S=S*255; H=H*255; H=round(H); S=round(S); L=round(L); H.SetName("H");S.SetName("S");L.SetName("L"); if (sum(tert(H<0||H>255,1,0)!=0)) Throw("Error in RGB->HSL") if (sum(tert(S<0||S>255,1,0)!=0)) Throw("Error in RGB->HSL") if (sum(tert(L<0||L>255,1,0)!=0)) Throw("Error in RGB->HSL") } RGBimage RGB2HSL(rgbimage RGBin) { image R = RGBin.red() image G = RGBin.green() image B = RGBin.blue() image H=r*0; image S=r*0; image L=r*0; RGB2HSL(R,G,B,H,S,L) return rgb(H,S,L) } RGBimage RGB2HSL(image Rin, image Gin, image Bin) { return RGB2HSL(RGB(Rin,Gin,Bin)) } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /* Methods to convert HSL values into RGB values */ /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ void HSL2RGB(number H, number S, number L, number &R, number &G, number &B) { R = 0; G = 0; B = 0; // STEP 1: Convert the input values to the range 0-1 // RGB images are 0.255 integer. Convert to 0..1 float, H = H/255; ; S = S/255; L = L/255; // Step 2: Test L and compute temp. values // If L < 0.5, temp2=L*(1.0+S) // If L >= 0.5, temp2=L+S - L*S number temp2 if (L<0.5) temp2 = L*(1+S) else temp2 = L+S-L*S number temp1 = 2.0*L - temp2 // Step 3: compute R number temp3=H+1/3 if (temp3>1) temp3-=1; if (temp3<0) temp3+=1; if (6.0*temp3 < 1 ) R = temp1+(temp2-temp1)*6.0*temp3 else if (2.0*temp3 < 1 ) R = temp2 else if (3.0*temp3 < 2 ) R = temp1+(temp2-temp1)*(2/3-temp3)*6.0 else R = temp1 // Step 4: compute G temp3=H if (temp3>1) temp3-=1; if (temp3<0) temp3+=1; if (6.0*temp3 < 1 ) G = temp1+(temp2-temp1)*6.0*temp3 else if (2.0*temp3 < 1 ) G = temp2 else if (3.0*temp3 < 2 ) G = temp1+(temp2-temp1)*(2/3-temp3)*6.0 else G = temp1 // Step 5: compute B temp3=H-1/3 if (temp3>1) temp3-=1; if (temp3<0) temp3+=1; if (6.0*temp3 < 1 ) B = temp1+(temp2-temp1)*6.0*temp3 else if (2.0*temp3 < 1 ) B = temp2 else if (3.0*temp3 < 2 ) B = temp1+(temp2-temp1)*(2/3-temp3)*6.0 else B = temp1 // Step 6: if S=0 (gray) all R,G,B = L if (S==0) { R=L; G=L; B=L; } // Scale back to 0..255 R=R*255; G=G*255; B=B*255; R=round(R); G=round(G); B=round(B); if ((R<0||R>255)) Throw("Error in HSL->RGB") if ((G<0||G>255)) Throw("Error in HSL->RGB") if ((B<0||B>255)) Throw("Error in HSL->RGB") } rgbNumber HSL2RGB(rgbNumber HSLnum) { number h=red(HSLnum) number s=green(HSLnum) number l=blue(HSLnum) number r,g,b HSL2RGB(h,s,l,r,g,b) return rgb(r,g,b) } void HSL2RGB(image H, image S, image L,image &R, image &G, image &B) { H.ConvertToFloat() S.ConvertToFloat() L.ConvertToFloat() R = H*0; G = H*0; B = H*0; // STEP 1: Convert the input values to the range 0-1 // RGB images are 0.255 integer. Convert to 0..1 float, H = H/255; ; S = S/255; L = L/255; // Step 2: Test L and compute temp. values // If L < 0.5, temp2=L*(1.0+S) // If L >= 0.5, temp2=L+S - L*S image temp2 = tert(L<0.5,L*(1+S),L+S-L*S) image temp1 = 2.0*L - temp2 // Step 3: compute R image temp3=H+1/3 temp3=tert(temp3>1,temp3-1,tert(temp3<0,temp3+1,temp3)) image Ch1 = temp1+(temp2-temp1)*6.0*temp3 image Ch2 = temp2 image Ch3 = temp1+(temp2-temp1)*(2/3-temp3)*6.0 image Ch4 = temp1 R = tert( 6.0*temp3 < 1 , Ch1, \ tert( 2.0*temp3 < 1 , Ch2, \ tert( 3.0*temp3 < 2 , Ch3, \ Ch4 ))) // Step 4: compute G temp3=H temp3=tert(temp3>1,temp3-1,tert(temp3<0,temp3+1,temp3)) Ch1 = temp1+(temp2-temp1)*6.0*temp3 Ch2 = temp2 Ch3 = temp1+(temp2-temp1)*((2.0/3.0)-temp3)*6.0 Ch4 = temp1 G = tert( 6.0*temp3 < 1 , Ch1, \ tert( 2.0*temp3 < 1 , Ch2, \ tert( 3.0*temp3 < 2 , Ch3, \ Ch4 ))) // Step 5: compute B temp3=H-1/3 temp3=tert(temp3>1,temp3-1,tert(temp3<0,temp3+1,temp3)) Ch1 = temp1+(temp2-temp1)*6.0*temp3 Ch2 = temp2 Ch3 = temp1+(temp2-temp1)*((2.0/3.0)-temp3)*6.0 Ch4 = temp1 B = tert( 6.0*temp3 < 1 , Ch1, \ tert( 2.0*temp3 < 1 , Ch2, \ tert( 3.0*temp3 < 2 , Ch3, \ Ch4 ))) // Step 6: if S=0 (gray) all R,G,B = L image bGray = tert(S==0,1,0) R = L*bGray+R*!bGray G = L*bGray+G*!bGray B = L*bGray+B*!bGray // Scale back to 0..255 R=R*255; G=G*255; B=B*255; R=round(R); G=round(G); B=round(B); R.SetName("R");G.SetName("G");B.SetName("B"); if (sum(tert(R<0||R>255,1,0)!=0)) Throw("Error in HSL->RGB") if (sum(tert(G<0||G>255,1,0)!=0)) Throw("Error in HSL->RGB") if (sum(tert(B<0||B>255,1,0)!=0)) Throw("Error in HSL->RGB") } RGBimage HSL2RGB(rgbimage HSLin) { image H = HSLin.red() image S = HSLin.green() image L = HSLin.blue() image R=H*0; image G=H*0; image B=H*0; HSL2RGB(H,S,L,R,G,B) return RGB(R,G,B) } RGBimage HSL2RGB(image Hin, image Sin, image Lin) { return HSL2RGB(RGB(Hin,Sin,Lin)) } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /* Methods to create RGB color images out of complex images, */ /* using phase & modulus represented as HUE and BRIGHTNESS */ /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ RGBimage ComplexAsHSL(compleximage in,number Rmin, number Rmax, number Pmin, number Pmax, number HUEstart) { realimage PhiImg = phase(in) // this returns [-Pi , Pi] realimage Rimg = modulus(in) realimage sat=Rimg*0+1 // Do a mapping of regions. Only represent R map [Rmin,Rmax] = [0,1] // result("\n map ["+min( Rimg)+","+max( Rimg)+"] to ["+rMin+","+rMax+"]") RImg = tert(RImgrMax,rMax,RImg) RImg-=rMin; RImg*=1/(rMax-rMin) // Do a mapping of regions. Only represent Phi map [Phimin,Phimax] = [0,1] // result("\n map ["+min(PhiImg)+","+max(PhiImg)+"] to ["+pMin+","+pMax+"]") PhiImg = tert(PhiImgpMax,pMax,PhiImg) PhiImg-=pMin; PhiImg*=1/(pMax-pMin) PhiImg+=HUEstart While (sum(tert(PhiImg>1,1,0))>0) PhiImg-=tert(PhiImg>1,1,0) While (sum(tert(PhiImg<0,1,0))>0) PhiImg+=tert(PhiImg<0,1,0) Rimg*=255; PhiImg*=255; sat=255; return HSL2RGB(RGB(PhiImg,sat,Rimg)) } RGBimage ComplexAsHSL(compleximage in, number HUEstart) { realimage PhiImg = phase(in) realimage Rimg = modulus(in) return ComplexAsHSL(in,min(Rimg),max(Rimg),min(PhiImg),max(PhiImg),HUEstart) } RGBimage ComplexAsHSL(compleximage in) { realimage PhiImg = phase(in) realimage Rimg = modulus(in) return ComplexAsHSL(in,min(Rimg),max(Rimg),min(PhiImg),max(PhiImg),0) } RGBimage ComplexAsHSL(compleximage in, number Pmin, number Pmax, number HUEstart) { realimage PhiImg = phase(in) // this returns [-Pi , Pi] realimage Rimg = modulus(in) return ComplexAsHSL(in,min(Rimg),max(Rimg),pMin,pMax,HUEstart) } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /* Methods to create a scaled RGB color images out of complex images including a color legend. */ /* using phase & modulus represented as HUE and BRIGHTNESS */ /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ Component ComplexAsHSLlegend(number SHOW, number sx, number sy, number Rmin, number Rmax, number Pmin, number Pmax, number HUEstart) { imagedocument imgDoc imgDoc=NewImageDocument("Legend") image Pimg,Rimg Rimg:=RealImage("R",4,sx,sy) = icol/sx*(Rmax-Rmin)+Rmin Pimg:=RealImage("P",4,sx,sy) = irow/sy*(2*Pi())-Pi() compleximage c c=complex(Rimg,Pimg).rect() RGBimage legend = ComplexAsHSL(c,Rmin,Rmax,Pmin,Pmax,HUEstart) Component doc = imgDoc.ImageDocumentAddImageDisplay(legend,4) number size=sy/15 string str str=" -Pi -" component Comp Comp=NewTextAnnotation(0,0,str,size) doc.ComponentAddChildAtBeginning(comp) Comp.ComponentPositionAroundPoint(0,0,1.0,0.5,1,1) str=" +Pi -" Comp=NewTextAnnotation(0,0,str,size) doc.ComponentAddChildAtBeginning(comp) Comp.ComponentPositionAroundPoint(0,sy,1.0,0.5,1,1) str="|" Comp=NewTextAnnotation(0,0,str,size) doc.ComponentAddChildAtBeginning(comp) Comp.ComponentPositionAroundPoint(0,sy,0.5,0,1,1) Comp=NewTextAnnotation(0,0,str,size) doc.ComponentAddChildAtBeginning(comp) Comp.ComponentPositionAroundPoint(0,sy,0.5,-1,1,1) Comp=NewTextAnnotation(0,0,str,size) doc.ComponentAddChildAtBeginning(comp) Comp.ComponentPositionAroundPoint(sx,sy,0.5,0,1,1) str=""+rmin Comp=NewTextAnnotation(0,0,str,size) doc.ComponentAddChildAtBeginning(comp) Comp.ComponentPositionAroundPoint(0,sy,0.5,-2,1,1) str=""+rmax Comp=NewTextAnnotation(0,0,str,size) doc.ComponentAddChildAtBeginning(comp) Comp.ComponentPositionAroundPoint(sx,sy,0.5,-1,1,1) if (show) imgDoc.ImageDocumentClone(1).ImageDocumentShow() component outComp=imgDoc.ImageDocumentGetRootComponent().ComponentGetChild(0) return outComp } imagedocument ComplexAsHSLwithLegend(compleximage in, number HUEstart) { realimage PhiImg = phase(in) realimage Rimg = modulus(in) number sx,sy number lx=50 number ly=200 in.getsize(sx,sy) RGBimage out = ComplexAsHSL(in,min(Rimg),max(Rimg),min(PhiImg),max(PhiImg),HUEstart) imagedocument iDoc=NewImageDocument("") Component comp = iDoc.ImageDocumentAddImageDisplay(out,4) Component legComp = ComplexAsHSLlegend(0,lx,ly,min(Rimg),max(Rimg),min(PhiImg),max(PhiImg),HUEstart) comp.ComponentAddChildAtEnd(legComp) ImageDocument finalDoc = idoc.ImageDocumentClone(1) comp = finalDoc.ImageDocumentGetRootComponent().ComponentGetChild(0).ComponentGetChild(0) number rat=min(sx/lx,sy/ly)*0.75 comp.ComponentTransformCoordinates(sx*1.2,sy*0.1,rat,rat) return finalDoc } imagedocument ComplexAsHSLwithLegend(compleximage in, number Pmin, number Pmax, number HUEstart) { realimage PhiImg = phase(in) realimage Rimg = modulus(in) number sx,sy number lx=50 number ly=200 in.getsize(sx,sy) RGBimage out = ComplexAsHSL(in,min(Rimg),max(Rimg),pMin,pMax,HUEstart) imagedocument iDoc=NewImageDocument("") Component comp = iDoc.ImageDocumentAddImageDisplay(out,4) Component legComp = ComplexAsHSLlegend(0,lx,ly,min(Rimg),max(Rimg),pMin,pMax,HUEstart) comp.ComponentAddChildAtEnd(legComp) ImageDocument finalDoc = idoc.ImageDocumentClone(1) comp = finalDoc.ImageDocumentGetRootComponent().ComponentGetChild(0).ComponentGetChild(0) number rat=min(sx/lx,sy/ly)*0.75 comp.ComponentTransformCoordinates(sx*1.2,sy*0.1,rat,rat) return finalDoc } imagedocument ComplexAsHSLwithLegend(compleximage in,number Rmin, number Rmax, number Pmin, number Pmax, number HUEstart) { realimage PhiImg = phase(in) // this returns [-Pi , Pi] realimage Rimg = modulus(in) realimage sat=Rimg*0+1 number sx,sy number lx=50 number ly=200 in.getsize(sx,sy) // Do a mapping of regions. Only represent R map [Rmin,Rmax] = [0,1] // result("\n map ["+min( Rimg)+","+max( Rimg)+"]to ["+rMin+","+rMax+"]") RImg = tert(RImgrMax,rMax,RImg) RImg-=rMin; RImg*=1/(rMax-rMin) // Do a mapping of regions. Only represent Phi map [Phimin,Phimax] = [0,1] //result("\n map ["+min(PhiImg)+","+max(PhiImg)+"]to ["+pMin+","+pMax+"]") PhiImg = tert(PhiImgpMax,pMax,PhiImg) PhiImg-=pMin; PhiImg*=1/(pMax-pMin) PhiImg+=HUEstart While (sum(tert(PhiImg>1,1,0))>0) PhiImg-=tert(PhiImg>1,1,0) While (sum(tert(PhiImg<0,1,0))>0) PhiImg+=tert(PhiImg<0,1,0) Rimg*=255; PhiImg*=255; sat=255; image out= HSL2RGB(RGB(PhiImg,sat,Rimg)) imagedocument iDoc=NewImageDocument("") Component comp = iDoc.ImageDocumentAddImageDisplay(out,4) Component legComp = ComplexAsHSLlegend(0,lx,ly,rMin,Rmax,pMin,pMax,HUEstart) comp.ComponentAddChildAtEnd(legComp) ImageDocument finalDoc = idoc.ImageDocumentClone(1) finalDoc.ImageDocumentSetName( "Complex color coded with legend, Phase = HUE, Mod = brightness" ) comp = finalDoc.ImageDocumentGetRootComponent().ComponentGetChild(0).ComponentGetChild(0) number rat=min(sx/lx,sy/ly)*0.75 comp.ComponentTransformCoordinates(sx*1.2,sy*0.1,rat,rat) return finalDoc } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /* Application test methods */ /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ Void TestComplexImageAsHUE() { Image P:=RealImage("Phase",4,512,512)=sin((irow+iradius)/500*Pi()) Image M:=RealImage("Modulus",4,512,512)=iradius+icol ComplexImage C = Complex(M,P).rect() RGBImage HUEImg := ComplexAsHSL(c,0) HUEImg.SetName( "Complex colorcoded, Phase = HUE, Mod = brightness" ) C.SetName( "Complex" ) P.SetName( "Phase") M.SetName( "Mod") HUEImg.ShowImage() C.ShowImage() ImageDocument HUEImgWithLegend =ComplexAsHSLwithLegend( C, 100, 500, 0, PI(), 0 ) HUEImgWithLegend.ImageDocumentShow() } TestComplexImageAsHUE()