ravegoat
12-09-11, 13:27
مقدمه:
در .Net نمي توان دو تصوير را با دستور شرطي و استفاده از عملگر تساوي با يكديگر مقايسه كرد. به عبارت ديگر كد زير يك فرمان غلط است:
Dim a,b As Bitmap
'Runtime Error:
If a = b Then MsgBox("The two pictures are same")
پس بايد به دنبال راه ديگري باشيم.
با يك منطق ساده مي توان اين نتيجه را گرفت كه دو تصوير زماني مشابه اند كه اندازه ي آن ها (پهنا و ارتفاع) با هم برابر بوده و رنگ تمامي پيكسل هاي آن دو نظير به نظير با هم برابر باشد.
بر اساس منطق فوق براي مقايسه ي دو تصوير نياز داريم كه رنگ تمامي پيكسل هاي آن ها را با هم مقايسه كنيم. از ديد برنامه نويسي بايد از دو حلقه ي تو در تو استفاده كرد به گونه اي كه يك حلقه پيكسل ها را در راستاي طولي و ديگري پيكسل ها را در راستاي عرضي پويش كند. پويش به محض يافتن تفاوت ميان دو پيكسل متوقف مي شود و مقداري جهت اعلام عدم تطابق دو عكس برگردانده مي شود.
اما اين سوال پيش مياد آيا اين روش كارايي لازم را دارد؟
اين پرسش پاسخ قطعي ندارد. دو تصوير مشابه به ابعاد 1440x900 px را در نظر مي گيريم. الگوريتم ارايه شده در بالا براي تاييد مشابهت دو تصوير بايد 1440*900=1296000 پيكسل را در هر تصوير نظير به نظير با هم مقايسه كند كه پروسه اي بسيار زمان بر خواهد بود.
حال دو تصوير ديگر را با اندازه ي مشابه در نظر مي گيرم به طوري كه در مورد تشابه آن دو اطلاعي نداريم. اگر الگوريتم اولين پيكسل را بررسي كند و پيكسل ها متفاوت باشند، پروسه خيلي سريع در همين ابتدا به پاسخ مي رسد (در كم تر از يك ميلي ثانيه).
با كمي گشت و گذار در اينترنت مي توانيم روش هايي جالب ديگري را براي مقايسه ي تصاوير بيابيم. يكي از روش هايي موجود تبديل بايت هاي دو تصوير به دو رشته ي منحصر به فرد است. در واقع ما به هر تصوير مي توانيم يك اثر انگشت اختصاص دهيم. اين كار را مي توان با روش هاي Hashing و رمزنگاري مانند MD5 انجام داد. اين روش به مراتب سريع تر از الگوريتم قبلي است.
حال بياييم كارايي اين روش را نيز تحليل كنيم. بسته به روش در هم ريختن بايت ها، ممكن است هش كردن تصاوير بسيار بزرگ امكان پذير نباشد. از طرفي تغييرات بسيار جزيي در بايت هاي تصوير مي تواند عبارت اثر انگشت آن را كاملا" دگرگون كند. در نتيجه هنگام مقايسه ي فايل هاي تصويري حاوي EXIF (Property Item) نظير نرم افزار توليد عكس، مقدار ISO و غيره، اين عامل مي توان مشكل زا باشد. به عبارتي اين روش دو تصوير مشابه اما با داده هاي Exif متفاوت را به اشتباه مطابق يكديگر نمي داند.
براي رفع اين مشكل مي توانيم تصاوير را ابتدا به قالب BMP ببريم كه سبب حذف داده هاي Exif مي شود و سپس عمل Hashing را روي بايت هاي تصاوير تبديل شده اجرا كنيم. بديهي است كه با اين كار سرعت الگوريتم كاهش مي يابد.
الگوريتم آخر كه به معرفي آن مي پردازيم، ذخيره ي تصاوير در Memory Stream ها براي مقايسه است. در اين روش مستقيما" مي توان با تعيين ImageFormat از نوع BMP، داده هاي Exif را حذف كرد. سپس با فرمان Equal ، مي توانيم اين Memory Stream ها را مقايسه كنيم. به علاوه در اين روش بدون رخ دادن خطاي GDI+، مي توان تمامي منابع اشغال شده توسط مموري استريم ها را با فرمان Dispose آزاد كرد.
نتيجه گيري:
شايد تا كنون دريافته ايد كه هر يك از روش هاي ارايه شده بسته به كاربرد برنامه مي توانند در جاي خود بهترين باشند.
در مواردي كه مي دانيم اكثر تصاويري كه نياز به بررسي آن ها داريم با يكديگر تفاوت بسيار دارند، روش اول شايد بهتر گزينه باشد زيرا در حالي كه روش دوم وقت نسبتا" زيادي را صرف Hashing مي كند و روش آخر مقدار قابل توجهي از فضاي رم را با مموري استريم هاي خود اشغال مي كند، روش اول با احتمال زياد در همان پيكسل ها ابتدايي نتيجه را بسيار سريع مشخص مي كند.
روش آخر در حالت كلي خيلي سريع تر از ساير روش هاي مطرح شده عمل مي كند ولي زماني كه نياز به مقايسه ي تعداد زيادي عكس با هم داريم به دليل بارگذاري تمامي آن ها در حافظه، بخش زيادي از رم اشغال مي شود. اين در حالي است كه روش دوم پس از بارگذاري هر عكس يك اثر انگشت به آن اختصاص داده مي دهد و ديگر نيازي نيست كه آن عكس در حافظه مقيم باشد. در اين صورت هر تصوير با اثر انگشت خودش شناسايي شده و در نهايت اين اثر انگشت ها كه فضاي خيلي كم تري را نسبت به تصاوير نظيرشان اشغال مي كنند، با هم مقايسه مي شوند.
دوستان مي توانند سورس VB.NET تمام روش هاي مورد بحث را از فايل پيوست دريافت نمايند.
در .Net نمي توان دو تصوير را با دستور شرطي و استفاده از عملگر تساوي با يكديگر مقايسه كرد. به عبارت ديگر كد زير يك فرمان غلط است:
Dim a,b As Bitmap
'Runtime Error:
If a = b Then MsgBox("The two pictures are same")
پس بايد به دنبال راه ديگري باشيم.
با يك منطق ساده مي توان اين نتيجه را گرفت كه دو تصوير زماني مشابه اند كه اندازه ي آن ها (پهنا و ارتفاع) با هم برابر بوده و رنگ تمامي پيكسل هاي آن دو نظير به نظير با هم برابر باشد.
بر اساس منطق فوق براي مقايسه ي دو تصوير نياز داريم كه رنگ تمامي پيكسل هاي آن ها را با هم مقايسه كنيم. از ديد برنامه نويسي بايد از دو حلقه ي تو در تو استفاده كرد به گونه اي كه يك حلقه پيكسل ها را در راستاي طولي و ديگري پيكسل ها را در راستاي عرضي پويش كند. پويش به محض يافتن تفاوت ميان دو پيكسل متوقف مي شود و مقداري جهت اعلام عدم تطابق دو عكس برگردانده مي شود.
اما اين سوال پيش مياد آيا اين روش كارايي لازم را دارد؟
اين پرسش پاسخ قطعي ندارد. دو تصوير مشابه به ابعاد 1440x900 px را در نظر مي گيريم. الگوريتم ارايه شده در بالا براي تاييد مشابهت دو تصوير بايد 1440*900=1296000 پيكسل را در هر تصوير نظير به نظير با هم مقايسه كند كه پروسه اي بسيار زمان بر خواهد بود.
حال دو تصوير ديگر را با اندازه ي مشابه در نظر مي گيرم به طوري كه در مورد تشابه آن دو اطلاعي نداريم. اگر الگوريتم اولين پيكسل را بررسي كند و پيكسل ها متفاوت باشند، پروسه خيلي سريع در همين ابتدا به پاسخ مي رسد (در كم تر از يك ميلي ثانيه).
با كمي گشت و گذار در اينترنت مي توانيم روش هايي جالب ديگري را براي مقايسه ي تصاوير بيابيم. يكي از روش هايي موجود تبديل بايت هاي دو تصوير به دو رشته ي منحصر به فرد است. در واقع ما به هر تصوير مي توانيم يك اثر انگشت اختصاص دهيم. اين كار را مي توان با روش هاي Hashing و رمزنگاري مانند MD5 انجام داد. اين روش به مراتب سريع تر از الگوريتم قبلي است.
حال بياييم كارايي اين روش را نيز تحليل كنيم. بسته به روش در هم ريختن بايت ها، ممكن است هش كردن تصاوير بسيار بزرگ امكان پذير نباشد. از طرفي تغييرات بسيار جزيي در بايت هاي تصوير مي تواند عبارت اثر انگشت آن را كاملا" دگرگون كند. در نتيجه هنگام مقايسه ي فايل هاي تصويري حاوي EXIF (Property Item) نظير نرم افزار توليد عكس، مقدار ISO و غيره، اين عامل مي توان مشكل زا باشد. به عبارتي اين روش دو تصوير مشابه اما با داده هاي Exif متفاوت را به اشتباه مطابق يكديگر نمي داند.
براي رفع اين مشكل مي توانيم تصاوير را ابتدا به قالب BMP ببريم كه سبب حذف داده هاي Exif مي شود و سپس عمل Hashing را روي بايت هاي تصاوير تبديل شده اجرا كنيم. بديهي است كه با اين كار سرعت الگوريتم كاهش مي يابد.
الگوريتم آخر كه به معرفي آن مي پردازيم، ذخيره ي تصاوير در Memory Stream ها براي مقايسه است. در اين روش مستقيما" مي توان با تعيين ImageFormat از نوع BMP، داده هاي Exif را حذف كرد. سپس با فرمان Equal ، مي توانيم اين Memory Stream ها را مقايسه كنيم. به علاوه در اين روش بدون رخ دادن خطاي GDI+، مي توان تمامي منابع اشغال شده توسط مموري استريم ها را با فرمان Dispose آزاد كرد.
نتيجه گيري:
شايد تا كنون دريافته ايد كه هر يك از روش هاي ارايه شده بسته به كاربرد برنامه مي توانند در جاي خود بهترين باشند.
در مواردي كه مي دانيم اكثر تصاويري كه نياز به بررسي آن ها داريم با يكديگر تفاوت بسيار دارند، روش اول شايد بهتر گزينه باشد زيرا در حالي كه روش دوم وقت نسبتا" زيادي را صرف Hashing مي كند و روش آخر مقدار قابل توجهي از فضاي رم را با مموري استريم هاي خود اشغال مي كند، روش اول با احتمال زياد در همان پيكسل ها ابتدايي نتيجه را بسيار سريع مشخص مي كند.
روش آخر در حالت كلي خيلي سريع تر از ساير روش هاي مطرح شده عمل مي كند ولي زماني كه نياز به مقايسه ي تعداد زيادي عكس با هم داريم به دليل بارگذاري تمامي آن ها در حافظه، بخش زيادي از رم اشغال مي شود. اين در حالي است كه روش دوم پس از بارگذاري هر عكس يك اثر انگشت به آن اختصاص داده مي دهد و ديگر نيازي نيست كه آن عكس در حافظه مقيم باشد. در اين صورت هر تصوير با اثر انگشت خودش شناسايي شده و در نهايت اين اثر انگشت ها كه فضاي خيلي كم تري را نسبت به تصاوير نظيرشان اشغال مي كنند، با هم مقايسه مي شوند.
دوستان مي توانند سورس VB.NET تمام روش هاي مورد بحث را از فايل پيوست دريافت نمايند.