<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-707070464272674016</id><updated>2011-11-27T16:13:41.432-08:00</updated><title type='text'>Flatbed Telecine</title><subtitle type='html'>A do-it-yourself method of converting 8mm reel to reel home movies to digital format such as DVD</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>12</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-63064657693365517</id><published>2007-10-25T11:34:00.001-07:00</published><updated>2008-07-14T07:54:04.207-07:00</updated><title type='text'>Welcome</title><content type='html'>&lt;div style="text-align: justify;"&gt;Welcome to my 8mm to DVD conversion story.&lt;br /&gt;&lt;br /&gt;This blog walks you through the process I use to convert old family 8mm and super8 movies to DVD.&lt;br /&gt;&lt;br /&gt;The information on this blog represents a personal investment of over 1000 hours of software and hardware development in addition to another 500 to 1000 hours of running experience. All told, I have scanned over 15,000 feet of film (about 3 miles). Please do not ask for a copy of my code (unless you would like to purchase it). I believe I have described the concepts and processes in sufficient detail for anyone with very basic skills in programing and mathematics to be able to reproduce my process and program on their own (I learned to program by creating this project).&lt;br /&gt;&lt;br /&gt;If the information in this blog is helpful, if you have comments or questions, or if you have modifications or innovations of your own, I would like to hear about it. Please email Kyle at digireel@gmail.com.&lt;br /&gt;&lt;br /&gt;None of what I present here would be possible without the help of the following individuals and their websites:&lt;br /&gt;Richard Kinch &lt;a onclick="return top.js.OpenExtLink(window,event,this)" href="http://www.truetex.com/telecine.htm" target="_blank"&gt;Link&lt;/a&gt;&lt;br /&gt;Todd Campbell&lt;br /&gt;Jim Carroll &lt;a href="http://www.jiminger.com/s8/index.html"&gt;Link&lt;/a&gt;&lt;br /&gt;Jim Carroll's posted Java code was particularly helpful in developing my own version and some of the early steps of my process rely heavily on his code with some routines nearly identical to his. The concepts for the later processes are similar, though the approach and method are quite different.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;&lt;a href="http://digireel.blogspot.com/2007/10/background.html"&gt;Get Started&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;**NOTE** This blog is still under construction. Much of the content is missing. I will update and complete this blog as I have time. Thanks for your patience.&lt;br /&gt;This blog represents my approach to a problem with many solutions. I make no promises inherent or implied with regard to any information contained here. By reading the content of this blog, you agree that under NO circumstances will I be held responsible for any damage to person or property as a result of any action taken by any individual in response to or because of any content of this blog.&lt;br /&gt;&lt;br /&gt;Please also realize that I am not a programmer by profession and so I am sure my approach to programming is riddled with inefficiencies and bad practices - try not to hold it against me.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-63064657693365517?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/63064657693365517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=63064657693365517' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/63064657693365517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/63064657693365517'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/welcome.html' title='Welcome'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-31226528491478025</id><published>2007-10-25T11:33:00.000-07:00</published><updated>2011-06-28T12:16:47.603-07:00</updated><title type='text'>Background</title><content type='html'>&lt;div style="TEXT-ALIGN: justify"&gt;Transferring 8mm and super8 movies to digital format can be done in many ways. I will mention 3 well established methods, but focus only on the last.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="FONT-WEIGHT: bold;font-size:130%;" &gt;Method 1: Camcorder Telecine&lt;/span&gt;&lt;br /&gt;In this method, the film is projected onto a screen and recorded by a camcorder. There are inherent problems with this method such as different frame rates in the two media. The 8mm or super8 film was recorded at 16 or 18 frames per second, respectively, but the camcorder will almost certainly record at 30 frames per second. This leads to flickering in the transfered movie. There are some projectors and camcorders that have been modified specifically to address this issue; however, there is also a loss of quality when the projected image is re-recorded. The loss of quality associated with this method makes this process unsuitable for archiving or preserving original film data.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="FONT-WEIGHT: bold;font-size:130%;" &gt;Method 2: Camera Telecine&lt;/span&gt;&lt;br /&gt;In this method, a &lt;a href="http://www.moviestuff.tv/ez_series.html"&gt;modified projector&lt;/a&gt; has been set up to project or illuminate the original image of the film directly onto a high quality CCD imaging device (Megapixel digital camera without a lense). This method is very fast (up to several frames per second during transfer) and has the advantage of recording each individual frame with less loss of quality (and may even capture the grain of the film). The disadvantages are a high capital investment (starting at $2,495) for the combination projector/camera and the resulting images may be slightly clipped. This method may be suitable for archiving, but it may not capture the full resolution of the film. (I would love more information on this method if anyone has first hand experience - email Kyle at digireel [at] gmail [dot] com).&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: justify"&gt;The only film tansfer company that I feel comfortable refering people to is &lt;a href="http://www.gotmemories.com/"&gt;Got Memories&lt;/a&gt;. Got Memories uses the camera telecine process described here and they are incredibly nice people to boot! I found them very willing to answer questions and work with customers on special projects.&lt;br /&gt;&lt;br /&gt;&lt;span style="FONT-WEIGHT: bold;font-size:130%;" &gt;Method 3: Flatbed Telecine&lt;/span&gt;&lt;br /&gt;This is the method I use and believe to be the best as far as preservation of original film quality. In this method, a device is built to advance a section of the film across a flat bed film scanner. The scanner and the device are controlled by custom software which also identifies and crops each individual frame of the film. The main advantage of this method is greater control over the quality of the final product with scanners capable of 4800 dpi optical resolution and better (enough to capture the grain of the film). Some scanners are quite fast while others are very slow. Scanning the film at these high resolutions can capture the grain of the film (maximum detail possible on film). While nothing can replace films once lost, this method comes as close as possible to preserving all of the original data in a useful form. The main disadvantage of this method is the investment into developing a system (learning curve and actual construction) and the time required to scan the film. Materials will likely cost between $250 and $500 depending on how you choose to implement the system. On a modern Intel 2.4 GHz quad-core processor with 2GB of ram running WindowsXP, this system requires about 1 minute 54 seconds to process 22 frames. That amounts to about 6 hours/50 feet of film. Every 50 feet of film requires about 30 GB of hard drive space during scanning and capturing, so a 400 foot reel will require 240 GB of hard drive space and you will want extra space for editing and processing (probably at least 300 GB total space for a 400 foot reel). After processing, the final product can be 4.5 GB / 50 ft of film (or even less if you don't care about keeping the frame images) which easily fits on a DVD.&lt;br /&gt;&lt;br /&gt;This blog focuses entirely on the Flatbed Telecine process.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="FONT-WEIGHT: bold;font-size:130%;" &gt;A short background in 8mm and super8 film&lt;/span&gt;&lt;br /&gt;8mm format film became available to the public around 1932, though it is rare to find films from earlier than about 1936 or 1937. Color and Black and White film were available almost from the beginning. I have some color films from 1938 that are still vibrant and true to the original hues while my color films from 1937 have almost completely lost their yellow dye (making them very red). Beginning in the 60s, the super8 format was developed and began to gain popularity. 8mm and super8 remained the standard formats until the 80's when camcorders became widely available. For a more detailed history, see &lt;a href="http://www.kodak.com/US/en/motion/s8mm/history.jhtml?id=0.1.4.6.24.6.16&amp;amp;lc=en"&gt;Kodak's 8mm Film History&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Scan from a 1937 8mm home movie:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://1.bp.blogspot.com/_TKVJ6OFqJzk/RyLEELUIHjI/AAAAAAAAADQ/Cpx0-lwavJY/s1600-h/1937+8mm+4800dpi.jpg"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5125874901936774706" border="0" alt="" src="http://1.bp.blogspot.com/_TKVJ6OFqJzk/RyLEELUIHjI/AAAAAAAAADQ/Cpx0-lwavJY/s400/1937+8mm+4800dpi.jpg" /&gt;&lt;/a&gt;If you wish to see the 4800 dpi JPG file (513 KB), email me at digireel@gmail.com&lt;br /&gt;&lt;br /&gt;The specs for 8mm are (image shows vertical orientation):&lt;br /&gt;*NOTE: I did not develop this image or this spec table, but I cannot find the original source webpage. If you know it or find it, I would like to give proper credit to the creator.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RyK_77UIHhI/AAAAAAAAADA/DFZ2w7SFEqE/s1600-h/Regular8.GIF"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5125870362156342802" border="0" alt="" src="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RyK_77UIHhI/AAAAAAAAADA/DFZ2w7SFEqE/s320/Regular8.GIF" /&gt;&lt;/a&gt;&lt;br /&gt;(remember that 8mm is filmed on 16mm film stock and sliced in two, so this image shows 2 strips on a single film before it has been split)&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;DIMENSIONS&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;mm&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;inches&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fw: Film width&lt;/td&gt;&lt;td&gt;15.95&lt;/td&gt;&lt;td&gt;0.628&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PCr: Picture Corner radius&lt;/td&gt;&lt;td&gt;0.25&lt;/td&gt;&lt;td&gt;0.01&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PEs: Picture Edge to Film Edge separation (Max)&lt;/td&gt;&lt;td&gt;2.87&lt;/td&gt;&lt;td&gt;0.113&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ph: Picture height&lt;/td&gt;&lt;br /&gt;&lt;td&gt;3.68&lt;/td&gt;&lt;td&gt;0.145&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PPh: Picture Position horizontal&lt;/td&gt;&lt;td&gt;7.54&lt;/td&gt;&lt;td&gt;0.297&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PPv1: Picture Position vertical-1&lt;/td&gt;&lt;td&gt;0.81&lt;/td&gt;&lt;td&gt;0.032&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PPv2: Picture position vertical-2&lt;/td&gt;&lt;td&gt;0.46&lt;/td&gt;&lt;td&gt;0.018&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pw: Picture width&lt;/td&gt;&lt;td&gt;4.88&lt;/td&gt;&lt;td&gt;0.192&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SEs: Sprocket hole to Picture Edge separation&lt;/td&gt;&lt;td&gt;0.902&lt;/td&gt;&lt;td&gt;0.0355&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sh: Sprocket hole height&lt;/td&gt;&lt;td&gt;1.270&lt;/td&gt;&lt;td&gt;0.05&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sr: Sprocket hole radius&lt;/td&gt;&lt;td&gt;0.25&lt;/td&gt;&lt;td&gt;0.01&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SSh: Sprocket hole Separation horizontal&lt;/td&gt;&lt;td&gt;12.32&lt;/td&gt;&lt;td&gt;0.485&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SSv: Sprocket hole Separation vertical (Long)&lt;/td&gt;&lt;td&gt;3.810&lt;/td&gt;&lt;td&gt;0.15&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sw: Sprocket hole width&lt;/td&gt;&lt;td&gt;1.829&lt;/td&gt;&lt;td&gt;0.0720&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: justify"&gt;&lt;br /&gt;Scan from 1976 super8 home movie:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_TKVJ6OFqJzk/RyLGTLUIHkI/AAAAAAAAADY/zWM0GpR3Kcg/s1600-h/1976+super8+4800dpi.jpg"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5125877358658068034" border="0" alt="" src="http://1.bp.blogspot.com/_TKVJ6OFqJzk/RyLGTLUIHkI/AAAAAAAAADY/zWM0GpR3Kcg/s400/1976+super8+4800dpi.jpg" /&gt;&lt;/a&gt;If you wish to see the 4800 dpi JPG file (576 KB), email me at digireel@gmail.com&lt;br /&gt;&lt;br /&gt;The specs for super8 are (image shows vertical orientation):&lt;br /&gt;*NOTE: I did not develop this image or this spec table, but I cannot find the original source webpage. If you know it or find it, I would like to give proper credit to the creator.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RyLAi7UIHiI/AAAAAAAAADI/pKsd9AvtcZI/s1600-h/Super8.GIF"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5125871032171240994" border="0" alt="" src="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RyLAi7UIHiI/AAAAAAAAADI/pKsd9AvtcZI/s320/Super8.GIF" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;DIMENSIONS&lt;/b&gt;&lt;/td&gt;&lt;td&gt;mm&lt;/td&gt;&lt;td&gt;in&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fw: Film width&lt;/td&gt;&lt;td&gt;7.976&lt;/td&gt;&lt;td&gt;0.3140&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PCr: Picture Corner radius&lt;/td&gt;&lt;td&gt;0.13&lt;/td&gt;&lt;td&gt;0.005&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PEs: Picture Edge to Film Edge separation (max)&lt;/td&gt;&lt;td&gt;1.47&lt;/td&gt;&lt;td&gt;0.058&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ph: Picture Height&lt;/td&gt;&lt;td&gt;4.14&lt;/td&gt;&lt;td&gt;0.163&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PPh: Picture Position horizontal&lt;/td&gt;&lt;td&gt;7.16&lt;/td&gt;&lt;td&gt;0.282&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PPv: Picture Position vertical&lt;/td&gt;&lt;td&gt;9.98&lt;/td&gt;&lt;td&gt;0.393&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pw: Picture width&lt;/td&gt;&lt;td&gt;5.79&lt;/td&gt;&lt;td&gt;0.228&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sh: Sprocket hole height&lt;/td&gt;&lt;td&gt;1.143&lt;/td&gt;&lt;td&gt;0.045&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sr: Sprocket hole radius&lt;/td&gt;&lt;td&gt;0.13&lt;/td&gt;&lt;td&gt;0.005&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ss: Sprocket hole separation (long)&lt;/td&gt;&lt;td&gt;4.234&lt;/td&gt;&lt;td&gt;0.1667&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sw: Sprocket hole width&lt;/td&gt;&lt;td&gt;0.914&lt;/td&gt;&lt;td&gt;0.036&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: justify"&gt;&lt;br /&gt;From the scanned images above, we want to extract the individual frames and string them together into some kind of video format.&lt;br /&gt;&lt;br /&gt;Single frame from 1937 8mm home movie:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RyLPH7UIHmI/AAAAAAAAADo/7GaPEa8a870/s1600-h/00.01.09.jpg"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5125887060989189730" border="0" alt="" src="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RyLPH7UIHmI/AAAAAAAAADo/7GaPEa8a870/s400/00.01.09.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;Single frame from 1976 super8 home movie:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RyLPDbUIHlI/AAAAAAAAADg/WczdU6MTne8/s1600-h/00.00.14.jpg"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5125886983679778386" border="0" alt="" src="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RyLPDbUIHlI/AAAAAAAAADg/WczdU6MTne8/s400/00.00.14.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;Short video clip of 1937 8mm home movie:&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: center"&gt;&lt;br /&gt;&lt;object width="320" height="266" class="BLOG_video_class" id="BLOG_video-2ff58605f9fd7972" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"&gt;&lt;param name="movie" value="http://www.youtube.com/get_player"&gt;&lt;param name="bgcolor" value="#FFFFFF"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="flashvars" value="flvurl=http://v12.nonxt7.googlevideo.com/videoplayback?id%3D2ff58605f9fd7972%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330316523%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D7353FE82CA80F51ABFD32BC6716E4D8C955B04E6.7E77558B103C56868FFC3EB59D14EE1CBB6F28F2%26key%3Dck1&amp;amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3D2ff58605f9fd7972%26offsetms%3D5000%26itag%3Dw160%26sigh%3D0lRTrPZnU3vwPZpQsaDamwoIQY8&amp;amp;autoplay=0&amp;amp;ps=blogger"&gt;&lt;embed src="http://www.youtube.com/get_player" type="application/x-shockwave-flash"width="320" height="266" bgcolor="#FFFFFF"flashvars="flvurl=http://v12.nonxt7.googlevideo.com/videoplayback?id%3D2ff58605f9fd7972%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330316523%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D7353FE82CA80F51ABFD32BC6716E4D8C955B04E6.7E77558B103C56868FFC3EB59D14EE1CBB6F28F2%26key%3Dck1&amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3D2ff58605f9fd7972%26offsetms%3D5000%26itag%3Dw160%26sigh%3D0lRTrPZnU3vwPZpQsaDamwoIQY8&amp;autoplay=0&amp;ps=blogger"allowFullScreen="true" /&gt;&lt;/object&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: justify"&gt;Short video clip of 1976 super8 home movie:&lt;/div&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: center"&gt;&lt;br /&gt;&lt;object width="320" height="266" class="BLOG_video_class" id="BLOG_video-e87d84bcc068c87f" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"&gt;&lt;param name="movie" value="http://www.youtube.com/get_player"&gt;&lt;param name="bgcolor" value="#FFFFFF"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="flashvars" value="flvurl=http://v2.nonxt6.googlevideo.com/videoplayback?id%3De87d84bcc068c87f%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330316523%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D66836AF77008D7C60DB09CA91C8B4F99B59882CA.4F220A11E482FB39D56261499E83F9E772BA3F47%26key%3Dck1&amp;amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3De87d84bcc068c87f%26offsetms%3D5000%26itag%3Dw160%26sigh%3DcXWePhPdyqgvK95s8JdfsD2mJVY&amp;amp;autoplay=0&amp;amp;ps=blogger"&gt;&lt;embed src="http://www.youtube.com/get_player" type="application/x-shockwave-flash"width="320" height="266" bgcolor="#FFFFFF"flashvars="flvurl=http://v2.nonxt6.googlevideo.com/videoplayback?id%3De87d84bcc068c87f%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330316523%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D66836AF77008D7C60DB09CA91C8B4F99B59882CA.4F220A11E482FB39D56261499E83F9E772BA3F47%26key%3Dck1&amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3De87d84bcc068c87f%26offsetms%3D5000%26itag%3Dw160%26sigh%3DcXWePhPdyqgvK95s8JdfsD2mJVY&amp;autoplay=0&amp;ps=blogger"allowFullScreen="true" /&gt;&lt;/object&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: justify"&gt;There is a bit of vertical shifting on the right side of the picture in the 1937 film. I believe this is due to curl in the film that I am not able to eliminate. The second video has a bit of vertical jumping and a repeated frame or two. These were some of the first films I transfered and I had not yet perfected all of my settings. Since these were transfered, I have fixed the vertical jumping and repeated frame issues, but the best I can do for curl is to sandwich the film between 2 panes of glass and try to keep it as flat as possible. Vertical jumping can be eliminated by scanning a smaller segment of film at a time. The videos above were taken from 32 frame scans. I now limit my scans to 25 frames and have no jumping at all. Since the scanning and processing scale linearly with the number of frames, no more time is required to process the film at 25 frames per scan than at 32 frames per scan; however, the scanner is performing a lot more scans at 25 than at 32.&lt;br /&gt;&lt;br /&gt;See the Results section for the best examples of films.&lt;br /&gt;&lt;br /&gt;I have organized my blog into sections to be a step by step guide to building your own flatbed telecine device. I start with building the hardware in the next section and finish with a detailed description of the computer code I wrote.&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size:180%;"&gt;&lt;a style="FONT-WEIGHT: bold" href="http://digireel.blogspot.com/2007/10/hardware.html"&gt;&lt;br /&gt;&lt;/a&gt;&lt;/span&gt;&lt;span style="font-size:180%;"&gt;&lt;a style="FONT-WEIGHT: bold" href="http://digireel.blogspot.com/2007/10/hardware.html"&gt;Go on to Hardware&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-31226528491478025?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='enclosure' type='video/mp4' href='http://www.blogger.com/video-play.mp4?contentId=2ff58605f9fd7972&amp;type=video%2Fmp4' length='0'/><link rel='enclosure' type='video/mp4' href='http://www.blogger.com/video-play.mp4?contentId=e87d84bcc068c87f&amp;type=video%2Fmp4' length='0'/><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/31226528491478025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=31226528491478025' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/31226528491478025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/31226528491478025'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/background.html' title='Background'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_TKVJ6OFqJzk/RyLEELUIHjI/AAAAAAAAADQ/Cpx0-lwavJY/s72-c/1937+8mm+4800dpi.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-8213693501586500276</id><published>2007-10-25T11:32:00.000-07:00</published><updated>2008-12-30T11:26:55.072-08:00</updated><title type='text'>Hardware</title><content type='html'>&lt;div&gt;To perform flatbed telecine, you need:&lt;br /&gt;Computer&lt;br /&gt;Film Scanner&lt;br /&gt;Custom Film Advancing Device&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Computer&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;You will want a computer with a decent processor (P4 and above over 2.0 GHz) and you might consider a multi-core solution (depending on how patient you are - or aren't). You will definitely need a large hard drive (500 GB is nice, but you may want more if you plan on doing multiple projects at a time). The computer must have some kind of communication port. I prefer the ease of using a parallel port; however, someone more clever than I might be able to make use of the USB port for communicating with the Film Advancing Device.&lt;br /&gt;&lt;span style="font-size:180%;"&gt;&lt;br /&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Scanner&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;You need a good film scanner. I started my process using an Epson Perfection 4870 that I got re-manufactured for about $200 in 2006. In May of 2008, that scanner stopped functioning correctly, so I replaced it with an Epson Perfection V500 Photo scanner for $250. The only modifications to my system that I had to make were adjustments to the plywood base that positions my scanner and I had to make a new frame for holding the film in place on the scanner bed. If you choose to use a different scanner, you are on your own for figuring out keystrokes to control the scanning software. The scanner's optical resolution is very important. The purpose of scanning the film is to be able to preserve the original quality even though the film will continue to deteriorate. I recommend a resolution of 4800 dpi, but the V500 has the ability to scan at 6400 dpi optically.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Custom Film Advancing Device&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Here is where the fun begins. The essential components for the device are:&lt;br /&gt;1. Film Sprocket - $20&lt;br /&gt;2. Stepper Motor - $20&lt;br /&gt;3. Drive Chip (&lt;a href="http://www.newark.com/jsp/displayProduct.jsp?sku=89K1130&amp;amp;CMP=KNC-G10000683&amp;amp;HBX_OU=50&amp;amp;HBX_PK=ULN2003A"&gt;ULN2003&lt;/a&gt;) - $0.30&lt;br /&gt;4. Computer interface port - $1&lt;br /&gt;5. Gears - $12&lt;br /&gt;6. Gearbox - $5&lt;br /&gt;Total Cost - $60 (less if you know where to look)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Sprocket&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Old sprockets are found in projectors and film editors. I found film editors to be the easiest, most useful and cheapest source (about $20 on ebay including shipping - cheaper if broken). Movie cameras are NOT a good source because unexposed film has different dimensions. Try to get a dual 8 sprocket that will do both 8mm and super8.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Stepper Motor&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;I suggest &lt;a href="http://www.doc.ic.ac.uk/~ih/doc/stepper/kp4m4/"&gt;KP4M4-001&lt;/a&gt; (found in very old disk drives). I happen to be a student at BYU and our electrical engineering department has a lab with lots of these motors, so they gave me a couple. I don't know where I would find them otherwise.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Drive Chip, Circuit Wiring, &amp;amp; Communication Port&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The &lt;a href="http://www.newark.com/jsp/displayProduct.jsp?sku=89K1130&amp;amp;CMP=KNC-G10000683&amp;amp;HBX_OU=50&amp;amp;HBX_PK=ULN2003A"&gt;ULN2003&lt;/a&gt; is basically 7 logic gates in a single chip. The pins on each side are electrically isolated from each other. When current is passed through a pin on one side, it allows current to flow through the corresponding pin on the other side. In this way, you can use your computer's 5V I/O ports to control a 12V stepper motor. The power for the stepper motor can either come from your computer's 12V supply, or from an external DC power supply. I use 15V power supplies from Radio Shack. The image below is the wiring I use for my circuit and controlling chip for powering the KP4M4 motor. Note that there is a 15V Zener diode between the +12V and pin 9 of the ULN2003 to prevent electrical back flow to the computer as the motor winds down (a very good idea).&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzqBE2h-XHI/AAAAAAAAAEc/sHfZbuPc1ss/s1600-h/Wiring+Diagram.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5132556645699574898" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: pointer; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzqBE2h-XHI/AAAAAAAAAEc/sHfZbuPc1ss/s320/Wiring+Diagram.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I used the &lt;a href="http://computer.howstuffworks.com/parallel-port1.htm"&gt;Parallel Port&lt;/a&gt; because it seemed the easiest and most intuitive. Serial and USB may also be used, but I don't know how to do it. Only pins 2-5 of the parallel port are used in making the device (Pin 2 is the Data 0 Pin in the diagram above).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="FONT-WEIGHT: bold"&gt;BEWARE:&lt;/span&gt;&lt;/span&gt; applying the wrong voltage or incorrectly wiring a circuit connected to your computer's communication ports will likely destroy your computer. If you are not comfortable with the risk of frying your computer, then you need to learn more before playing with this stuff. Anything you choose to do is at your own risk.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Gears &amp;amp; Structure&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;You are on your own for designing and building a gearbox. The two pictures below are different versions I made. One of the boxes is a jewelry box and the other is an inverted tissue box (both are unfinished hardwood) that I got at Joanne's Craft Store and can be found at most any craft store. The gears all came from a &lt;a href="http://www.vexrobotics.com/vex-robotics-gear-kit.shtml"&gt;VEX Gear Kit&lt;/a&gt; that I bought at Radio Shack on clearance. I used plastic/metal cement to attach one of my sprockets to the gear in one of the designs. In the other, I used a Dremmel Tool and friction fit the sprocket onto the gear. The gear on the stepper motors are both friction set. I used 1/8 inch round hard wood dowels for axles. The gears rotate freely on the dowels, but the dowels are glued into place.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_TKVJ6OFqJzk/RzQtL6yZX_I/AAAAAAAAAEU/L_QB_uBO4r8/s1600-h/IMG_1159.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5130775558264283122" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: pointer; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_TKVJ6OFqJzk/RzQtL6yZX_I/AAAAAAAAAEU/L_QB_uBO4r8/s320/IMG_1159.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_TKVJ6OFqJzk/RzQs_6yZX-I/AAAAAAAAAEM/KqaWQGLVaUE/s1600-h/IMG_1143.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5130775352105852898" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: pointer; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_TKVJ6OFqJzk/RzQs_6yZX-I/AAAAAAAAAEM/KqaWQGLVaUE/s320/IMG_1143.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This last gearbox has an automated take-up reel. To turn the reel, I used the take-up arm from an old projector with the friction mechanism still intact. I used a 1/8 inch square metal dowel to connect the large gear on the outside of the box with a smaller gear inside the box. I had to reshape the gear inside the box to fit snugly with one of the other gears from the projector which turns the gears in the take-up arm. The take-up reel was a bit tricky to figure out, but sure beats finding 400 feet of film on the floor each morning.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_TKVJ6OFqJzk/RzwDS5srM7I/AAAAAAAAAEs/mfuFWwBPFRE/s1600-h/Plastic+Guide.jpg"&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="320" height="266" class="BLOG_video_class" id="BLOG_video-3f72c0cc4f2d7d57" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"&gt;&lt;param name="movie" value="http://www.youtube.com/get_player"&gt;&lt;param name="bgcolor" value="#FFFFFF"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="flashvars" value="flvurl=http://v5.nonxt3.googlevideo.com/videoplayback?id%3D3f72c0cc4f2d7d57%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330316523%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D65B29239706F102846823D8288A5DCCAFC05B93D.349B133ED6CBF54B789B698CB0CBB7FF9323EBB3%26key%3Dck1&amp;amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3D3f72c0cc4f2d7d57%26offsetms%3D5000%26itag%3Dw160%26sigh%3DnXRVsa8XpGSCKbNOwdLUSjPD9mE&amp;amp;autoplay=0&amp;amp;ps=blogger"&gt;&lt;embed src="http://www.youtube.com/get_player" type="application/x-shockwave-flash"width="320" height="266" bgcolor="#FFFFFF"flashvars="flvurl=http://v5.nonxt3.googlevideo.com/videoplayback?id%3D3f72c0cc4f2d7d57%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330316523%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D65B29239706F102846823D8288A5DCCAFC05B93D.349B133ED6CBF54B789B698CB0CBB7FF9323EBB3%26key%3Dck1&amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3D3f72c0cc4f2d7d57%26offsetms%3D5000%26itag%3Dw160%26sigh%3DnXRVsa8XpGSCKbNOwdLUSjPD9mE&amp;autoplay=0&amp;ps=blogger"allowFullScreen="true" /&gt;&lt;/object&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: justify"&gt;&lt;br /&gt;I used screws to attach the gear box to a piece of plywood large enough to hold the scanner and gear box. I also built guides on the piece of plywood for the scanner so that it sits in exactly the same position relative to the gear box when I set up to do a scan.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RzwCipsrM6I/AAAAAAAAAEk/Xy9j8dbfQuI/s1600-h/Guide+Assembly.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5132980469627564962" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: pointer; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_TKVJ6OFqJzk/RzwCipsrM6I/AAAAAAAAAEk/Xy9j8dbfQuI/s400/Guide+Assembly.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I built a film guide that sits on the scanner bed to ensure that I get as little rotation and variation in film placement as possible. The guide consists of 2 pieces of plastic and a wooden frame. Turns out that thin CD jewel cases are just about perfect as film guides. Each of the plastic guides is actually two pieces of plastic glued together.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzwHWJsrM8I/AAAAAAAAAE0/lQ_BmNGf6hY/s1600-h/800px-Jewel_case_-_thin.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5132985752437339074" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: pointer; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzwHWJsrM8I/AAAAAAAAAE0/lQ_BmNGf6hY/s400/800px-Jewel_case_-_thin.jpg" border="0" /&gt;&lt;/a&gt;The first is just wide enough to hold the film (the film should move free when the guide is assembled, but the closer the fit, the better). The second piece is cut from the hinge side of the case and is long enough to position the film at the optimal distance from the top of the scanner to get a good scan. Together, the plastic and wood should fit snugly onto the bed of the scanner to prevent as much vertical movement as possible.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_TKVJ6OFqJzk/RzwDS5srM7I/AAAAAAAAAEs/mfuFWwBPFRE/s1600-h/Plastic+Guide.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5132981298556253106" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: pointer; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_TKVJ6OFqJzk/RzwDS5srM7I/AAAAAAAAAEs/mfuFWwBPFRE/s400/Plastic+Guide.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I noticed some Newton rings on very light sections of film as seen in these frames&lt;/p&gt;&lt;p&gt;&lt;img id="BLOGGER_PHOTO_ID_5285663585607238194" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 338px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_TKVJ6OFqJzk/SVpzBsZWQjI/AAAAAAAAAMU/Fgh1rHxf2Gg/s400/Newton+Rings.jpg" border="0" /&gt;&lt;/p&gt;&lt;img id="BLOGGER_PHOTO_ID_5285664758830836978" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 354px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_TKVJ6OFqJzk/SVp0F-_prPI/AAAAAAAAAMk/vRsNRiNONug/s400/Newton+Ring+Zoom.jpg" border="0" /&gt;Although light in these images, Newton rings show up on the video. They can be disipated with Anti-Newton ring glass as seen in this scan of the same frames from above:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;img id="BLOGGER_PHOTO_ID_5285663941327167778" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 329px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_TKVJ6OFqJzk/SVpzWZjloSI/AAAAAAAAAMc/b2aVxvUhaKY/s400/No+Newton+Rings.jpg" border="0" /&gt; &lt;img id="BLOGGER_PHOTO_ID_5285666071915199714" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_TKVJ6OFqJzk/SVp1SanW_OI/AAAAAAAAAMs/irp1mSRseFw/s400/No+Newton+Rings+Zoom.jpg" border="0" /&gt;Originally, I used 2 pieces of 4x6 inch anti-Newton ring glass to help eliminate the color rings. With the new scanner, I use 2 pieces of 2x6 inch anti-Newton ring glass. In both cases, a single pane of glass is 2mm thick. The glass had to be special ordered from &lt;a href="https://linux19.domainnameservers.net/~fpoint5/store/agora.cgi"&gt;here&lt;/a&gt; for $38/pane for the 4x6 or $20/pane for the 2x6 (just ask them for a quote on the size and type of glass you want). One piece of glass sits directly on the scanner bed ("fuzzy" side up) and the film goes over it. The other piece goes on top of the film ("fuzzy" side down) to sandwich the film between the coated sides of the glass. The weight of the glass also helps eliminate curl in the film. Both panes of glass should have completely smooth edges on all sides to avoid scratching the film. I have not noticed any scratching on the film as a result of the glass itself, but any hard particle sandwiched between the glass will scratch film moving past it. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;&lt;a style="FONT-WEIGHT: bold" href="http://digireel.blogspot.com/2007/10/computer-io.html"&gt;Go on to Computer I/O&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-8213693501586500276?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='enclosure' type='video/mp4' href='http://www.blogger.com/video-play.mp4?contentId=3f72c0cc4f2d7d57&amp;type=video%2Fmp4' length='0'/><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/8213693501586500276/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=8213693501586500276' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8213693501586500276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8213693501586500276'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/hardware.html' title='Hardware'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzqBE2h-XHI/AAAAAAAAAEc/sHfZbuPc1ss/s72-c/Wiring+Diagram.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-4769175003529688834</id><published>2007-10-25T11:31:00.000-07:00</published><updated>2008-06-01T14:46:43.279-07:00</updated><title type='text'>Computer I/O</title><content type='html'>&lt;div style="text-align: justify;"&gt;This section is divided into 2 parts:&lt;br /&gt;1. Interfacing with the scanning software to initiate scans&lt;br /&gt;2. Controlling the stepper motor&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Scanning Software Interface&lt;/span&gt;&lt;br /&gt;I chose the simplest method for controlling the scanner; I simply pass key strokes to the scanning software (EpsonScan in professional mode) in order to initiate a scan. My program remains in a loop that searches for the expected file to appear in the expected folder on the hard drive during the scan. When the program registers that the file exists, it continues on with the processing of the file. The scanning software is started from my code, but it must be configured manually with the correct settings before the film can be scanned automatically. This means setting the destination folder for the scanned images and resetting the scan counter and image name. I use the image name "scan###" and I always scan in 24 bit BMP format (### indicates the scanning software counter).&lt;br /&gt;&lt;br /&gt;In Visual Basic.net (Visual Studio 2005 was available to everyone for download from the Microsoft website), the scanning program is initially started from within my program and then the following code brings the scanning software to the foreground and passes keystrokes to initiate a scan:&lt;br /&gt;&lt;br /&gt;AppActivate("EPSON Perfection 4870")&lt;br /&gt;System.Windows.Forms.SendKeys.SendWait("{TAB}")&lt;br /&gt;System.Windows.Forms.SendKeys.SendWait("s")&lt;br /&gt;&lt;br /&gt;These commands perform the following actions (in order):&lt;br /&gt;1. Make Epson Perfection 4870 the foreground application&lt;br /&gt;2. Send the keystroke &lt;tab&gt; to the foreground application (prepares the scanning software to receive hotkey commands)&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;3. Send the keystroke "s" to the foreground application (starts a scan using the "s" hotkey)&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;If you are working on your computer &lt;/tab&gt;(like typing this blog) &lt;tab&gt;at the same time that the program is running (as it is right now for me), you may get the timing just right so that when the program calls the scanning software to the foreground, you just happen to click on a link to edit your blog so that the blog comes to the foreground instead and you may find a random "s" in your text. The result is a poorly edited blog (sorry) and your program will continue to wait for the next scan which was never actually started. Continuing the program means that you have to push the "scan" button in the software. It should pick up automatically again from there. This is the hazard of using the key-stroke solution to the scanner interface problem.&lt;br /&gt;&lt;br /&gt;In my code, the 1st and 3rd command are used every time a scan is initiated; however, the 2nd command is used only before the first scan. In addition, the code waits at least 0.5 seconds between sending consecutive commands as keystrokes (otherwise, the process executes the keystrokes faster than the program is able to respond resulting in missed commands).&lt;br /&gt;&lt;br /&gt;&lt;/tab&gt;&lt;tab&gt;&lt;/tab&gt;After the 999th image is scanned&lt;tab&gt;, the Epsonscan software will not allow any further scanning until till the counter is reset to 001. As I do not want to overwrite any scans, I use the following code to change the scanned image name and image counter in the scanner settings:&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;         If scans = 1000 Then&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             AppActivate("EPSON Perfection 4870")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             Do Until DateTime.Now &gt;= estart.AddSeconds(0.5)&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;                 Application.DoEvents()&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             Loop&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;                System.Windows.Forms.SendKeys.SendWait("{TAB}")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait(" ")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("{DOWN}")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("{ENTER}")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("{TAB}")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("{RIGHT}")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("1")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("{TAB}")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("1")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             System.Windows.Forms.SendKeys.SendWait("{ENTER}")&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;             scans += 1&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;         End If&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;This code assumes that the last command given to the scanning software was "Scan." For this case in EpsonScan, &lt;/tab&gt;"{TAB}" &lt;tab&gt;&lt;tab&gt;highlights the scanner settings button, &lt;/tab&gt;&lt;/tab&gt;" " (space)&lt;tab&gt;&lt;tab&gt;&lt;space&gt; accesses the menu, "&lt;down&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;{DOWN}" &lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;selects the correct option, "&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;{ENTER}"&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt; opens the change settings dialogue, &lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;"{TAB}"&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt; &lt;tab&gt; highlights the scanned image name,&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt; "{RIGHT}"&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt; &lt;right&gt; moves the cursor to the far right of the name, "1" adds a 1 to the name, &lt;tab&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;"{TAB}"&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt; highlights the image counter, "1" resets the counter to 001, and&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt; "{ENTER}"&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt; &lt;enter&gt; accepts the changes in the settings and closes the dialogue. In this way, the scanner automatically jumps from the image name "scan999.bmp" to "scan1001.bmp" and the scanning and processing continue as normal.&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Stepper Motor Control&lt;/span&gt;&lt;span style="font-size:130%;"&gt; &lt;/span&gt;&lt;br /&gt;Commands to the stepper motor are easily given through the &lt;a href="http://computer.howstuffworks.com/parallel-port1.htm"&gt;parallel port&lt;/a&gt;. The parallel port is a 25-pin communications port. The 1st pin signals when the computer is transmitting data. Pins 2-9 are data output pins (from computer to device), pins 10-17 are input pins (from device to computer), and pins 18-25 are grounding pins. Only pins 2-5 and possibly pin 25 are used for controlling the stepper motor.&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;Data is transfered through the parallel port in 1 byte (8 bit) increments (1 number at a time). Each of pins 2-9 represent 1 bit. An 8 bit communication system allows for the numbers 0-255 sent in binary. In binary, the possible numbers in each digit are 0 or 1 only.&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;0000 0001 = 1*2^0 = 1&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;0000 1001  = 1*2^3 + 1*2^0 = 9&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;0001 1001 = 1*2^4 + 1*2^3 + 1*2^0 = 25&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;1001 1001 = 1*2^7 + 1*2^3 + 1*2^0 = 153&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;The computer cannot actually read or communicate numbers. Instead, it uses a 5 volt signal and measures the voltage as "on" (5V) or "off" (0V), corresponding to "1" or "0" respectively. Pin 2 corresponds to the "one's" place (2^0, the far right digit in the binary numbers above), pin 3 corresponds to the "two's" place (2^1, the second digit from the right in the example above), and so on until pin 9, which corresponds to the "128's" place (2^7, the far left digit in the example above).&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;The &lt;a href="http://www.doc.ic.ac.uk/%7Eih/doc/stepper/kp4m4/"&gt;KP4M4&lt;/a&gt; stepper motor has 4 electromagnetic coils. Control of the stepper motor is very simple. When a voltage (and current) is applied to its electromagnetic coils, the permanent magnet on its rotor aligns with magnetic field. If the coils are magnetized successively in the correct order, the stepper motor is made to turn. The motor will turn 3.6 degrees per coil excitation, or 100 excitations per revolution. With the KP4M4 the correct sequence of coil excitations is simply 1, 2, 3, 4.&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzqBE2h-XHI/AAAAAAAAAEc/sHfZbuPc1ss/s320/Wiring+Diagram.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px;" src="http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzqBE2h-XHI/AAAAAAAAAEc/sHfZbuPc1ss/s320/Wiring+Diagram.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;We need to send a series of numbers through the parallel port that correspond to turning on the motor coils in the correct sequence to get the rotor to spin. The simplest method is to excite 1 coil at a time, but if 2 coils are excited at the same time, we can increase torque from the motor as the magnetic fields will be stronger. In this case, we excite coils 1&amp;amp;4, 4&amp;amp;3, 3&amp;amp;2, and then 2&amp;amp;1. There is even a possible sequence of exciting 3 coils at a time in a similar fashion, but I found double coil excitation to be enough. The simplest way to do this is to send the following sequence of bytes:&lt;br /&gt;&lt;br /&gt;0000 1001&lt;br /&gt;0000 1100&lt;br /&gt;0000 0110&lt;br /&gt;0000 0011&lt;br /&gt;&lt;br /&gt;These correspond to the numbers 9, 12, 6, and 3 (or in hexadecimal notation, 9, c, 6, 3). Since the leading 4 digits of each number are zero and those digits correspond to pins 6-9, we do not need to wire pins 6-9 to anything. When current from the 5V parallel port pin flows through the ULN2003 chip, the chip allows current to flow through the corresponding 12V (or 15V in my case) pin to the motor coils.&lt;br /&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;Windows XP makes it difficult to send signals through the parallel port (if you wish to use USB, you will have different challenges and I have no information on the subject). A driver (&lt;a href="http://logix4u.net/Legacy_Ports/Parallel_Port/Inpout32.dll_for_Windows_98/2000/NT/XP.html"&gt;inpout32.dll&lt;/a&gt;) specifically designed to fool XP into thinking that your commands are native XP commands is required. Inpout32.dll needs only to be downloaded, unzipped, and copied to the Windows\system32\ folder to function correctly. The driver must be declared as a module in Visual Basic.net in the following manner:&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;Module InpOut32_declarations&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt; Public Declare Function Inp Lib "inpout32.dll" Alias "Inp32" _&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;     (ByVal PortAddress As Short) As Short&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt; Public Declare Sub Out Lib "inpout32.dll" Alias "Out32" _&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;     (ByVal PortAddress As Short, ByVal value As Short)&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;End Module&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;To send a number to the parallel port using inpout32, the number must be in hexadecimal (base 16) notation. The commands to send numbers through the parallel port (and hence activate the stepper motor) are:&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;Out(LPT, &amp;amp;H0S) 'Print '0' to D7-D0&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;a = &amp;amp;H9S&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;b = &amp;amp;HCS&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;c = &amp;amp;H6S&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;d = &amp;amp;H3S&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;For i = 0 To Steps&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;If i Mod 4 = 0 Then&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;              Out(LPT, a)&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;          ElseIf i Mod 4 = 1 Then&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;              Out(LPT, b)&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;          ElseIf i Mod 4 = 2 Then&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;              Out(LPT, c)&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;          ElseIf i Mod 4 = 3 Then&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;              Out(LPT, d)&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;          End If&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;          j = DateTime.Now&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;          Do Until Delay &gt;= j.AddSeconds(dtime)&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;              Delay = DateTime.Now&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;          Loop&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;      Next i&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;      Out(LPT, &amp;amp;H0S)&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;I like to begin and end by turning off all of the motor coils (sending a "0" to the port) to be sure the motor rests between film advances. If the coils are in a constant state of excitement, the motor will wear out and burn out faster. In fact, the first thing my program does when it starts up is to send a "0" to the parallel port to be sure that random voltages in the port (all pin voltages are random until set to a specific value) do not keep the motor constantly excited. The bit of code above says that the computer will successively activate the coils of the motor until a certain number of steps has been achieved where 1 step = 3.6 degrees. Since my processor crunches through these numbers far faster than my port and motor can respond, I built a delay into the loop so that the code sends the number and holds it for a specified amount of time (0.01 seconds) in order to be sure that the motor and port respond to each step.&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;a href="http://digireel.blogspot.com/2007/10/code.html"&gt;&lt;span style="font-size:180%;"&gt;&lt;span style="font-weight: bold;"&gt;Go on to the VB Code&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;br /&gt;&lt;tab&gt;&lt;tab&gt;&lt;space&gt;&lt;down&gt;&lt;enter&gt;&lt;tab&gt;&lt;right&gt;&lt;tab&gt;&lt;enter&gt;&lt;/enter&gt;&lt;/tab&gt;&lt;/right&gt;&lt;/tab&gt;&lt;/enter&gt;&lt;/down&gt;&lt;/space&gt;&lt;/tab&gt;&lt;/tab&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-4769175003529688834?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/4769175003529688834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=4769175003529688834' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/4769175003529688834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/4769175003529688834'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/computer-io.html' title='Computer I/O'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_TKVJ6OFqJzk/RzqBE2h-XHI/AAAAAAAAAEc/sHfZbuPc1ss/s72-c/Wiring+Diagram.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-7062004453528672363</id><published>2007-10-25T11:30:00.000-07:00</published><updated>2009-11-24T12:06:47.405-08:00</updated><title type='text'>VB Code</title><content type='html'>The process of converting scanned film images to individual frames and avi movie files is not too complicated. The math and logic behind the methods are quite simple. Basically, you have to identify the sprockets, pick off the images, and compile them into a movie file. Identifying the sprockets sounds easy, but actually makes up about 90% of processing time and program code.&lt;br /&gt;&lt;br /&gt;My program is built around a graphical user interface (GUI) to allow the user to be able to use the same code for many different operations including scanning and processing film, reprocessing a set of previously scanned film images, continuing a project that was interrupted before completion, or compiling a movie file from previously captured frames of a film.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_TKVJ6OFqJzk/R6YouUY7BUI/AAAAAAAAAFY/XgjQsk7xysw/s1600-h/telecine.jpg"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5162858799039186242" border="0" alt="" src="http://1.bp.blogspot.com/_TKVJ6OFqJzk/R6YouUY7BUI/AAAAAAAAAFY/XgjQsk7xysw/s400/telecine.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;The user may choose regular 8, super 8, frame rate, motor delay (time to wait between sending signals to the motor), automatic or manual calibration of sprocket size and position, and whether to create debugging images.&lt;br /&gt;&lt;br /&gt;The main program simply keeps track of the number of scanned images and the number of frames that have been picked off and orchestrates the execution of the routines that advance film, initiate a scan, process the scan, and create the movie file.&lt;br /&gt;&lt;br /&gt;In general, the program executes the following suedo-code:&lt;br /&gt;&lt;br /&gt;Initialize parameters (depending on user choices)&lt;br /&gt;Check to see if Scanning software is ready&lt;br /&gt;- Not ready? Tell user and stop execution&lt;br /&gt;Ask User for working directory&lt;br /&gt;Create output directory&lt;br /&gt;Number of scans = 0&lt;br /&gt;Number of frames = 0&lt;br /&gt;Main program loop&lt;br /&gt;- Add 1 to total number of scans&lt;br /&gt;- Check to see if there are 999 scanned images&lt;br /&gt;- - Have 999 scanned images? Change file name &amp;amp; reset counter on Epsonscan software&lt;br /&gt;- Predict filename of next scanned image&lt;br /&gt;- Start Scan&lt;br /&gt;- Scanning Loop&lt;br /&gt;- - Check to see if expected file exists&lt;br /&gt;- - - File Exists? Exit Loop&lt;br /&gt;- End Scanning Loop&lt;br /&gt;- Create grayscale Image&lt;br /&gt;- Find edges&lt;br /&gt;- Find Sprockets&lt;br /&gt;- Pick off frames&lt;br /&gt;- - Add frames picked off to total number of frames&lt;br /&gt;- Advance Film&lt;br /&gt;End Main Program Loop&lt;br /&gt;Advance end of film through machine&lt;br /&gt;Create movie file(s)&lt;br /&gt;End Program&lt;br /&gt;&lt;br /&gt;The next sections cover the algorithms for finding edges and sprockets in more detail.&lt;br /&gt;&lt;br /&gt;A lot of time can be saved if the scanned images of the film are straight, consistent, and have little extra space above and below the film. In general, the top and bottom edge of the scanned film image should come as close to the film edge as possible. The edges of the film may even be cut off slightly, but cut too much off and the program will be unable to accurately find the sprockets or the frames.&lt;br /&gt;&lt;br /&gt;In finding the sprockets, the full image is not needed. Only the top 1/3 - 1/2 of the image is required because once the position of the sprockets is known from the smaller, working image, the frames can be taken directly from the original image. Performing the sprocket finding algorithms on only the top third of the image reduces processing time by 75%!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digireel.blogspot.com/2008/02/bitmap-tutorial.html"&gt;&lt;span style="font-size:180%;"&gt;Go on to Bitmap Tutorial&lt;/span&gt;&lt;br /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-7062004453528672363?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/7062004453528672363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=7062004453528672363' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/7062004453528672363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/7062004453528672363'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/code.html' title='VB Code'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_TKVJ6OFqJzk/R6YouUY7BUI/AAAAAAAAAFY/XgjQsk7xysw/s72-c/telecine.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-8603362597073166668</id><published>2007-10-25T11:29:00.001-07:00</published><updated>2008-06-01T15:01:19.437-07:00</updated><title type='text'>Bitmap Tutorial</title><content type='html'>Before you can understand how to manipulate images, you have to know how the computer sees them. For a bitmap image, the computer actually sees a HUGE series of numbers strung together in a single long list. Each number is 1 byte (8 bits, or 8 1's and 0's) between 0 and 255.&lt;br /&gt;&lt;br /&gt;The first 40 bytes tell the computer how many bits per pixel, the number of pixels high and wide the image is, the physical dimensions of the original image (in inches), the compression format used (if any), etc.&lt;br /&gt;&lt;br /&gt;The computer creates and stores images by defining a dot of color called a pixel. The images we see on a screen are created by mixing intensities of 3 colors for each pixel. The most common pixel format is 24 bit. 24 bit color allows for 256 different levels of intensity for each of the three colors that are combined to make every other color on the screen. As each combination of different levels of intensity results in a different color, 24 bit pixels can have up to 256 x 256 x 256 = 16,777,216 different colors. The three colors used by computers are blue, green, and red. To define a pixel, all that is needed is the relative intensity of the three colors. For example, the following sets of numbers define pixels of the corresponding color:&lt;br /&gt;&lt;br /&gt;[0 0 0] = Black&lt;br /&gt;[255 0 0] = Blue&lt;br /&gt;[0 255 0] = Green&lt;br /&gt;[0 0 255] = Red&lt;br /&gt;[255 255 255] = White&lt;br /&gt;&lt;br /&gt;When you and I think of a picture, we think of a 2 dimensional matrix of color dots, but the computer sees a very large list of numbers in only 1 dimension. The computer knows when one line of pixels stops and the next one starts based on the data in the first 40 bytes of the image. In order to correctly manipulate the image, the total number of pixels, the number of pixels in each row, and the number of rows must be known. In addition, the number of bytes in a row of the image must be an integer multiple of 4. If the number of pixels in a row does not give a number bytes that is an integer multiple of 4, the image file will have columns of "remainder" bytes at the end of the rows to meet this requirement. The remainder has a value from 0 to 3, is constant for each image, and represents the number of these "dummy" columns. The full length of the row in bytes including the remainder is called the "stride."&lt;br /&gt;&lt;br /&gt;In Visual Basic, if the commands "Imports system.drawing.imaging" and "Imports system.runtime.interopservices" are included before any of the actual program code, then image as well as the important information can be retrieved as follows:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;Dim bmd As BitmapData&lt;br /&gt;Dim bm As Bitmap&lt;br /&gt;Dim bmdat() as byte&lt;br /&gt;bmd = bm.LockBits(New Rectangle(0, 0, bm.Width, bm.Height),&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;Imaging.ImageLockMode.ReadWrite,&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;Imaging.PixelFormat.Format24bppRgb)&lt;/span&gt;&lt;br /&gt;w = bmd.Width   'width of image in pixels&lt;br /&gt;h = bmd.Height   'height of image in pixels&lt;br /&gt;bmsize = w*h      'total size of image in pixels&lt;br /&gt;ReDim bmdat(bmd.Stride * h - 1)&lt;br /&gt;Marshal.Copy(bmd.Scan0, bmdat, 0, bmd.Stride * bmd.Height)&lt;br /&gt;r = bmd.Stride - w * 3   'remainder in bytes&lt;br /&gt;&lt;br /&gt;The code above copies the full image data in bytes into the 1 dimensional matrix "bmdat." Here, w is width in pixels, h is height in pixels, and r is the remainder in bytes.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digireel.blogspot.com/2007/10/grayscale.html"&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;Go on to Grayscale&lt;/span&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-8603362597073166668?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/8603362597073166668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=8603362597073166668' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8603362597073166668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8603362597073166668'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2008/02/bitmap-tutorial.html' title='Bitmap Tutorial'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-8325416006481667984</id><published>2007-10-25T11:29:00.000-07:00</published><updated>2008-06-01T15:11:08.106-07:00</updated><title type='text'>Grayscale</title><content type='html'>The algorithms that find edges in the image require the image to be in grayscale.&lt;br /&gt;&lt;br /&gt;In the Bitmap tutorial, example code was provided to capture the full pixel data of the original image. In finding the sprockets, only about the top 1/3 of the image will be used. The first step is to convert the top 1/3 of the full image data to grayscale data. This will not affect the original image, but has lots of advantages:&lt;br /&gt;&lt;br /&gt;1. Simplicity: Each pixel can be represented by a single byte&lt;br /&gt;2. Memory: Required storage space in memory will be less&lt;br /&gt;3. Processor Time: Fewer numbers to crunch = less core time&lt;br /&gt;&lt;br /&gt;I keep saying that we only need about the top 1/3 of the image, but to be exact, I take the frame width dimension (given in the &lt;a href="http://digireel.blogspot.com/2007/10/background.html"&gt;background&lt;/a&gt; section), convert it from inches to pixels, and subtract it from the total height of the scanned image in pixels. The remaining image is all that is required until we are ready to pick off the actual frames.&lt;br /&gt;&lt;br /&gt;Many algorithms for converting full color images to grayscale have been developed. I found one on the internet that works well, but I have lost the source. If you happen to know the source, please email me so that I may update this page (digireel@gmail.com). The algorithm I use is:&lt;br /&gt;&lt;br /&gt;gray = 0.2 * blue + 0.5 * green + 0.3 * red&lt;br /&gt;&lt;br /&gt;For 24 bit color, gray will end up as an integer from 0 to 255.&lt;br /&gt;For example, a pixel with BGR color [125 200 50] would be:&lt;br /&gt;0.2 * 125 + 0.5 * 200 + 0.3 * 50 = 140&lt;br /&gt;&lt;br /&gt;When truncating the image and converting it to grayscale, the remainder must not be neglected (see the &lt;a href="http://digireel.blogspot.com/2008/02/bitmap-tutorial.html"&gt;bitmap tutorial&lt;/a&gt; for discussion on the remainder). Forgetting to account for the remainder bytes at the end of each row may result in a final image that is slanted as much as 45 degrees to the left and that exhibits color shift. After the image is converted to grayscale, the remaining processes can be done without having to worry about the remainder until it is time to pick off the frames from the original image, or until a debugging image is to be created.&lt;br /&gt;&lt;br /&gt;With the truncated image converted to grayscale, the data is ready for edge detection.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;&lt;a href="http://digireel.blogspot.com/2007/10/edge-image.html"&gt;Go on to Canny Edge Detection&lt;br /&gt;&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-8325416006481667984?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/8325416006481667984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=8325416006481667984' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8325416006481667984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8325416006481667984'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/grayscale.html' title='Grayscale'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-1314472261391238684</id><published>2007-10-25T11:28:00.000-07:00</published><updated>2008-06-01T15:16:39.441-07:00</updated><title type='text'>Canny Edge Detection</title><content type='html'>The process of finding edges in the image is accomplished by using the Canny Edge Detection method. This method is well documented on the internet and an excellent tutorial has been written by &lt;a href="http://www.pages.drexel.edu/%7Eweg22/can_tut.html"&gt;Bill Green&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The three steps to a Canny Edge Detection are:&lt;br /&gt;1. Blur the image&lt;br /&gt;2. Calculate image gradients&lt;br /&gt;3. Create a hysteresis&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Blurring&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The image is blurred using a Gaussian Mask in order to filter out noise such as dust particles and faint lines (which we are not interested in). A normal probability function in 2 dimensions is used to calculate the Mask. The mask is applied to every pixel of the image for which the edges of the mask still fall on pixels (i.e. there will be some space on the edges that the mask will not be applied to). The following equation calculates the normal probability function used in creating the mask:&lt;br /&gt;&lt;br /&gt;p = exp(-(x^2 + y^2)/(2 * sigma^2)) / (2 * Pi * sigma^2)&lt;br /&gt;&lt;br /&gt;The mask is created in the following code:&lt;br /&gt;&lt;br /&gt;win = 1 + 2 * Ceiling(3 * sigma)&lt;span style="color: rgb(0, 0, 0);"&gt;&lt;/span&gt;&lt;br /&gt;center = Floor(win / 2)&lt;br /&gt;ReDim gk(win - 1, win - 1)&lt;br /&gt;xsum = 0&lt;br /&gt;For i = 0 To win - 1&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;For j = 0 To win - 1&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;b = i - center&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;d = j - center&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;fx = Exp(-0.5 * (b ^ 2 + d ^ 2) / (sigma * sigma)) / _&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;**&lt;/span&gt;(sigma * sigma * 2 * pi)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;gk(i, j) = fx&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;xsum = xsum + fx&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;Next j&lt;br /&gt;Next i&lt;br /&gt;For i = 0 To win - 1&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;For j = 0 To win - 1&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;gk(i, j) = gk(i, j) / xsum&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;Next j&lt;br /&gt;Next i&lt;br /&gt;&lt;br /&gt;Sigma is generally a user input. Good values of sigma range from 0.5 to 1.4. The larger the sigma, the greater the blurring, but the time required to apply the mask increases exponentially. I recommend a value of 1.0 as it seems to give adequate blurring and can be done in little time.&lt;br /&gt;&lt;br /&gt;The mask is applied in the following code (note: idat() is the array containing the grayscale pixels values where each pixel is represented by a single byte):&lt;br /&gt;&lt;br /&gt;For i = 0 To numberofpixels - 1&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;xdot = 0.0&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;xsum = 0.0&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;For cc = -center To center&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;For j = -center To center&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;If (((i + row) Mod row) + cc) &gt;= 0 And _&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;**&lt;/span&gt;(((i + row) Mod row) + cc) _&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;**&lt;/span&gt;And (i + j * row) &gt; 0 And (i + j * row)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;xdot = xdot + idat(i + cc + j * row) * _&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;**&lt;/span&gt;gk(center + cc, center + j)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;xsum = xsum + gk(center + cc, center + j)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;End If&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;Next j&lt;span style="color: rgb(0, 0, 0);"&gt;&lt;br /&gt;*****&lt;/span&gt;Next cc&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;gdat(i) = (xdot / xsum) * boostblurfactor + 0.5&lt;br /&gt;Next i&lt;br /&gt;For i = 1 To numberofpixels - 1&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;idat(i) = gdat(i)&lt;br /&gt;Next i&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;The boostblurfactor in the code above is just a constant for changing the amount of blur effect. I use a value of 0.9 because that is the value that was used in the source code I modeled my code &lt;/span&gt;&lt;span style="font-size:100%;"&gt;from. The constant "row" is equal to the number of pixels in each row of the original image.&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;span&gt;Gradients&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;To find the edges, it is not enough to know where the image is intensely dark or light, but rather to know where there is a very sudden change from dark to light (or light to dark). The gradient is a measure of the amount of change around each pixel. The following code calculates the gradients at each pixel (note that derx() and dery() are arrays that hold the values of the gradients in those directions and the magnitude() array holds the combined gradient for the pixel):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;For i = 0 To numberofpixels - 1&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;'Compute x derivative&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;If i = 0 Or (i Mod row) = 0 Then&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;c = idat(i + 1)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;d = idat(i)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;delx(i) = c - d&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;ElseIf ((i + 1) Mod row) = 0 Then&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;c = idat(i)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;d = idat(i - 1)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;delx(i) = c - d&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;Else&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;c = idat(i + 1)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;d = idat(i - 1)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;delx(i) = c - d&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;End If&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;'Compute y derivative&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);font-size:100%;" &gt;*****&lt;/span&gt;&lt;span style="font-size:100%;"&gt;If i &lt;&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;c = idat(i + row)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;d = idat(i)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;dely(i) = c - d&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;ElseIf i &gt; numberofpixels - row - 1 Then&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;c = idat(i)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;d = idat(i - row)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;dely(i) = c - d&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;Else&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;c = idat(i + row)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;d = idat(i - row)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;dely(i) = c - d&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;End If&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;If i Mod (467 * row) = 0 Then Application.DoEvents()&lt;br /&gt;Next i&lt;br /&gt;&lt;br /&gt;'Calculate the magnitude of the gradient&lt;br /&gt;For i = 0 To numberofpixels - 1&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;sq1 = delx(i) * delx(i)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;sq2 = dely(i) * dely(i)&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;magnitude(i) = 0.5 + Sqrt(sq1 + sq2)&lt;br /&gt;&lt;span&gt;Next i&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;After calculating the gradient,  we need to suppress any pixel that is not a maximum. We will use three values to indicate pixel status as:&lt;/span&gt;&lt;br /&gt;&lt;span&gt;1. No Edge (0)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;2. Possible Edge (128)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;3. Definite Edge (255)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;The pixels around the edges of the image will be set to No Edge. For the remaining pixels, a simple test shows whether the pixel in question is a possible edge or not, but the eight different directions from which the gradient may be coming create a complicated set of conditions for the test.  The code is:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;        Dim i As Integer&lt;/span&gt;&lt;br /&gt;&lt;span&gt;        Dim xperp, yperp, z1, z2, mag1, mag2 As Double&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;        'Initialize parameters&lt;/span&gt;&lt;br /&gt;&lt;span&gt;        ReDim idat(bmsize - 1) 'idat() is now the result array&lt;/span&gt;&lt;br /&gt;&lt;span&gt;        noedge = 0&lt;/span&gt;&lt;br /&gt;&lt;span&gt;        possedge = 128&lt;/span&gt;&lt;br /&gt;&lt;span&gt;        edge = 255&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;        'Zero the Result() Image Edges&lt;/span&gt;&lt;br /&gt;&lt;span&gt;        For i = 0 To bmsize - 1&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            If i &lt;&gt; (bmsize - row - 1) Or (i Mod row) = 0 _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Or ((i + 1) Mod row) = 0 Or magnitude(i) = 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                idat(i) = noedge&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            Else&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                xperp = -delx(i) / magnitude(i)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                yperp = dely(i) / magnitude(i)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            End If&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            If delx(i) &gt;= 0 And dely(i) &gt;= 0 And delx(i) &gt;= dely(i) _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And i &gt; row And i &lt; (bmsize - row - 1) And (i Mod row) &lt;&gt; 0 _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Left Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag1 = (magnitude(i) - z1) * xperp + (z2 - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i + row + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (magnitude(i) - z1) * xperp + (z2 - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf delx(i) &gt;= 0 And dely(i) &gt;= 0 And delx(i) &lt;&gt; row _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And i &lt; (bmsize - row - 1) And (i Mod row) &lt;&gt; 0 _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Left Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - row)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag1 = (z1 - z2) * xperp + (z1 - magnitude(i)) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i + row)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i + row + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (z1 - z2) * xperp + (z1 - magnitude(i)) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf delx(i) &gt;= 0 And dely(i) &lt;&gt;= -dely(i) And i &gt; row _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And i &lt; (bmsize - row - 1) And (i Mod row) &lt;&gt; 0 _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Left Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i + row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag1 = (magnitude(i) - z1) * xperp + (z1 - z2) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (magnitude(i) - z1) * xperp + (z1 - z2) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf delx(i) &gt;= 0 And dely(i) &lt;&gt; row And i &lt; (bmsize - row - 1) _ &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And (i Mod row) &lt;&gt; 0 And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;'Left Point&lt;/span&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;z1 = magnitude(i + row)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;z2 = magnitude(i + row - 1)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;mag1 = (z1 - z2) * xperp + (magnitude(i) - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - row)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (z1 - z2) * xperp + (magnitude(i) - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf delx(i) &lt;&gt;= 0 And -delx(i) &gt;= dely(i) And i &gt; row _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And i &lt; (bmsize - row - 1) And (i Mod row) &lt;&gt; 0 _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Left Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag1 = (z1 - magnitude(i)) * xperp + (z2 - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i + row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (z1 - magnitude(i)) * xperp + (z2 - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf delx(i) &lt;&gt;= 0 And -delx(i) &lt;&gt; row And i &lt; (bmsize - row - 1) &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And (i Mod row) &lt;&gt; 0 And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Left Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - row)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag1 = (z2 - z1) * xperp + (z1 - magnitude(i)) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;***** &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i + row)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i + row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (z2 - z1) * xperp + (z1 - magnitude(i)) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf delx(i) &lt;&gt;= -dely(i) And i &gt; row And i &lt; (bmsize - row - 1) _ &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And (i Mod row) &lt;&gt; 0 And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt; 'Left Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i + row + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag1 = (z1 - magnitude(i)) * xperp + (z1 - z2) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (z1 - magnitude(i)) * xperp + (z1 - z2) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf i &gt; row And i &lt; (bmsize - row - 1) And (i Mod row) &lt;&gt; 0 _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And ((i + 1) Mod row) &lt;&gt; 0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                'Left Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i + row)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i + row + 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag1 = (z2 - z1) * xperp + (magnitude(i) - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;'Right Point&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z1 = magnitude(i - row)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                z2 = magnitude(i - row - 1)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mag2 = (z2 - z1) * xperp + (magnitude(i) - z1) * yperp&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            End If&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            'Now determine if the point is a maximum&lt;/span&gt;&lt;br /&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;If mag1 &gt; 0.0 Or mag2 &gt; 0.0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                idat(i) = noedge&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            ElseIf mag2 = 0.0 Then&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                idat(i) = noedge&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            Else&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                idat(i) = possedge&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            End If&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            If i Mod (467 * row) = 0 Then Application.DoEvents()&lt;/span&gt;&lt;br /&gt;&lt;span&gt;        Next i&lt;br /&gt;&lt;br /&gt;After filtering out all of the points that probably aren't edges, a hysteresis is created on the magnitude of the gradients for the remaining points. We choose a low and high threshold. If the magnitude of the gradient is lower than the low threshold, it cannot be an edge, but if it is above the high threshold, we assume it is an edge. Anything in between is considered a possible edge. As points are eliminated from the list of possible edge points, the image changes. We have to continue our comparison of gradient magnitudes and process of eliminating points until the image stops changing.&lt;br /&gt;&lt;br /&gt;The code for generating the hysteresis and creating the edge data is given here:&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Dim i, j, highc, hist(), edges, hight, lowt, tpos As Integer&lt;br /&gt;Dim thigh As Single&lt;br /&gt;Dim tlow As Single&lt;br /&gt;Dim mmag As Short&lt;br /&gt;Dim x() As Integer = {1, 1, 0, -1, -1, -1, 0, 1}&lt;br /&gt;Dim y() As Integer = {0, 1, 1, 1, 0, -1, -1, -1}&lt;br /&gt;   Dim unchanged As Boolean&lt;br /&gt;&lt;br /&gt;   'Initialize parameters&lt;br /&gt;   ReDim hist(32768)&lt;br /&gt;   thigh = 0.9&lt;br /&gt;   tlow = 0.5&lt;br /&gt;   hist.Initialize()&lt;br /&gt;&lt;br /&gt;   For i = 0 To bmsize - 1&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            'Initialize edges of image to noedge&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            If idat(i) = possedge Then idat(i) = possedge&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Else idat(i) = noedge&lt;br /&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;If i &lt;&gt; bmsize - 1 - row Or ((i + row) Mod row) = 0&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Or ((i + row + 1) Mod row) = 0 Then idat(i) = noedge&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            'Creates the histogram of the magnitudes in the image&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            If idat(i) = possedge Then _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;hist(magnitude(i)) = hist(magnitude(i)) + 1&lt;br /&gt;   Next i&lt;br /&gt;&lt;br /&gt;   'Calculate the number of pixels that pass the suppression&lt;br /&gt;   For i = 1 To 32767&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            If hist(i) &lt;&gt; 0 Then&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                mmag = i&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                edges = edges + hist(i)&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            End If&lt;br /&gt;   Next i&lt;br /&gt;&lt;br /&gt;   highc = edges * thigh + 0.5&lt;br /&gt;&lt;br /&gt;   I found this comment from Jim Carol's source code useful: Compute the high threshold value as the (100 * thigh) percentage point in the magnitude of the gradient histogram of all the pixels that passes non-maximal suppression. Then calculate the low threshold as a fraction of the computed high threshold value. John Canny said in his paper "A Computational Approach to Edge Detection" that "The ratio of the high to low threshold in the implementation is in the range two or three to one." That means that in terms of this implementation, we should choose tlow ~= 0.5 or 0.33333.&lt;br /&gt;&lt;br /&gt;   i = 1&lt;br /&gt;   edges = hist(1)&lt;br /&gt;   Do While i &lt; (mmag - 1) And edges &lt;&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;i = i + 1&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;edges = edges + hist(i)&lt;br /&gt;Loop&lt;br /&gt;hight = i&lt;br /&gt;lowt = hight * tlow + 0.5&lt;br /&gt;&lt;br /&gt;'Looks for pixels above the hight to locate edges we will need to run over the entire image until it stops changing. In this first step we seed the image with everything that is clearly an edge by being above the high threshold.&lt;br /&gt;&lt;br /&gt;For i = 0 To bmsize - 1&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;If idat(i) = possedge And magnitude(i) &gt;= hight Then idat(i) = edge&lt;br /&gt;   Next i&lt;br /&gt;&lt;br /&gt;   'run over image until it stops changing&lt;br /&gt;   unchanged = False&lt;br /&gt;   Do While unchanged = False&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;            unchanged = True&lt;br /&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;For j = 0 To bmsize - 1&lt;br /&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;If idat(j) = edge Then&lt;br /&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;For i = 0 To 7&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                        tpos = j - y(i) * row + x(i)&lt;br /&gt;                   &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;If idat(tpos) = possedge _&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;And magnitude(tpos) &gt; lowt Then&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                            unchanged = False&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;                           &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt; idat(tpos) = edge&lt;br /&gt;                   &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;End If&lt;br /&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Next i&lt;br /&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;End If&lt;br /&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;Next j&lt;br /&gt;   Loop&lt;br /&gt;&lt;br /&gt;   'Set any remaining possible edges to non-edge&lt;br /&gt;   For i = 0 To bmsize - 1&lt;br /&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;*****&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span&gt;If idat(i) &lt;&gt; edge Then idat(i) = noedge&lt;br /&gt;   Next i&lt;br /&gt;&lt;br /&gt;   'idat() now has the edge data stored to it&lt;br /&gt;&lt;br /&gt;An edge image can be exported to bmp format by assigning the value of each point in idat() to all three of the color values for each pixel of a bitmap image with the original dimensions as explained in the bitmap tutorial. Edge images have exactly 2 colors: black and white. I will occasionally use an edge image for troubleshooting purposes.&lt;br /&gt;&lt;br /&gt;With the edge image, it is now possible to look for shapes in our bitmap. We will use this ability to find the sprockets holes in the film. Once the sprockets are known, the frames are easily picked off.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;&lt;span style="font-size:180%;"&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;a href="http://digireel.blogspot.com/2007/10/sprockets.html"&gt;&lt;span style="font-size:180%;"&gt;Go on to Sprockets&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-1314472261391238684?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/1314472261391238684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=1314472261391238684' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/1314472261391238684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/1314472261391238684'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/edge-image.html' title='Canny Edge Detection'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-8007774053395019391</id><published>2007-10-25T11:27:00.000-07:00</published><updated>2010-06-29T14:44:17.975-07:00</updated><title type='text'>Sprocket Perforations</title><content type='html'>Before we can find the sprocket holes, we must know what we are looking for. In my &lt;a style="COLOR: rgb(153,255,153); FONT-WEIGHT: bold" href="http://digireel.blogspot.com/2007/10/background.html"&gt;Background&lt;/a&gt; section I list the published dimensions for regular 8 and super8 film, including the sprocket dimensions. To find these dimensions in pixels, simply multiply the dimension in inches by the dots per inch of your scan.&lt;br /&gt;&lt;br /&gt;The sprockets are positively determined one at a time starting at the right side of the scanned image since that is the location of the next frame in my set up. In finding the sprockets, I use a bit of logic to help narrow my search. I have broken this down into 3 simplifications:&lt;br /&gt;&lt;br /&gt;First, I am not actually looking for the whole sprocket all at once. Instead, I am only looking for the edge pixel that corresponds to the exact center of the left edge of the sprocket. When I find an edge pixel, I assume it is the center of the left edge of the sprocket, I make a list of where I expect the other edges pixels to be in relation to this one if it really is a sprocket, and check to see if they are there.&lt;br /&gt;&lt;br /&gt;Second, since I am only looking for the exact center of the left edge of each sprocket, my search window can be very narrow. There is no point in searching the entire image since even if I did find a sprocket near the bottom of the image, I would not be able to pick off a frame that corresponds to that sprocket. Instead, I guess that the top edge of the sprocket must be at least 5 pixels from the top of the picture. Then, since I am only looking for the edge pixel that is the center of the edge of the sprocket, I don't look for any edge pixels above 1/2 the sprocket width + 5 pixels from the top of the image. I call this my "tlim" or top limit. I also do not search for any pixel below 1/2 the sprocket width + the frame width from the bottom of the image.&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 176px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5285672207225728370" border="0" alt="" src="http://3.bp.blogspot.com/_TKVJ6OFqJzk/SVp63ibYLXI/AAAAAAAAAM0/Wv7ZA0217lc/s400/Sprocket+Search+Area.jpg" /&gt;Since we have already eliminated the bottom section of the original image, this gives a narrow strip through the center of the image in which I can reasonably expect to find my sprocket center.&lt;br /&gt;&lt;br /&gt;Third, if I already know where a sprocket is, then the next sprocket down the line should be 1 frame width away, so I search for it at that exact point (plus a little margin of error) first. If I don't find the next sprocket where I think it should be, I increase my search window to include more possibilities. If I still don't find it, I assume my last "sprocket" was a mistake and search the whole width of the frame.&lt;br /&gt;&lt;br /&gt;The tricky part of this business is how to tell if a pixel really is the edge I am looking for, or if it is a random speck of dust. This is accomplished with statistics and a couple of very long arrays of numbers. The steps below give the logic process for determining the actual location of the sprockets.&lt;br /&gt;&lt;br /&gt;1. Find the next edge pixel within the search window&lt;br /&gt;-assume it is the center of the left edge of the sprocket&lt;br /&gt;2. Calculate the location of the exact center of the sprocket if 1 were true&lt;br /&gt;3. Based on the dimensions of the sprocket, check to see if there are other pixels in the expected locations around the potential center pixel&lt;br /&gt;4. Give the center pixel a score for each pixel that you expected to find on the sprocket (a perfect super8 sprocket at 4800 dpi will have a score greater than 3000):&lt;br /&gt;+5 There is an edge pixel exactly where you expected it to be&lt;br /&gt;+3 There is an edge pixel +/- 1 pixel from the expected position&lt;br /&gt;+1 There is an edge pixel +/- 2 pixels from the expected position&lt;br /&gt;+0 There was no edge pixel&lt;br /&gt;5. Record the location of the center pixel and its score in a 2D matrix&lt;br /&gt;6. If you haven't finished searching the entire window, return to 1&lt;br /&gt;7. The pixel with the highest score is the real center of the sprocket&lt;br /&gt;8. Save the actual center of the sprocket in a separate array&lt;br /&gt;&lt;br /&gt;For debugging purposes, I keep track of the actual sprocket and color code the pixels of the sprocket hole by their score (5 = red, 3 = blue, and 1 or 0 = green). I can save an image with the sprockets colored over the original scan to show how well the sprockets are being found as seen in the image below.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="TEXT-ALIGN: center" align="justify"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_TKVJ6OFqJzk/SHtve_YTABI/AAAAAAAAAII/6qB0YqmDSnQ/s1600-h/s8+sprocket+debug+image.jpg"&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; DISPLAY: block; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5222890771066322962" border="0" alt="" src="http://4.bp.blogspot.com/_TKVJ6OFqJzk/SHtve_YTABI/AAAAAAAAAII/6qB0YqmDSnQ/s400/s8+sprocket+debug+image.jpg" /&gt;&lt;/a&gt;This is a s8 sprocket with edges identified &amp;amp; scored. Note that corners are not specifically scored because of the dificult geometry, but are treated as a region (shown in green) within which any edge pixel receives full marks as a kind of bonus.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;I found that there is need for some variation. Different makers of film, or film made at different times will have slightly different dimensions than those published and it will be enough to cause problems with picking off the frames.&lt;br /&gt;&lt;br /&gt;I solved this problem by including user inputs on my GUI that modify the sprocket dimensions and writing a routine that automatically searches for the optimal sprocket dimensions. This requires me to keep track of the average sprocket score of all sprockets found in the project, and the standard deviation of the sprocket scores. The basic idea of the automatic calibration is:&lt;br /&gt;&lt;br /&gt;1. Check to see if the average score of the current scan is within statistical tolerance of the total average score for the entire project&lt;br /&gt;- If so don't bother adjusting, otherwise go on&lt;br /&gt;2. Adjust the vertical sprocket dimension by n&lt;br /&gt;- n = -10 for the first, -8 for the second, . . . up to +10&lt;br /&gt;3. Record the sum score for all of the sprockets in the image&lt;br /&gt;4. Go back to 2 until n = 10&lt;br /&gt;5. Compare sum scores and choose the value of n that gives the greatest sum score&lt;br /&gt;6. Go back to 2, but for the horizontal dimension this time.&lt;br /&gt;7. With the 2 dimension modifiers, find the sprockets&lt;br /&gt;8. Adjust the statistics for the entire project with the new sum scores&lt;br /&gt;&lt;br /&gt;I found this routine to be quite efficient and hassle free if the tolerances are set properly. The total average sprocket score is easy enough to calculate. There is a field in the gui for the user to input the number of standard deviations he would like for his tolerances. I use 3.0. The following codes calculate the statistics.&lt;br /&gt;&lt;br /&gt;Average sprocket value of current strip&lt;br /&gt;For i = 0 To cmags.Count - 1 'average sprocket value of current strip&lt;br /&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;*****&lt;/span&gt;mavg = mavg + cmags(i) / cmags.Count&lt;br /&gt;Next i&lt;br /&gt;&lt;br /&gt;Average sprocket value for project&lt;br /&gt;For i = 0 To cs.Count - 1&lt;br /&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;*****&lt;/span&gt;cavg = cavg + cs(i) / cs.Count&lt;br /&gt;Next i&lt;br /&gt;&lt;br /&gt;Sum square error &amp;amp; standard deviation&lt;br /&gt;For i = 0 To cs.Count - 1&lt;br /&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;*****&lt;/span&gt;s = cs(i) - cavg&lt;br /&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;*****&lt;/span&gt;ss = s * s&lt;br /&gt;&lt;span style="COLOR: rgb(0,0,0)"&gt;*****&lt;/span&gt;ssum = ssum + ss&lt;br /&gt;Next i&lt;br /&gt;stdev = Sqrt(ssum / cs.Count)&lt;br /&gt;&lt;br /&gt;So then the tolerance condition is:&lt;br /&gt;mavg less than (cavg - stdev*n)&lt;br /&gt;&lt;br /&gt;where n is the user input number of standard deviations in the tolerance interval. If the condition is true, then recalculate the sprocket dimensions, otherwise you have a good sprocket. &lt;a href="http://digireel.blogspot.com/2007/10/frames.html"&gt;&lt;span style="font-size:180%;"&gt;&lt;span style="FONT-WEIGHT: bold"&gt;Frames&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-8007774053395019391?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/8007774053395019391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=8007774053395019391' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8007774053395019391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/8007774053395019391'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/sprockets.html' title='Sprocket Perforations'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_TKVJ6OFqJzk/SVp63ibYLXI/AAAAAAAAAM0/Wv7ZA0217lc/s72-c/Sprocket+Search+Area.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-4679808019708074768</id><published>2007-10-25T11:26:00.000-07:00</published><updated>2008-12-30T13:48:14.643-08:00</updated><title type='text'>Frames</title><content type='html'>The work of all of the previous code was simply to find the centers of the sprockets. Now that you have them, it is a simple exercise in mathematics to locate the frames.&lt;br /&gt;&lt;br /&gt;In general, regular 8mm frames are found between sprockets while super 8 frames are found at the center of the sprockets. With the frame &amp;amp; sprocket dimensions known (see the &lt;a href="http://digireel.blogspot.com/2007/10/background.html"&gt;Background&lt;/a&gt; tutorial), the corners of the frames are found at the following coordinates (in x, y format): &lt;div&gt;&lt;/div&gt;&lt;div&gt;R8:&lt;/div&gt;&lt;div&gt;1. Center of sprocket, Lower sprocket edge&lt;/div&gt;&lt;div&gt;2. Center of sprocket + Frame Height, Lower sprocket edge &lt;/div&gt;&lt;div&gt;3. Center of sprocket + Frame Height, Lower sprocket edge + Frame Width&lt;/div&gt;&lt;div&gt;4. Center of sprocket, Lower sprocket edge + Frame Width&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;S8:&lt;/div&gt;&lt;div&gt;1. Center of sprocket - 1/2 Frame Height, Lower sprocket edge&lt;/div&gt;&lt;div&gt;2. Center of sprocket + 1/2 Frame Height, Lower sprocket edge&lt;/div&gt;&lt;div&gt;3. Center of sprocket - 1/2 Frame Height, Lower sprocket edge + Frame Width&lt;/div&gt;&lt;div&gt;4. Center of sprocket + 1/2 Frame Height, Lower sprocket edge + Frame Width&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;I wanted to keep my films and frames in a standard aspect ratio (about 1.33:1) for viewing on the TV or computer, so the frame dimensions above were adjusted to give specific pixel dimensions at 4800 dpi:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;R8: 960 x 720 pixels&lt;/div&gt;&lt;div&gt;S8: 1104 x 792 pixels&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Each frame is saved individually as a BMP file with the name formatted to reflect the sequence of frames in the film as follows: mm.ss.ff.bmp where mm is the minute, ss is the second, and ff is the frame. A typical frame in a large reel could be 23.13.09.bmp which is the 9th frame of the 13th second of the 23 minute of the film. Remember that R8 was filmed at 16 fps and S8 was filmed at 18 fps.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As each camera exposing the film was different, some interesting differences between films developed. One interesting variation from film to film is the position of the frame in relation to the sprocket. Sometimes the relationship between the center of the sprocket and edge of the frame is off from expected by as much as 30 - 40 pixels. In the example below, the frame and sprocket are off by 20 pixels as seen by the blue outline of the frame representing the expected location of the sprocket. (Aren't I cute? Early Summer 1982)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;img id="BLOGGER_PHOTO_ID_5285694064573229490" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 366px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_TKVJ6OFqJzk/SVqOvzZHKbI/AAAAAAAAAM8/IjsIg7yIiMo/s400/Offset+Frames.jpg" border="0" /&gt;&lt;img id="BLOGGER_PHOTO_ID_5285694420138635570" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 287px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_TKVJ6OFqJzk/SVqPEf-gsTI/AAAAAAAAANM/mUFYD84TIW0/s400/Offset+Frame.jpg" border="0" /&gt;In the next images, the frame location was adjusted 20 pixels.&lt;img id="BLOGGER_PHOTO_ID_5285694165060011186" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 364px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_TKVJ6OFqJzk/SVqO1pvBxLI/AAAAAAAAANE/G-F-yAJqYpg/s400/Corrected+Frames.jpg" border="0" /&gt;&lt;img id="BLOGGER_PHOTO_ID_5285694497764737874" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 287px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_TKVJ6OFqJzk/SVqPJBKBA1I/AAAAAAAAANU/6z_lVCXzMEU/s400/Corrected+Frame.jpg" border="0" /&gt;Note that in the second frame shown above, the image fills the entire frame and is not cropped as in the first frame above.&lt;br /&gt;&lt;br /&gt;Originally, I was converting all of the *.bmp frames to *.jpg using Adobe Photoshop Elements, but it was time consuming. I found I could save 2/3 of the time if I simply added 6 additional lines of code and converted the images to *.jpg format right after saving them as *.bmp. Since the images are already loaded into memory at this point, it is easy and fast to convert them. The necessary code is:&lt;br /&gt;&lt;br /&gt;Imports System.Drawing&lt;br /&gt;Imports System.Drawing.Imaging&lt;br /&gt;&lt;br /&gt;Dim fbm as Bitmap&lt;br /&gt;Dim jcinfo As ImageCodecInfo&lt;br /&gt;Dim jqual As EncoderParameter&lt;br /&gt;Dim jparams As EncoderParameters&lt;br /&gt;Dim j As Integer&lt;br /&gt;Dim encoders() As ImageCodecInfo&lt;br /&gt;&lt;br /&gt;encoders = ImageCodecInfo.GetImageEncoders()&lt;br /&gt;j = 0&lt;br /&gt;While j &lt;&gt; &lt;div&gt;If encoders(j).FormatID = ImageFormat.Jpeg.Guid Then jcinfo = encoders(j)&lt;/div&gt;&lt;div&gt;j += 1&lt;/div&gt;&lt;div&gt;End While&lt;/div&gt;&lt;div&gt;jqual = New EncoderParameter(Encoder.Quality, CType(100L, Int32))&lt;br /&gt;jparams = New EncoderParameters(1)&lt;br /&gt;jparams.Param(0) = jqual&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;fbm = New Bitmap("c:\YourDirectory\YourFileName.bmp")&lt;br /&gt;fbm.Save("c:\YourDirectory\YourFileName.jpg", jcinfo, jparams)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;I found that converting these files with Adobe Photoshop Elements 4.0 requires about 30 seconds for the film strip and the individual frames, but only adds about 7 seconds to the overall process when done in the manner described above.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After the frames are captured and saved, they must be compiled into an AVI file.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;a href="http://8mm2dvd.blogspot.com/2007/10/bmp-to-avi.html"&gt;&lt;span style="font-size:180%;"&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size:180%;"&gt;&lt;a style="FONT-WEIGHT: bold" href="http://digireel.blogspot.com/2007/10/bmp-to-avi.html"&gt;BMP to AVI&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-4679808019708074768?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/4679808019708074768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=4679808019708074768' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/4679808019708074768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/4679808019708074768'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/frames.html' title='Frames'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_TKVJ6OFqJzk/SVqOvzZHKbI/AAAAAAAAAM8/IjsIg7yIiMo/s72-c/Offset+Frames.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-3271019404810204092</id><published>2007-10-25T11:25:00.000-07:00</published><updated>2011-01-05T10:42:18.894-08:00</updated><title type='text'>BMP to AVI</title><content type='html'>The whole point of this blog and all of the work of the previous sections was to take individual pictures on old, deteriorating strip of film, and preserve them in a digital movie format. The simplest way to do this that I found is simply to create an uncompressed AVI including all of the *.bmp frames.&lt;br /&gt;&lt;br /&gt;First, it is useful to review the AVI format. There are several good sites full of useful information, but I haven't kept track of any of them and cannot recommend any of them. At the most basic level, an AVI file is an empty box. You can put anything you want into it so long as you specify the method for retrieving the items within the box. Computers use codecs to determine how things are put into and removed from these "video boxes." My method uses the original AVI codec that comes with Windows. It is limited to a 2 GB files size (the codec was created long before 2 GB was ever dreamed of as a potential file size - back when the biggest hard drives were measured in MB). The codec is easy to use - you simply tell the computer (through the AVI Header) information about how many frames there will be in the video, their dimension (number of bytes each), and how fast to display them on the screen, then which order to play them in (pack the bytes of data into the file.&lt;br /&gt;&lt;br /&gt;The AVI Class used in my code was provided from somewhere else, so I will happily share it with anyone. Simply request the AVI Class in an email to &lt;a href="mailto:digireel@gmail.com"&gt;digireel [at] gmail [dot] com&lt;/a&gt; directed to Kyle. The AVI Class is called in the routines below.&lt;br /&gt;&lt;br /&gt;The essential code follows. I will give it in pieces and describe what happens in each step. First dimension the variables to be used.&lt;br /&gt;&lt;br /&gt;Dim i, j, k, l, fps, result, maxframes, navi, pw, ph As Integer&lt;br /&gt;Dim bmp(), fbmp() As Byte&lt;br /&gt;Dim avistream, pfile As IntPtr&lt;br /&gt;Dim bm As Bitmap&lt;br /&gt;Dim bmd As BitmapData&lt;br /&gt;Dim bi As New Avi.BITMAPINFOHEADER&lt;br /&gt;Dim opts As New Avi.AVI_COMPRESS_OPTIONS&lt;br /&gt;Dim strhdr As New Avi.AVISTREAM_INFO&lt;br /&gt;&lt;br /&gt;Some of these variables are part of the AVI class I mentioned above. There are other variables used, but they are straight forward. Next populate the AVI header with the correct information. The Height and Width are the height and width of your individual *.bmp frames in pixels. The strhdr.dwRate property is the frame rate in frames per second (either 16 or 18 for 8mm films).&lt;br /&gt;&lt;br /&gt;'Populate the AVI Info Header&lt;br /&gt;strhdr.fccType = 1935960438 'vids&lt;br /&gt;strhdr.fccHandler = 541215044&lt;br /&gt;strhdr.dwFlags = 0&lt;br /&gt;strhdr.wPriority = 0&lt;br /&gt;strhdr.wLanguage = 0&lt;br /&gt;strhdr.dwInitialFrames = 0&lt;br /&gt;strhdr.dwScale = 1&lt;br /&gt;strhdr.dwRate = fps&lt;br /&gt;strhdr.dwStart = 0&lt;br /&gt;strhdr.dwSuggestedBufferSize = 3 * Height * Width&lt;br /&gt;strhdr.dwQuality = 0&lt;br /&gt;strhdr.dwSampleSize = 0&lt;br /&gt;strhdr.rcFrame.top = Height&lt;br /&gt;strhdr.rcFrame.left = Width&lt;br /&gt;&lt;br /&gt;Next populate a BMP header with the appropriate information. This header will be referenced by the AVI file header to interpret the image data for each frame. The Width and Height variables below are the width and height of your *.bmp frames in pixels. The bi.biBitCount refers to the bits per pixes (24 bit color) of the images.&lt;br /&gt;&lt;br /&gt;'Populate the BMP Info Header&lt;br /&gt;bi.biSize = Int(Marshal.SizeOf(bi))&lt;br /&gt;bi.biWidth = Width&lt;br /&gt;bi.biHeight = Height&lt;br /&gt;bi.biPlanes = 1&lt;br /&gt;bi.biBitCount = 24&lt;br /&gt;bi.biCompression = 0&lt;br /&gt;bi.biSizeImage = Width * Height&lt;br /&gt;bi.biXPelsPerMeter = bm.HorizontalResolution * 1 / 2.54 * 100&lt;br /&gt;bi.biYPelsPerMeter = bm.VerticalResolution * 1 / 2.54 * 100&lt;br /&gt;bi.biClrUsed = 0&lt;br /&gt;bi.biClrImportant = 0&lt;br /&gt;&lt;br /&gt;Resize 2 arrays to hold the byte information of the individual frames to be added to the AVI.&lt;br /&gt;&lt;br /&gt;ReDim bmp(bm.Height * bm.Width * 3 - 1)&lt;br /&gt;ReDim fbmp(bm.Height * bm.Width * 3 - 1)&lt;br /&gt;&lt;br /&gt;Determine the maximum number of frames that can be put into a single AVI file of 2 GB. In this case, I am not exactly sure of the file size of each frame used, so I give myself a 10 MB margin of error. Then determine how many AVI files will be needed to encode all of the frames.&lt;br /&gt;&lt;br /&gt;maxframes = Floor((2147483648 - 1048576 * 10) / (bm.Height * bm.Width * 3 + 40))&lt;br /&gt;navi = Ceiling(nframe / maxframes)&lt;br /&gt;&lt;br /&gt;With the headers populated and the number of frames in each AVI known, we are ready to initialize the AVI class and begin filling the video. All of the following code snippets must be repeated for each AVI to be created.&lt;br /&gt;&lt;br /&gt;First create the final AVI file and prepare it to the data you will put into it.&lt;br /&gt;&lt;br /&gt;Avi.AVIFileInit()&lt;br /&gt;strhdr.dwLength = nframe&lt;br /&gt;'Create the AVI file and get its pointer (pfile)&lt;br /&gt;result = Avi.AVIFileOpen(pfile, "c:\YourDirectory\YourVideoName.AVI", &amp;amp;H1 Or &amp;amp;H1000, 0&amp;amp;)&lt;br /&gt;'Create the AVI File Holder to fill the AVI file&lt;br /&gt;result = Avi.AVIFileCreateStream(pfile, avistream, strhdr)&lt;br /&gt;'Format the AVI File Holder&lt;br /&gt;result = Avi.AVIStreamSetFormat(avistream, 0, bi, Marshal.SizeOf(bi))&lt;br /&gt;&lt;br /&gt;Then fill the AVI file with the frames. This is done in a loop where each frame is read byte by byte into the AVI file. The following code is done for each frame.&lt;br /&gt;&lt;br /&gt;bm = New Bitmap("c:\YourDir\YourFrame.bmp")&lt;br /&gt;bmd = bm.LockBits(New Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb)&lt;br /&gt;Marshal.Copy(bmd.Scan0, fbmp, 0, bmd.Stride * bmd.Height)&lt;br /&gt;For j = 0 To bmd.Stride - 1&lt;br /&gt;For k = 0 To bmd.Height - 1&lt;br /&gt;bmp((bmd.Height - k - 1) * bmd.Stride + j) = fbmp(j + k * bmd.Stride)&lt;br /&gt;Next k&lt;br /&gt;Next j&lt;br /&gt;Marshal.Copy(bmp, 0, bmd.Scan0, bmd.Height * bmd.Stride)&lt;br /&gt;Avi.AVIStreamWrite(avistream, i, 1, bmd.Scan0, bmd.Stride * bmd.Height, 0, 0&amp;amp;, 0&amp;amp;)&lt;br /&gt;bm.UnlockBits(bmd)&lt;br /&gt;bm = Nothing&lt;br /&gt;bmd = Nothing&lt;br /&gt;&lt;br /&gt;Be sure to release the memory you allocated for the image and AVI files and close the files.&lt;br /&gt;&lt;br /&gt;Avi.AVIStreamRelease(avistream)&lt;br /&gt;Avi.AVIFileRelease(pfile)&lt;br /&gt;&lt;br /&gt;After creating the uncompressed files, I convert them to DV AVI format using Ulead Video Studio. I do this because the original AVI files are limited to 2 GB in size and a typical 50 foot reel may require 13 GB of space while the DV format AVI file can be any size (I have some files up to 10 GB or 40 minutes of film) and a typical 50 foot reel requires only 1 GB of space.&lt;br /&gt;&lt;br /&gt;To watch the films on a DVD player, it is a simple matter to use any software available to the user (I use Ulead) to convert the DV AVI file to mpeg2 format and burn it to a DVD (DVD video files are *.vob format).&lt;br /&gt;&lt;br /&gt;For my personal film archive, I keep only the *.jpg images, the DV AVI files, and the vob files.&lt;br /&gt;&lt;br /&gt;Some examples of my personal projects are found in the next section.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;&lt;a style="font-weight: bold;" href="http://digireel.blogspot.com/2007/10/results.html"&gt;Results&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-3271019404810204092?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/3271019404810204092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=3271019404810204092' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/3271019404810204092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/3271019404810204092'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/bmp-to-avi.html' title='BMP to AVI'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-707070464272674016.post-6354886353445615463</id><published>2007-10-25T11:24:00.000-07:00</published><updated>2010-09-15T13:59:51.347-07:00</updated><title type='text'>Results</title><content type='html'>Check it out!&lt;br /&gt;&lt;br /&gt;OK - its been 3 years since I created this guide and I have finished every section but the "Results". I intend to add samples (videos) of my most recent work soon . . . promise. :)&lt;br /&gt;Probably before I defend my PhD thesis . . . right.&lt;br /&gt;&lt;br /&gt;Please let me know if you have developed your own scanner based on my information and how things work for you.&lt;br /&gt;&lt;br /&gt;Thanks,&lt;br /&gt;Kyle&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/707070464272674016-6354886353445615463?l=digireel.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://digireel.blogspot.com/feeds/6354886353445615463/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=707070464272674016&amp;postID=6354886353445615463' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/6354886353445615463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/707070464272674016/posts/default/6354886353445615463'/><link rel='alternate' type='text/html' href='http://digireel.blogspot.com/2007/10/results.html' title='Results'/><author><name>Kyle Brunner</name><uri>http://www.blogger.com/profile/00330750821407798511</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry></feed>
