tag:blogger.com,1999:blog-107037802024-03-13T03:41:57.818+02:00ChocotoothOrthodontics, software development (Delphi), chocolates and other related stuff.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.comBlogger26125tag:blogger.com,1999:blog-10703780.post-22014824311398464052011-07-25T11:47:00.002+03:002011-07-25T11:51:34.042+03:00Controlling a digital camera with Delphi (part 3). How to shoot a pictureThe following Delphi code can be used to shoot a picture. The camera must support the command, otherwise nothing happens. Note that the code needs better error checking but it should work.<br /><pre><br />procedure TMainForm.ShootPicture;<br />var<br />aPortableDevManager: IPortableDeviceManager;<br />deviceIDs: TArrayPWideChar;<br />countDeviceIDs: Cardinal;<br />res: HResult;<br />aPortableDevice: IPortableDevice;<br />aPortableDevValues: IPortableDeviceValues;<br />key: _tagpropertykey;<br />aCmdPortableDevValues, aCmdPortableDevValuesResult: IPortableDeviceValues;<br />aCmdGUID: TGUID;<br />begin<br />//for info see: http://msdn.microsoft.com/en-us/library/dd319331%28v=VS.85%29.aspx<br />aPortableDevManager := CoPortableDeviceManager.Create;<br />if VarIsClear(aPortableDevManager) then exit;<br />//get number of devices<br />countDeviceIDs := 0;<br />res := aPortableDevManager.GetDevices(nil, countDeviceIDs);<br />if (res < 0) or (countDeviceIDs = 0) then exit; //failed<br />//get all devices:<br />setLength(deviceIDs, countDeviceIDs);<br />res := aPortableDevManager.GetDevices(@deviceIDs[0], countDeviceIDs);<br />if res < 0 then exit; //failed<br />//get properties of the first device:<br />//create device:<br />aPortableDevice := CoPortableDevice.Create;<br />if VarIsClear(aPortableDevice) then exit;<br />//create device values:<br />aPortableDevValues := CreateComObject(CLASS_PortableDeviceValues) as IPortableDeviceValues;<br />if VarIsClear(aPortableDevValues) then exit;<br />//open the device (assume the camera is the first device):<br />res := aPortableDevice.Open(deviceIDs[0], aPortableDevValues);<br />if res < 0 then exit; //failed<br />//Note: when FAILED, we should Release the interfaces. (not implemented yet)<br />//shoot a picture:<br />//assume the camera supports this command, otherwise nothing happens.<br />//create device values:<br />aCmdPortableDevValues := CreateComObject(CLASS_PortableDeviceValues) as IPortableDeviceValues;<br />if VarIsClear(aCmdPortableDevValues) then exit;<br /><br />key.fmtid := WPD_CATEGORY_COMMON;<br />key.pid := WPD_PROPERTY_COMMON_COMMAND_CATEGORY;<br />aCmdGUID := WPD_CATEGORY_STILL_IMAGE_CAPTURE;<br />res := aCmdPortableDevValues.SetGuidValue(key, aCmdGUID);<br />if res < 0 then exit; //failed<br /><br />key.fmtid := WPD_CATEGORY_COMMON;<br />key.pid := WPD_PROPERTY_COMMON_COMMAND_ID;<br />res := aCmdPortableDevValues.SetUnsignedIntegerValue(key, WPD_COMMAND_STILL_IMAGE_CAPTURE_INITIATE);<br />if res < 0 then exit; //failed<br /><br />//Shoot:<br />res := aPortableDevice.SendCommand(0, aCmdPortableDevValues, aCmdPortableDevValuesResult);<br />if res < 0 then exit; //failed<br />//I do not care about result:<br />aCmdPortableDevValuesResult := nil;<br />aCmdPortableDevValues := nil;<br />end;<br /></pre>Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com6tag:blogger.com,1999:blog-10703780.post-70210746680219204472011-05-10T20:08:00.003+03:002011-05-10T20:15:05.701+03:00Controlling a digital camera with Delphi (part 2)As described in my previous post, I started coding with the Windows Portable Device (WPD) library. To do this in Delphi, you first need to create a TLB.PAS file that contains the declarations and other info from the library. I am using Delphi 2009, so the steps for this version are (assuming you have your project open):<br /><br />1. menu: ‘Component -> Import Component…’<br />2. ‘Import a Type Library’, then click ‘Next’<br />3. From the list select: ‘PortableDeviceApi 1.0 Type Library’, then click ‘Next’<br />4. In the ‘Unit Dir Name’ box, enter the directory where you want the unit to be created, then click ‘Next’<br />5. Select ‘Create Unit’, then click ‘Finish’.<br />You should see the new Unit in Delphi. Add the unit to your project.<br /><br />The library contains most of the information you need, but there are a few things we need to modify. First of all, add the following in the const declarations at the beginning of the unit (not necessary, but useful):<br /><pre><br />CLASS_PortableDeviceValues: TGUID = '{0c15d503-d017-47ce-9016-7b3f978721cc}';<br /></pre><br />Then, change the declarations for the following functions, which are not properly defined (I wasted a few days until I figured this out):<br />For ‘IEnumPortableDeviceObjectIDs’, change the ‘Next’ function to:<br /><pre><br />function Next(cObjects: LongWord;<br /> pObjIDs: Pointer;<br /> var pcFetched: LongWord): HResult; stdcall;<br /></pre><br />For ‘IPortableDeviceManager’, change the ‘GetDevices’ and ‘GetDeviceFriendlyName’ to:<br /><pre><br />function GetDevices(pPnPDeviceIDs: Pointer;<br /> var pcPnPDeviceIDs: LongWord): HResult; stdcall;<br />function GetDeviceFriendlyName(pszPnPDeviceID: PWideChar;<br /> pDeviceFriendlyName: Pointer;<br /> var pcchDeviceFriendlyName: LongWord): HResult; stdcall;<br /></pre><br />There may be other functions that need editing but these are the ones I have discovered so far.<br />In addition, it is a good idea to define some constants, to make your life easier, especially when translating from c code. I created a new unit (named WPD) and added a long list of constants and some useful functions. I will post a link to the file once I clean it up a bit, so be patient for now. Most of the info can be found in the h files that are included in the Microsoft Windows Software Development Kit for Windows 7. This can be freely downloaded from the Microsoft web site. Also, be sure to visit and carefully study the <a href="http://msdn.microsoft.com/en-us/library/dd388998(v=vs.85).aspx">Microsoft site for portable devices</a> which contains examples of c code, upon which I based my implementation.<br /><br />To end today’s post, here is code to look for wpd devices connected to your computer and list their names and device Ids to a Memo component:<br /><pre><br />procedure TMainForm.ListDevices;<br />var<br /> aPortableDevManager: IPortableDeviceManager;<br /> countDeviceIDs: Cardinal;<br /> res: HResult;<br /> deviceIDs: TArrayPWideChar;<br /> i: integer;<br /> pcchDeviceFriendlyName: Cardinal;<br /> DeviceFriendlyName: PWideChar;<br />begin<br />aPortableDevManager := CoPortableDeviceManager.Create;<br /> if VarIsClear(aPortableDevManager) then exit;<br /> //get number of devices<br /> countDeviceIDs := 0;<br /> res := aPortableDevManager.GetDevices(nil, countDeviceIDs);<br /> Memo1.Lines.Add('Devices found: ' + inttostr(countDeviceIDs));<br /> if (res < 0) or (countDeviceIDs = 0) then exit;<br /> //get all devices:<br /> setLength(deviceIDs, countDeviceIDs);<br /> res := aPortableDevManager.GetDevices(@deviceIDs[0], countDeviceIDs);<br /> if res < 0 then exit; //failed<br /> for i := 0 to countDeviceIDs - 1 do //enumerate the devices:<br /> begin<br /> Memo1.Lines.Add(deviceIDs[i]);<br /> //get length of friendly name:<br /> pcchDeviceFriendlyName := 0;<br /> aPortableDevManager.GetDeviceFriendlyName(deviceIDs[i],<br /> nil,<br /> pcchDeviceFriendlyName);<br /> DeviceFriendlyName := PWideChar(StringOfChar(' ', pcchDeviceFriendlyName));<br /> //get name:<br /> aPortableDevManager.GetDeviceFriendlyName(deviceIDs[i],<br /> @DeviceFriendlyName[0],<br /> pcchDeviceFriendlyName);<br /> Memo1.Lines.Add(DeviceFriendlyName);<br /> end;<br />end;<br /></pre><br />You will need these in your ‘uses’ list: Windows, ComObj, ActiveX, PortableDeviceApiLib_TLB, Variants, Classes<br />TArrayPWideChar was defined as:<br /><pre><br />type<br /> pArrayPWideChar = ^TArrayPWideChar;<br /> TArrayPWideChar = array of PWideChar;<br /></pre><br />In my next post I hope to show you how to shoot a picture.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com2tag:blogger.com,1999:blog-10703780.post-4414977207025152462011-05-04T22:13:00.002+03:002011-05-04T22:17:43.070+03:00Controlling a digital camera with DelphiI want to create an application that will control a digital camera using Delphi, so that I can take pictures by issuing commands from the computer and then transfer them to my hard disk, all through the USB connection. I thought this would be relatively easy, but I managed to get lost pretty quickly.<br />First of all, I tried to implement the Picture Transfer Protocol (PTP), which is a relatively low level interface. I tried code by Miguel Lucero (<a href="http://www.delphi3000.com/articles/article_4077.asp">http://www.delphi3000.com/articles/article_4077.asp</a>) and Mike Heydon (<a href="http://www.delphi3000.com/articles/article_4841.asp">http://www.delphi3000.com/articles/article_4841.asp</a>) and managed to get the device name of the camera from the registry. Then I tried to communicate with it through the USB interface using the CreateFile and WriteFile/ReadFile functions. The CreateFile function seemed to work fine, returning what appeared as a valid handle:<br /><br />For an example see <a href="http://members.fortunecity.com/sanya_k/oly/registry.htm">http://members.fortunecity.com/sanya_k/oly/registry.htm</a>, and the same code in Delphi:<br /><pre><br />hcamIN := CreateFile(PWideChar(pipeIn),<br /> GENERIC_READ,<br /> FILE_SHARE_READ,<br /> nil, // no SECURITY_ATTRIBUTES structure<br /> OPEN_EXISTING, // No special create flags<br /> 0, // No special attributes<br /> 0); // No template file<br /></pre><br />The problem was with the ReadFile and WriteFile functions, which always returned a “Request is not supported” error (error 50), no matter what I tried. Then I tried the DeviceIoControl function, but this also failed. I do not know what the problem is. I am running Delphi 2009 on Windows 7. I tried running the application in administrator mode, but that did not work any better. I thought that perhaps the camera (a Nikon D70) does not support PTP or USB commands, but this is not true, because the Nikon Camera Control Pro software works perfectly and I could see the USB commands going through, with the help of a USB sniffer program (I tried SnoopyPro and USBlyzer). Finally, I gave up, after almost a week of trying. Perhaps it is Delphi’s fault. I would appreciate any suggestions. If anyone has had a similar experience, please let me know.<br /><br />After abandoning the USB low-level path, I decided to use the Windows Image Acquisition library (WIA), but soon realized that it is deprecated and that Windows Portable Devices interface (WPD) has taken its place. So, I started to code some basic stuff using WPD. I have made some progress and will report on it soon (I hope).Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-53453467464340941302008-12-26T18:17:00.006+02:002008-12-26T18:33:48.663+02:00Update of StickyButtonIn my <a href="http://chocotooth.blogspot.com/2008/12/delphi-2009.html">previous entry</a> I gave code to create a TButton descendant that emulates a TSpeedButton, but can accept 32 bit images from TImageLists (as TButtons do in Delphi 2009, but in contrast to TSpeedButtons). I mentioned that the new component does not automatically change state according to its Action's Checked property (if it is indeed associated with an Action). It turns out that this can be very easily corrected. Just add the following two procedures:<br /><pre><br />function TdhStickyButton.GetChecked: Boolean;<br />begin<br /> Result := Down;<br />end;<br /><br />procedure TdhStickyButton.SetChecked(Value: Boolean);<br />begin<br /> Down := Value;<br />end;<br /></pre><br />The declaration section now becomes:<br /><pre><br />type<br /> TdhStickyButton = class(TButton)<br /> private<br /> FDDown: Boolean;<br /> FJustKilledWhenDown: Boolean;<br /> procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;<br /> procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;<br /> procedure SetDownState(Value: Boolean);<br /> protected<br /> function GetChecked: Boolean; override;<br /> procedure SetChecked(Value: Boolean); override;<br /> public<br /> constructor Create(AOwner: TComponent); override;<br /> published<br /> property Down: Boolean read FDDown write SetDownState default false;<br /> end;<br /></pre>Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-56348757295030078142008-12-25T21:48:00.005+02:002008-12-26T18:50:02.079+02:00Delphi 2009Installed Delphi 2009 a week ago and have been trying to update my code. The big change is Unicode support. All strings are now Unicode strings by default so there are a lot of instances that need reassigning to AnsiString. That was not too difficult.<br /><br />My next attempt was to use 32 bit images in an ImageList. Not as easy as expected. I tried to load PNG images but the transparency did not come out very well. There was a faint grey border around the images, as if the semi-transparent pixels were grey instead of semi-transparent (fully transparent pixels were OK). After many hours of experimenting I found out that 32 bit bitmaps work perfectly. So now I use Inkscape to draw the icons, export to a PNG file, then use Pixelformer to convert from PNG to 32 bit BMP (using A8R8G8B8 format), then load the BMPs into the TImageList (which is set to 32 bits) and assign clNone as the transparent color. Pixelformer is a nice painting program that handles transparency very well and is perfect for this job.<br /><br />The alpha-channel images can be used on TButtons as well. Unfortunately, they do not work on TSpeedButtons. So I thought of changing all TSpeedButtons to TButtons, but TButtons do not have a Down state. I searched the web and found out I could use the BN_SETSTATE message to make a button seem depressed. The problem is, the button does not stay depressed: as soon as you move the focus away, the button pops up again. This strange phenomenon was partly explained by <a href="http://techrepublic.com.com/5208-10878-0.html?forumID=87&threadID=182949&messageID=1987325">Dennis Martin in this web page</a>. It seems that a Click event is fired when the button looses focus. Dennis has a solution, but I wanted to create a component to emulate a TSpeedButton. I came up with a different way. Here is the code (feel free to use and share):<br /><pre><br />//TdhStickyButton, stays down when Down is true.<br />//intercepts Kill_Focus message to set a flag that shows<br />// that it is down and was just killed (lost focus).<br />//intercepts WM_Command, which receives a Click message,<br />// and only sends it along if it was not just killed in a down state.<br />// also, resets temporary flag and sets the Down state appropriately<br />// (otherwise it reverts automatically).<br />type<br /> TdhStickyButton = class(TButton)<br /> private<br /> FDDown: Boolean;<br /> FJustKilledWhenDown: Boolean;<br /> procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;<br /> procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;<br /> procedure SetDownState(Value: Boolean);<br /> protected<br /> public<br /> constructor Create(AOwner: TComponent); override;<br /> published<br /> property Down: Boolean read FDDown write SetDownState default false;<br /> end;<br /><br />procedure Register;<br /><br />implementation<br /><br />procedure Register;<br />begin<br /> RegisterComponents('Additional', [TdhStickyButton]);<br />end;<br /><br />constructor TdhStickyButton.Create(AOwner: TComponent);<br />begin<br /> inherited Create(AOwner);<br /> FDDown := false;<br />end;<br /><br />procedure TdhStickyButton.WMKillFocus(var Message: TWMKillFocus);<br />begin<br /> if FDDown then FJustKilledWhenDown := true else FJustKilledWhenDown := false;<br /> inherited;<br />end;<br /><br />procedure TdhStickyButton.CNCommand(var Message: TWMCommand);<br />begin<br /> if (Message.NotifyCode = BN_CLICKED) and not(FJustKilledWhenDown) then Click;<br /> SendMessage(self.Handle, BM_SETSTATE, Longint(FDDown), 0);<br /> FJustKilledWhenDown := false;<br />end;<br /><br />procedure TdhStickyButton.SetDownState(Value: Boolean);<br />begin<br /> FDDown := Value;<br /> SendMessage(self.Handle, BM_SETSTATE, Longint(FDDown), 0);<br />end;<br /></pre><br />A StickyButton is used like a regular button. If you want to show it down, just set its 'Down' property to true. The button will not toggle between down and up automatically, you have to do it with code. Also, if you attach an Action to the button, it will not toggle together with the Action's 'checked' property, as SpeedButtons do; again, you have to do it with code.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com1tag:blogger.com,1999:blog-10703780.post-66845267900356240422008-04-24T11:06:00.006+03:002008-04-24T11:20:50.679+03:00How to Set the Language in a PowerPoint presentationTo change the language of all text in all slides (including the Notes of the slides), use the following macro:<br /><span style=";font-family:courier new;font-size:85%;"><br />Sub SetLangUK()<br />'set language to UK for all slides and notes:<br />Dim scount, j, k, fcount<br />scount = ActivePresentation.Slides.Count<br />For j = 1 To scount<br />fcount = ActivePresentation.Slides(j).Shapes.Count<br />For k = 1 To fcount 'change all shapes:<br />If ActivePresentation.Slides(j).Shapes(k).HasTextFrame Then<br /> ActivePresentation.Slides(j).Shapes(k).TextFrame _<br /> .TextRange.LanguageID = msoLanguageIDEnglishUK<br />End If<br />Next k<br />'change notes:<br />fcount = ActivePresentation.Slides(j).NotesPage.Shapes.Count<br />For k = 1 To fcount 'change all shapes:<br />If ActivePresentation.Slides(j).NotesPage.Shapes(k).HasTextFrame Then<br /> ActivePresentation.Slides(j).NotesPage.Shapes(k).TextFrame _<br /> .TextRange.LanguageID = msoLanguageIDEnglishUK<br />End If<br />Next k<br />Next j<br />End Sub<br /></span><br />I got half of this code from <a href="http://www.proz.com/forum/office_applications/34774-set_language_in_entire_ppt_presentation-.html">Antonín Otáhal</a> and added the part about the NotesPage.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com12tag:blogger.com,1999:blog-10703780.post-8955022571826615632007-06-24T22:02:00.000+03:002007-06-24T22:04:23.523+03:00Upgrading to Delphi 2007 Update 1Why should seemingly simple things induce so much frustration? I attempted to update my Delphi 2007 and lost more than 5 hours in the process and I haven’t finished yet. The InstallAware wizard is still installing. Everything started normally when I received a notification that Update 1 was available. So I downloaded the setup program and started the process. It seems that the setup program first uninstalls the whole of Delphi 2007 and then installs the updated copy. However, after uninstalling, it displayed a message saying that “Extraction of installation data downloaded from the web has failed. What would you like to do? Download a fresh copy of the installation data. Try to extract the existing download data again”. None of the options had any effect (I tried both many times) so the only choice was to quit the installer, leaving me with a computer without Delphi on it anymore! After googling a bit, I logged into Borland and downloaded the ‘Full download zip file (757MB)’ from CodeGear. However, installing from this file also seemed to produce exactly the same strange behaviour. Even though downloading from the web seemed to work fine, ‘extraction of data’ failed and nothing worked. I found the solution by luck: when the message appears, first select ‘Download a fresh copy’, then quit the installer and restart it. The installer will now properly extract the previously received file, but may fail on a subsequent one. Do the same thing for each file. This may require that you restart the installer many times, but eventually things work out OK.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-30908277777824710822007-05-12T20:59:00.000+03:002007-05-12T21:14:27.158+03:00Vista troublesAs anyone with even a tiny experience in upgrading from one OS to another would expect, going from Windows XP to Vista is not a trivial task. I installed Vista on one computer so that I could use it as a test-bed for upgrading my software ‘Viewbox’. Installation went fine except for the very frustrating fact that Vista Home Premium cannot be installed as an upgrade to XP Professional. You will need Vista Business or Ultimate to do that. So a clean install was done, requiring re-installation of a large number of software afterwards.<br />Then came the task of making Viewbox run on Vista (I already knew that Viewbox aborted on loading). I installed Delphi 2007 Professional and recompiled but the problem still persisted, flagging the error: ‘the computer does not have HTML Help support’. I knew this was not possible, because HTML Help support is built into Internet Explorer and depends on ‘hhctrl.ocx’, which is installed with Vista. The problem was that Viewbox could not find the ocx file. After a bit of googling, I found out that the file has changed location. If you want to find it, you should not rely on the registry entry, but you should look into the Windows folder. See the web page of <a href="http://www.helpware.net/">The Helpware Group</a> for more info.<br />However, even after solving this problem, Viewbox would not load. The problem was that I was using a TImageList component with a width of zero. Apparently, Windows XP has no problems with that, but Vista cannot tolerate it and shuts down the offending software. Fixing this was easy. Of course, you may ask why I was using a TImageList of zero width. That is another story.<br />After these changes, Viewbox seems to be running just fine under Vista. However, new security measures have been implemented in Vista. They include the User Account Control (UAC) system (for a detailed explanation, try this doc from Microsoft: WindowsVistaUACDevReqs.doc). The UAC does not allow writing of files in the C:\Program Files folder. This is a problem, because Viewbox saves its various settings in the Program Files\Viewbox folder as an INI file. I am not going to explain the details of the UAC (read the doc mentioned above). What you should know if you are programming in Delphi 2007, is that the XPManifest now includes an entry for the security level, as follows:<br /><span style="font-family:courier new;"> </span><<span style="font-family:courier new;">trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"</span>><br /> <<span style="font-family:courier new;">security</span>><br /><span style="font-family:courier new;"> </span> <<span style="font-family:courier new;">requestedPrivileges</span>><br /><span style="font-family:courier new;"> </span> <<span style="font-family:courier new;">requestedExecutionLevel</span><br /><span style="font-family:courier new;"> level="asInvoker"</span><br /><span style="font-family:courier new;"> uiAccess="false"/</span>><br /><span style="font-family:courier new;"> </span> <<span style="font-family:courier new;">/requestedPrivileges</span>><br /><span style="font-family:courier new;"> </span> <<span style="font-family:courier new;">/security</span>><br /><span style="font-family:courier new;"> </span><<span style="font-family:courier new;">/trustInfo</span><span style="font-family:courier new;"></span>><br />Setting ‘level’ equal to ‘asInvoker’ tells Vista not to implement virtualizing, so if your software tries to write to C:\Program Files\ it will fail. I have not figured out how to make Delphi change the Manifest contents so that virtualizing of files is possible.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com2tag:blogger.com,1999:blog-10703780.post-40903742961636580602007-02-27T11:58:00.000+02:002007-02-27T12:06:35.452+02:00TListBox bugAnother one of those bugs that make our life difficult. I have been trying to make a list box with variable item height. I wanted the height of each item to vary, depending on the item, so I thought I would associate a dummy object with each string of the list box, using code like this:<br /><br /><span style="font-family: courier new;">MyListBox.AddItem(aStr, Pointer(theHeight));<br /><br /></span>Then I would use an OnMeasureItem event to set the height:<br /><br /><span style="font-family: courier new;">procedure MyForm.MyListBoxMeasureItem(Control: TWinControl; Index: Integer; var Height: Integer);</span><br /><span style="font-family: courier new;">begin</span><br /><span style="font-family: courier new;">Height := integer(MyListBox.Items.Objects[Index]);</span><br /><span style="font-family: courier new;">end;<br /><br /></span>However, this does not work. The reason is rather interesting. When the AddItem procedure is called, the string is first added to the list box, then Delphi triggers the OnMeasureItem event, BEFORE the object has been added, so the event finds no object in the Items.Objects collection.<br />My workaround was to add the height as the first character of the string:<br /><br /><span style="font-family: courier new;">MyListBox.Items.Add(Char(theHeight) + aStr);<br /><br /></span>Then, in the OnMeasureItem procedure, I strip the character and convert it to an integer value:<br /><br /><span style="font-family: courier new;">Height := byte(MyListBox.Items[Index][1]);<br /><br /></span>Note that this will only work if the variable stored in the first character of the string is not zero, otherwise it will be mistaken as a string termination character and a null string will be added to the list box.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com1tag:blogger.com,1999:blog-10703780.post-1159362369034229312006-09-27T15:59:00.000+03:002006-11-11T18:21:51.680+02:00One year with the PriusOne year has passed since I got my Toyota Prius. I have been keeping a record of kilometres travelled and gas consumed. The average consumption over approximately 13000 Km has been about 5 lt/100 km. I have an <a href="http://www.dhal.com/downloads/prius.xls">Excel file here</a>, which contains a graph of ‘lt/100 km’. During the past year I have tried to let the tank empty as much as possible and then fill it up with 20 Euros. However, sometimes this was not possible, so you will see some spikes in the graph; these are artefacts of the recording procedure. The true average consumption is calculated as the total amount of litres over the whole year divided by the number of kilometres travelled.<br />Overall, I am still very pleased with this car. The only problem is limited visibility, especially towards the back.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1156866755155230092006-08-29T18:37:00.000+03:002006-08-29T18:52:35.180+03:00TListView troublesI have been trying to write some owner-draw procedures for a TListView in Delphi. Specifically, I need to draw some small bitmaps in the vsReport style. I thought this would be easy. So I used the OnCustomDrawSubItem event and all seemed fine until I started changing the width of the columns during run-time. The problem was that ‘column.width’ was not being updated during the dragging and the result was a mess. After looking into the source code of the ComCtrls unit I decided to write a custom control that would update the column widths properly. Again, I thought this would be easy. I spent almost all day trying to figure out why things were not working. Finally I discovered this blog entry: <a href="http://thomasfreudenberg.com/blog/comments/352.aspx">http://thomasfreudenberg.com/blog/comments/352.aspx</a>.<br />It seems that Microsoft has changed the notification for TListView and HDN_TRACK may not work. So, if you are having the same problems, here is my complete solution. It emulates a OnColumnTrack event (similar to the OnSectionTrack event of THeaderControl) and uses both HDN_TRACK and HDN_ITEMCHANGING to detect column changes.<br />This goes in the interface section of your unit:<br /><br /><span style="font-family:courier new;"><br />type<br /> TdhColumnTrackEvent = procedure(Sender: TCustomListview; Column: TListColumn; Width: Integer; State: TSectionTrackState) of object;<br /><br />TdhListView = class(TListview)<br /> private<br /> FColumnTrackEvent: TdhColumnTrackEvent;<br /> protected<br /> procedure WMNotify(var Msg: TWMNotify); message WM_NOTIFY;<br /> published<br /> property OnColumnTrack: TdhColumnTrackEvent read FColumnTrackEvent write FColumnTrackEvent;<br />end;</span><br /><br />This goes in the implementation section:<br /><br /><span style="font-family:courier new;">procedure TdhListView.WMNotify(var Msg: TWMNotify);<br />var<br /> TrackState: TSectionTrackState;<br /> aColumn: TListColumn;<br />begin<br /> inherited;<br /> with Msg do<br /> case NMHdr^.code of<br /> HDN_BEGINTRACK, HDN_ENDTRACK, HDN_TRACK, HDN_ITEMCHANGING:<br /> begin<br /> case NMHdr^.code of<br /> HDN_BEGINTRACK: TrackState := tsTrackBegin;<br /> HDN_ENDTRACK: TrackState := tsTrackEnd;<br /> else<br /> TrackState := tsTrackMove;<br /> end;<br /> with PHDNotify(Pointer(NMHdr))^, PItem^ do<br /> if ((Mask and HDI_WIDTH) <> 0) then<br /> begin<br /> aColumn := Columns[Item];<br /> if Assigned(FColumnTrackEvent) then FColumnTrackEvent(self, aColumn, cxy, TrackState);<br /> end;<br /> end;<br /> end;<br />end;</span>Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com1tag:blogger.com,1999:blog-10703780.post-1156865804162548322006-08-29T18:32:00.000+03:002006-08-29T18:36:44.173+03:00Vienna againIt has been along time since I updated this blog. It seems that I have so many things to do, there is hardly any time left. Of course, it is always a matter of priorities, so you can figure how high this blog is on my list. Anyway, I was in Vienna in July, two times during the same month. Very hot weather. Vienna is a great city. For chocolates I recommend ‘Xokolat’, at Freyung 2 (Tel. 5354363). A large selection and not too expensive. I tried a 100% for the first time and I definitely do not recommend it; too bitter. I threw most of it away. However, the regular ones were excellent. The ‘Queen of Finland’ was kind enough to bring me some Fazer, which was very much appreciated. I guess we will see each other again in Berlin.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1140088476678956252006-02-16T13:11:00.000+02:002006-05-09T04:25:49.686+03:00Minus 25 degreesGot back from Helsinki, after 4 days in the cold. I had a wonderful time and I have to thank the Queen of Finland once again (Jutti knows who I mean). I flew from Athens to Helsinki through Munich and I have to remember to avoid doing this in the future, because it seems that Munich has a tendency to get fogged-out in the winter, which leads to delays and cancellations. Fortunately I was lucky and had only half an hour of delay, which did not affect my arrival. Helsinki seemed out of this world. Everything frozen, snowing, landscape in black and white. I felt I had landed on another planet (e.g. Pluto). The next days were sunny, which, contrary to expectations (at least of those who live in milder climates) leads to lower temperatures (see title above). Some observations regarding these temperatures: snow does not melt, it just remains dust and gets blown by the wind like sand; also, do not touch metal doors or gates with bare hands (your hands will freeze and may get stuck on the cold metal). When I flew back and landed in Munich (transit), the minus 12 degrees there felt nice and warm.<br />Finland has the Fazer chocolates. I bought those with the chilli peppers and they were rather good, not too strong.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1127980292595022062005-09-29T10:50:00.000+03:002005-09-29T10:51:32.610+03:00My new PriusI bought a Toyota Prius just a week ago. I have been procrastinating over it for a long time, but finally reminded myself that we only live once. My primary reason was environmental, but I suspect that, in the end, other reasons will prove equally important (especially torque). First of all, I must say that fuel consumption is around 5 lt/100km, but I am keeping a log and will have more reliable data on this during the next months. My previous car, a Citroen Xanthia 1800, needed around 9.5 lt/100km, and that was by driving very smoothly and shifting into neutral when going downhill. My wife’s Peugeot 406 (2 lt) goes at 10 lt/100km. Apart from fuel consumption, other nice things are:<br />The Prius has an automatic transmission, which works very well and handles all the ‘high-tech’ business of engaging the electric or conventional engine and charging the battery (this is shown on the LCD display and is great fun in the beginning). After a while, this high-tech feeling may fade away and I think that I will not take much notice. However, when looking at the other cars on the road, you are reminded that you are driving something different.<br />Torque is great! I can climb steep roads going as slowly as I wish with no fear of the engine stalling. If the battery is well charged, this can even work with the electric engine only, so there is no noise (‘vroom-vroom’) and no exhaust fumes (very nice, if you are in a garage). There is a rather steep road very close to my office, with traffic lights. When the green light turns on, most cars have trouble starting without rolling back a bit. The Prius is so easy.<br />The car is silent (completely silent) when going on battery power. This is great in my garage, but if you are driving on the street, pedestrians may not realize you are there, so take care.<br />Interior room is very comfortable. The trunk is a bit small.<br />Acceleration is also very good (by family car standards), but, when the conventional engine kicks in, there is some engine noise. This is probably more noticeable in the Prius than other cars, because most of the times it is not there.<br />Main negative: Price was rather high, at Euro 28.500. When I bought the car, I was certain that fuel economy would not compensate for this, because I was comparing the Prius with cars that cost around 15.000 to 20.000 Euro. But now I think that this is more of a car than I thought, so perhaps a more valid comparison would be with a larger car, say an Avensis (at around 25000 Euro).Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com1tag:blogger.com,1999:blog-10703780.post-1125340151907862702005-08-30T09:29:00.000+03:002005-08-29T21:36:29.020+03:003D Rendering with Iso-surfacesI am back on iso-surface rendering. After reading a lot of stuff on the subject I decided to experiment using points to render the surfaces. This is very easy to implement: you go through the whole volume, detect where an iso-surface lies (take a ‘cube’ composed of 8 voxels and see if all of them have values higher or lower than the iso-surface value; if they do, there is no iso-surface there, otherwise mark the center of the cube as an iso-surface point), and draw an OpenGL point for every detected position. Points can be drawn using glDrawArrays for very fast results. You also need to enlarge the size of the points, so that they do not leave holes between them, otherwise the ‘surface’ looks like a point cloud. Do that with glPointSize. Round points can be drawn with glEnable(GL_POINT_SMOOTH).<br />The results were not very nice. The image looked very ‘blocky’, as you can see here (click on image to enlarge).<br /><a href='http://photos1.blogger.com/img/57/3538/320/isosurface1.jpg'><img border='0' style='border:1px solid #000000; margin:2px' src='http://photos1.blogger.com/img/57/3538/100/isosurface1.jpg'></a><br />To improve this I thought I had to resort to constructing a true surface (of triangles). The best-known algorithm for doing that is the ‘marching cubes’, but it is patented (!). I found some alternatives, but I was concerned of the speed factor, besides the fact that they all seemed rather complicated to implement. Then I read ‘Interactive Ray Tracing for Isosurface Rendering’ and ‘Iso-splatting: A Point-based Alternative to Isosurface Visualization’ (search Google and you will find the pdf files). From these papers I realized that the problem was not the points per se, or the supposedly reduced resolution of the data, but the position of the points. I was placing each point in the center of the ‘cube’ of eight voxels, but the iso-surface could be passing through the cube at any position. In this way, the points were all aligned to each other, producing the ‘blocky’ effect. The solution was to move the points towards the iso-surface. The papers, mentioned above, describe some sophisticated ways to do that (using Newton-Raphson or solving a cubic equation), but, not being mathematically so proficient, I thought of a simpler method: I start from the center of the cube and iteratively move towards the iso-surface as follows: I use the gradient vector inside the cube to specify the direction I will move along. The gradient points from lower to higher values. Therefore, I start from the center of the cube and calculate the value there. If it is smaller than the iso-surface value (i.e. I am inside the surface), I move along the gradient by an amount equal to 1/4 the side of the cube, otherwise I move in the opposite direction. Then I calculate the value at the new position and repeat the process, but each time I halve the distance that I move. 3 or 4 such steps are enough to give the result shown in the second image. The solution is approximate but the result is great and the extra code incurs a very small time penalty.<br /><a href='http://photos1.blogger.com/img/57/3538/320/isosurface2.jpg'><img border='0' style='border:1px solid #000000; margin:2px' src='http://photos1.blogger.com/img/57/3538/100/isosurface2.jpg'></a><br />Notice that, no matter how many times you iterate, you will never end up outside the ‘cube’. Conversely, if the iso-surface is very close to one of the corners, you will never reach it, but that does not seem to affect the result much.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com1tag:blogger.com,1999:blog-10703780.post-1125339879039985312005-08-29T21:23:00.000+03:002005-08-29T21:24:39.046+03:00Delphi 2005, Firefox and Hyper-threadingI got Delphi 2005. Installed it, tested it, liked it, went back to Delphi 7. Why? Because it seems to have annoying bugs. The first thing I noticed was the very long loading time. This is, of course, not a bug, but still, it is irritating. Presumably there are a lot of things to load, because Delphi 2005 comes with .NET support and a whole lot of other stuff. Anyway, I am willing to put up with this, but then I noticed that things were really moving very slowly. Response was lethargic and there was no explanation (my machine is a 3GHz with 1Gb of RAM). Then, there were problems with loading the TLB library of my project (by the way, the Delphi 7 project loaded with no compatibility problems, which is a huge plus). The library editor would not show the contents properly and Delphi kept asking to save the TLB file every time I exited, even if nothing had been changed.<br />The most serious problem was the very slow response of the program. I searched the Internet and finally found the answer: Delphi 2005 seems to be incompatible with hyperthreading. What is this, you may ask. Well, as far as I understand, the new motherboards have a special feature that makes Windows think that there are two processors available, when you only have one. This way, some programs are supposed to work faster. However, it seems that this may cause incompatibilities. There is a way to disable this from inside Windows, but it has to be done every time you start the specific program. I tried it with Delphi 2005 and after disabling the ‘dual processor’, things breezed along. I then remembered that I had the same problems with Firefox. Firefox just hung up for long intervals and I had to give it up and return to Internet Explorer. However, after disabling the ‘dual processor’, Firefox worked fine. So, in the end, I decided to disable this feature completely. This was easily done by using the BIOS setup. You need to go to Advanced CPU configuration and disable Hyper-Threading technology. After that, Windows sees only one processor and everything works great.<br />The end result? I am back on Firefox, but I am waiting till Borland issues an update on Delphi 2005 to start using it.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com1tag:blogger.com,1999:blog-10703780.post-1119441663736704012005-06-22T14:57:00.000+03:002005-06-24T19:16:29.803+03:00More recipes (evidence-based) and DICOMFinally, the British Medical Journal (BMJ) has released the results of the <a href="http://bmj.bmjjournals.com/cgi/content/full/330/7505/1422">“polymeal” competition</a>. You can find all recipes on <a href="bmj.bmjjournals.com/cgi/eletters/329/7480/0-f">bmj.com</a>. From the winner recipe (submitted by Heather Haywood) I particularly liked the fondue. I am going to try the other ones as well (albeit with some minor modifications), and perhaps let you know the results.<br />During the past couple of months I have been doing many things, one of which was to try and implement DICOM network communication between my cephalometric software and a DICOM server. I must say that this is a very frustrating endeavour because the DICOM information manual (published by NEMA - National Electrical Manufacturers Association, medical.nema.org) is very difficult to read and understand. I got a lot of help from looking at the source code provided by the <a href="http://dicom.offis.de/dcmtk.php.en">DCMTK software package</a> and using the <a href="http://www.xs4all.nl/~ingenium/dicom.html">Conquest software</a> as a testing bed. While I was doing all this, I was thinking about writing a do-it-yourself guide for all those who may need to do the same, but I am afraid that there is no free time. However, I am planning to release the source code, when it is done. Currently I am at the stage where the client communicates with the server and gets info about what images are available. The next step (which shouldn’t be that difficult) is to actually transfer the image from the server to the client.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1114701562733711432005-04-28T18:19:00.000+03:002005-04-28T18:32:26.016+03:00Baklava<a href="http://photos1.blogger.com/img/57/3538/320/baklava.jpg"><img style="margin: 10px;" src="http://photos1.blogger.com/img/57/3538/100/baklava.jpg" border="0" /></a><br />Yesterday I made some baklava (for the first time). Now this should be considered heresy, because it contains no chocolate, but it came out excellent (even Katerina thought so), so I will give you the recipe. You need phylo for baklava (very thin), 250 gr pistachio nuts, 250 gr butter, 500 gr sugar and 300 gr water. I used a 30 x 35 cm baking pan. First melt the butter and use a brush to apply on the pan. Then add one phylo at a time. After spreading phylo sheet on the pan, brush liberally with butter. Continue for 8 to 9 layers. Then add the crumbled nuts, then add 8 to 9 more layers of phylo. Very important: carve the pieces (all the way down) with a sharp knife before baking. Cover with aluminium foil and bake for 30 minutes at 180 degrees (Celcius), then uncover and bake for 30 more minutes at 160, until golden. Meanwhile prepare the syrup by boiling the sugar and water for 5-10 minutes. When the baking is over, pour the syrup (while hot). I calculated total calories at around 6500, so that comes out at approximately 270 calories per piece (24 servings). I include picture of final product. Of course, you may also have seen chocolate baklava! This is something I should try some other day.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1113841509280054342005-04-18T19:23:00.000+03:002005-04-18T19:26:25.383+03:00Back from Geneva and ViennaJust returned from Geneva and Vienna. Weather was so-so, cloudy with a drizzle. In Geneva it was also rather cold. Why do the shops there close so early? Everything closes at 7 pm, and some shop owners close earlier than that, even though they have a sign with 7 pm on it at the door. I missed Zogg's the first day by about 10 minutes, but managed to be there on time the next day. I finally bought some chocolates from Zogg's (3 rue du Mont-Blanc) and from Gilles Desplanches (2 Confederation, Place Bel-Air). Prices are at around 95 Swiss francs per kilo, which is about 65 Euro. Both excellent, although I think that Gilles Desplanches may be slightly better. After returning home I tried to get into Zogg's web site (www.jpzogg.ch) but it does not seem to work. I will try again tomorrow.<br />In Vienna I had a Sacher Torte at the Sacher hotel. Nice, but not completely to my taste. The freshly squeezed orange juice was better. Next time I think I will try the Coupe Romanoff. Vienna is a very impressive city (the old town). Unfortunately I did not have time for anything more than a quick stroll yesterday evening. I would love to go to some of the museums, and especially the Leonardo Da Vinci exhibit.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1113400409725634082005-04-13T16:51:00.000+03:002005-04-13T16:53:29.726+03:00Making FacesLeaving for Geneva and Vienna tomorrow. Unfortunately, I doubt it if I will have much time to hunt for chocolates. I guess I will have to make do with what I find at the airport (isn't that sad?), mainly to cover orders from friends and family.<br />Have been reading a very nice book, "Making Faces" by Prag and Neave of British Museum Press. It describes how scientists reconstruct faces from skeletal remains. The book deals mainly with archaeology but discusses forensics also. The authors build up the face by adding layers of muscles and skin (using clay) on a duplicate of the skull. The whole method seems to involve a fair amount of artistic skill, but the authors claim that the result is reproducible because it is based on scientific principles (mainly data on the average thickness of soft tissues at various points on the face). A literature search in PubMed did not find many papers dealing with this issue. It seems that data are rather sparse. I wonder if we could get better predictive power concerning the relationship of hard and soft tissues if we factor morphometrics into the procedure. Is soft tissue thickness dependent on facial shape? How well can we predict soft tissue shape if we know the skeletal shape underneath? Nose size and shape could very well be correlated to skeletal shape (I know it is, because I have done some preliminary statistics, but is the correlation strong enough to be useful?).Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1110113505188498992005-03-06T14:48:00.000+02:002005-03-06T14:59:23.790+02:00SVD algorithmImplemented IsoRegion leaping, as suggested by Fung and Heng (see posting of Feb 2, 2005). Speed increases significantly but is nowhere near interactive rates, for the size of volume and image that I target. A 700 x 650 image needs 6.3 secs to draw (compared to 14.4 without IsoRegion leaping). I am sure I could tweak some more speed out of this (e.g. by trying different brick sizes, optimizing some more) but the gain would probably be no more than 1 sec at the maximum. So the rest will have to come from reduced-quality rendering during interactive movement of the volume.<br />Meanwhile, during most of the past 2 weeks, I have been trying to implement a SVD (singular value decomposition) algorithm. I looked up the algorithm from <a href="http://www.nr.com/index.html">Numerical Recipes in C</a>, but the pdf files (<a href="http://www.library.cornell.edu/nr/cbookcpdf.html">c2-6.pdf</a>) have a buggy old version. I finally figured that out (after many frustrated hours). The most recent version I could find on the net was the file <a href="http://www.library.cornell.edu/nr/cornell_only/c.210/cpp/recipes/">svdcmp.cpp</a>, which needed just two corrections, as mentioned in the Numerical Recipes bug reports. It is also written for zero-based arrays, in contrast to the one in the pdf file. I converted this to Delphi and it works perfectly (after correcting another two bugs that I introduced myself). Now I can use this to find least squares solutions. My aim is to calculate principal components of shape when some of the points are missing. This will allow me to find the most probable positions of the missing points, based on the other points and the shape variability of the sample.<br />And now for a different kind of recipe: chocolate prunes. I use prunes (dried) without the pits, place them for a few hours in brandy and red wine (half and half). Then I melt chocolate, pour some in small paper cups, immerse half of a prune in each cup and cover with some more chocolate. Very easy. Then leave in the fridge until set. 200 gr of chocolate make up approximately 30 small cups. Whatever brandy and wine is left over, you drink (but don't drive).Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1108760569727874762005-02-18T23:02:00.000+02:002005-02-18T23:27:52.126+02:00Optimization continues<a href="http://photos1.blogger.com/img/57/3538/320/volrender1_50crop.jpg"><img style="margin: 10px;" src="http://photos1.blogger.com/img/57/3538/100/volrender1_50crop.jpg"/></a><br />After a rather hectic week, at last Friday is here. Of course, Fridays are not so nice, because I work from 8 am to 8 pm, but after that I have the whole weekend in front of me for doing some more work! During the week I did not have much time to optimize the volume rendering algorithm much. I have prepared a nice picture (I hope you like it) of the Chapel Hill Dataset. It is shown here at half the actual size (when you click on it) and cropped. The actual image is 700 x 650 pixels and it takes about 9.5 secs to draw. This is way too much, so I will have to get really creative with the optimization tricks. The volume is 208 x 256 x 225 voxels in size (rather small). I have applied a 2D transfer function to show the bones and soft tissues.<br />I did some more web searching for Delphi optimization and found a really nice site (but with a few links broken). You can find it <a href="http://web.archive.org/web/20021012123720/www.optimalcode.com/opguide.htm">here</a>. I applied the following changes, and the speed-up is significant:<br />1. Changed order of nested 'for' loops that loop over a three-dimensional array.<br />2. Changed FPU precision mode to: SetPrecisionMode(pmSingle);<br />3. Used multiplication instead of division. I multiply by the inverse, and this is much faster.<br />4. Turned off range checking and overflow checking (this is rather obvious, but do it after making sure the algorithm works OK): {$R-}{$Q-}<br />5. Changed sequence of instructions, to group together instructions that use the same variables.<br />6. Changed variables to smaller size (e.g. single instead of double, byte instead of word), where possible.<br />7. In-lined small procedures.<br />Changes that I thought would be beneficial but were not:<br />1. Changing the 'for' variable to anything else than integer slowed the loop down.<br />Changes I would like to make but have not figured out how, yet:<br />1. Substitute 'sqrt' with something faster (even if it is only approximate).<br />2. Same for 'trunc'.<br />3. Get rid of cache misses. This seems to be the major problem (I knew that, of course).<br />It seems that I will not be able to get interactive rates and keep the quality of the image at the same level. So I will probably have to resort to tricks, like drawing at reduced quality when rotating or translating the volume. I could not ask the user to get a faster machine, because I am using a Pentium 4 running at 3 GHz with 1 Gb of RAM. So, this is a fast machine already (for today, at least). Most of the profiling was done by using the QueryPerformanceCounter call. I know that this is not very accurate, but it gives a good estimate if you take the average of a few runs. I have tried VTune but there is a significant learning curve and I did not have much time to go into great depth. Looks promising though.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1108291608567363802005-02-13T12:06:00.000+02:002005-02-13T12:46:48.570+02:00Ray casting without errorsAt last I have managed to complete the ray casting procedure in Delphi and it runs without errors. Now I need to make it much faster. I am using the data set from the Institute for Anthropology, Univeristy of Vienna, as one of my benchmarks. You can download it from <a href="http://www.anthropology.at/virtanth/ct_homosapiens.php">here</a>. The dataset is of a cranium without the mandible (too bad), at two resolutions. I am using the low resolution version (voxel size of 1 mm, total voxels: 218 x 218 x 142). In a window of approximately 470 x 470 pixels, it takes more than 6 seconds to draw. My target is to reduce this to less than a sec. I guess I could do it if I incorporate the IsoRegion idea. However, this particular dataset is rather noisy and the empty space around the cranium is not so empty. In contrast, the CT scan from the Chapel Hill Volume Rendering Test Dataset (<a href="http://www.psychology.nottingham.ac.uk/staff/cr1/render.html">get it here</a>) is much 'smoother' and should benefit from IsoRegion leaping significantly.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1108064896735742652005-02-10T21:24:00.000+02:002005-02-14T20:03:33.470+02:00Chocolates from GenevaYesterday I got an unexpected gift, a small box of chocolates from Geneva. They are Du Rhone. The gesture was very kind and I am grateful. The chocolates were fine but I was not ecstatic. I remember that the best chocolates I have tasted were bought in Geneva on rue du Mont Blanc, two years ago. I will try to find some time and get some more this April, when I will be there. They were fantastic (but the price was also on the same level).<br />Meanwhile, the volume rendering procedure in Delphi is moving along. I have weeded out a few bugs and I think I can have it working in the next few days. Then it will be a matter of optimizing the code heavily. Those who are interested in volume rendering should definitely look into <a href="http://www.cg.tuwien.ac.at/%7Ebruckner/homepage/content/mastersthesis/index.html">Stefan Bruckner's Master's thesis</a>. I got some nice ideas from there, but I am not implementing it exactly as he proposes. I have kept the idea of subdividing the whole volume into bricks but I am not using his rather complex scheme of addressing. Instead, I have decided to duplicate some of the voxels (i.e. have the bricks slightly overlapping) so that calculation of gradients is easier (and hopefully faster). I am also thinking of implementing the idea of IsoRegion leaping, that I read about in a paper by <a href="http://www.cse.cuhk.edu.hk/%7Epffung/">Fung</a> and Heng. A preliminary test showed that I should get a speed-up of a factor of 3 or 4, which is very significant.Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0tag:blogger.com,1999:blog-10703780.post-1107952492885046942005-02-09T14:17:00.000+02:002005-02-09T14:37:44.086+02:00Delphi optimizationI am trying to code a volume renderer, to display computed tomography data. I have been struggling with it for some weeks now and have managed to produce a ray casting procedure, but it is still very slow (I will describe this in another posting later on). Searching the Internet I have come across some rather advanced (for me at least) optimization strategies for Delphi, which may interest some of you. Use Google to look for 'CodingForSpeedInDelphi.doc' by Dennis Christensen. It mentions VTune, a software by Intel that can be used to check for cache misses, cache thrashing and other esoteric stuff. Intel gives the software for a trial period of one month, but be prepared for a very long download time (size is about 200Mb!). I haven't tried it yet, but meantime I have applied some of the advice in the Christensen document (and in a ppt file, also found be Google: singlepe_optimize.ppt). By re-ordering some 'for' loops and substituting divisions by multiplications of the reciprocal, I have managed to shave off approximately 1 full second from the ray casting procedure (which amounts to 10% of the total). Now, if only I could get rid of those annoying Out of Range errors!Demetrishttp://www.blogger.com/profile/04231998373189014531noreply@blogger.com0