نویسهخوانی به عملیات تشخیص متن در عکس و تبدیل آن میباشد. در این پروژه از شما انتظار میرود تا متن انگلیسی را در تصاویر حاوی متون تایپ شده انگلیسی تشخیص دهید. # مقدمه مدتی پیش در ادارات و سازمانهای اداری، تجاری مثل بانکها تمام اسناد و مدارک بصورت دستنویس بود وبه صورت کاغذ بایگانی میشد، برای بایگانی آن اسناد و همچنین نگه داری آنها مکانی بزرگ مورد نیاز بود، حتی دستیابی به سند مورد نظر زمانبر بود. با پیشرفت تکنولوژی پس از مدتی تصمیم گرفته شد تا تمام آن اسناد بصورت متنهای قابل ویرایش و قابل جستجو در رایانهها ذخیره شوند، اما با این حجم عظیم اسناد، چگونه؟ نابینایان نیاز به کتابهایی داشتند که یا صوتی باشد و یا به خط مخصوصشان نوشته شده باشد، اما با این تعداد زیاد کتابها، چگونه ؟ اینگونه نیازها باعث شد تا مفهومی به نام OCR[^Optical Character Recognition] معرفی شود، OCR به فرایند تبدیل اسناد تایپ شده، متون داخل تصاویر، دستنوشتهها و ... به متن قابل ویرایش و جستجو برای دستگاه میباشد. امروزه مثالهایی این چنین که از OCR استفاده میشود را در اطرافمان زیاد میبینیم، از بارکد خوانهای فروشگاه، اسکنرهای بانک برای اسکن قبوض و چکها، ... تا اپلیکیشنهایی چون QRCode خوانها که در تلفنهای هوشمند خود و اطرافیان به کرات مشاهده کردهایم. ## پیش پردازش تصاویرمورد نظر ممکن است دارای مشکلاتی باشد که فرایند تشخیص متن را به خطا بیاندازد، برخی مشکلات و راه حل آنها و پیش پردازشهای دیگر عبارتند از: + **اصلاح زاویهی تصاویر[^De-Skew] :** ممکن است تصویر ورودی زاویه مناسبی نداشته باشه،باید با روشهایی تصویر را چرخاند تا زاویهاش با خط افق صفر شود! + **حذف نویز موجود در تصاویر:** ممکن است به دلیل قدیمی بودن، کیفیت پایین دوربین و دلایلی دیگر تصویر ورودی دارای مقداری نویز (نقاط سیاه یا رنگی اضافی در تصویر) باشد که باید در از بین بردن یا حداقل کاهش آنها کوشید تا تشخیص اشتباهی نداشته باشیم.[1,2] + **سیاه و سفید کردن تصاویر:** البته این مورد جزء مشکلات نمیباشد ولی اگر تصاویر رنگی بود، نیاز است که به دلیل تشخیص راحت تر و جدا کردن متن از پس زمینهی تصویر از این کار استفاده شود(به عبارتی دیگر تضاد رنگی بین نوشته و پسزمینه به وجود بیاید). برای این کار، روشهای گوناگونی از قبیل otsu، markov، global fixed و ... وجود دارد که بعداً برخی از روشهایش را توضیح خواهم داد.[1,2,3] + **حذف خطوط اضافی:** برخی از نوشتهها دارای خطوط (بردارهایی) در اطرافشان یا زیرخط[^Underline] و ... هستند که جزء خود متن نیستند.[2] + **چسبیدگی یا جداشدن حروف:** در برخی تصاویر ممکن است قسمتی از یک حرف جدا شده باشد و یا ممکن است چند حرف بههم چسبیده باشند، مانند تصویر زیر[2] ![تصویر شماره 1 - برخی از مشکلات پیش رو و رفع آن](https://boute.s3.amazonaws.com/192-problem.jpg) ### روشهای سیاه و سفید کردن تصاویر[^Binarization] در این قسمت چند مورد از روشهای سیاه و سفید کردن تصاویر با هدف جدا کردن متن از پسزمینه اشاره میکنیم. \* در اینجا هر رنگ پیکسل -براساس مقدار سیاه یا سفید بودن[^GrayScale]- تصویر ورودی را x و هر پیکسل تصویر نهایی را b مینامیم. + **روش Global Fixed Threshold :** در این روش که از سادهترین روشهاست اگر $x_i\ge 0.5$ باشد $b_i$ را یک میگذاریم، در غیر این صورت صفر میگذاریم(فرض بر این است که $x_i$ بین صفر تا یک و $b_i$ فقط یک یا صفر میتوانند باشند).[3] + **روش Otsu Threshold :** تقریبا همانند روش قبلی میباشد با این تفاوت که به جای مقدار 0.5 مقدار t را درنظر میگیریم، این مقدار باید به گونهای انتخاب شود که به بهترین نتیجه منجر شود. اگر رنگ پیکسلها را بر اساس مقدار سیاه یا سفید بودنشان مقداردهی کنیم مقادیر اینگونه میباشد که کلاس مورد نظر {G={0,1,…,L-1 می باشد و بر اساس t این به دو کلاس {0,1,…,t} و {t+1,t+2,…,L-1} تقسیم میشود که باید مقدار واریانس داخل کلاسی به حداقل و واریانس بین کلاسی به حداکثر برسد، یعنی اگر واریانس داخلی را به صورت زیر تعریف کنیم، باید t به گونه ای باشد که این واریانس به حداقل برسد . $${\sigma_W}^2(t) = W_1(t){\sigma_1}^2(t) + W_2(t){\sigma_2}^2(t) $$ که در این جا Wها احتمال دو کلاس مجزا شده توسط t می باشد و اگر واریانس بین کلاسی را به صورت زیر تعریف کنیم، باید مقدارش به حداکثر برسد. $${\sigma_B}^2(t) = {\sigma}^2 - {\sigma_W}^2(t) = W_1(t)W_2(t) [\mu_1(t) - \mu_2(t) ]^2 $$ و داریم[4] $$W_1(t) = {\Sigma}_0^t p_i(t)$$ $$\mu_1(t) = {\Sigma}_0^t p_i(t) x_i(t) $$ + **روش Markov Model :** در این روش علاوه بر پیکسل فعلی، پیکسلهای قبلی هم مورد بررسی قرار میگیرد، در اینجا بطور مثال پیکسل قبلی یعنی پیکسلهای بالا و سمت چپ پیکسل مورد نظر هستند که تصمیم برای این که پیکسل کنونی سیاه است یا سفید به پیکسلهای قبلی وابسته است، علاوه بر برتری این روش نسبت به روشهای دیگر به خاطر تشخیص بهتر انحنا و زواید حروف، معایبی هم دارد بطور مثال در تصویر شماره 3 در نمونه شماره 2 میبینید که پیکسل اضافهتری هم سیاه شده است.[3] ![تصویر شماره 2 - بررسی پیکسل های قبلی در روش Markov](https://boute.s3.amazonaws.com/192-Markov1.png) ![تصویر شماره 3 - مقایسه روش Markov و روش General fixed threshold](https://boute.s3.amazonaws.com/192-Markov2.png) ##پردازش در این مرحله باید به هدف اصلی که تشخیص حروف و تبدیل آنها به متن قابل ویرایش و جستجو برای دستگاه میباشد برسیم. الگوریتمها و روشهای گوناگونی وجود دارند که در قسمتهای بعدی مفصلتر به آنها خواهم پرداخت. ##پس پردازش پس از این که متن را تشخیص دادیم، ممکن است برخی از حروف به درستی تشخیص داده نشده باشند یا حتی ممکن است خود دستنوشته غلط املایی داشته باشد، پس برای بالا بردن دقت نتیجهی کار میتوان از واژهنامهها[^Lexicon] استفاده کرد که برای زبانهای برنامه نویسی مختلف ارائه شده اند. واژهنامهها میتوانند عمومی یا تخصصی برای زمینه خاصی مثلا ورزشی، مهندسی، ... باشند.[1,7] # کارهای مرتبط مواردی که در مقدمه ذکر شد، کارهایی است که برای بهبود نتایج انجام داده میشود. برای تشخیص و تبدیل متن از تصاویر، روش های متفاوتی وجود دارد که برخی از آنها شامل چند مورد زیر میباشد: ## الگوریتمها + **تطبیق الگو[^Pattern Matching] به روش شیار-قطاع :** در این روش به تولید ماتریس مخصوص هر حرف میپردازیم و سپس ماتریس را با ماتریسهای نمونههایی که از قبل داشتهایم و مقدار آن را میدانیم مقایسه میکنیم و سپس تصمیم میگیریم. پس از طی نمودن مراحل پیش پردازشها برای نرمالسازی تصویر مورد نظر، باید ابتدا متن را به صورت سطر به سطر جدا کرده و در هر سطر هر یک از حروف را مورد پردازش قرار دهیم و پس از اتمام پردازش بر روی یک حرف، سراغ حروف بعدی رفته و در انتها از در کنار هم گذاشتن اطلاعات بدست آمده، متن را تشکیل میدهیم. پس از این که تصویر را محدود به حروف مورد نظر کردیم، تصویر حرف کنونی را به صورت صفر و یک درون یک ماتریس $ n \times n $ ذخیره میکنیم(بهطور مثال ماتریس $ 15 \times 15 $)، بدین صورت که بهجای قسمتهای سیاه رنگ 1 گذاشته و قسمتهای سفید رنگ را صفر میگذاریم. در ادامه ماتریس به دست آمده را به 5 شیار[^Track] و 8 قطاع[^Sector] تقسیم میکنیم، برای این کار ابتدا درایهی مرکزی ماتریس را یافته و سپس دورترین درایه از نظر فاصله نسبت به درایهی مرکزی مییابیم، با تقسیم مقدار به دست آمده-شعاع-به تعداد شیارها میتوان شیارهای مختلف ماتریس را به دست آورد. ![تصویر شماره 4 - تبدیل تصویر به ماتریس متناظر و تقسیم ماتریس به شیار و قطاع](https://boute.s3.amazonaws.com/192-patttern.png) با در نظر گرفتن شیار و قطاع مرتبط با آن میتوان تعداد درایههای با مقدار یک را به دست آورد، سپس ماتریسی جدیدی تشکیل داده، موسوم به ماتریس شیار-قطاع که هر درایهی آن نشاندهندهی تعداد یک در شیار و قطاع مورد نظر میباشد. حال برای تشخیص حرف، آن را با نمونههایی که از قبل داشتهایم مقایسه کرده و آن را به دست میآوریم.[6] + **تطبیق الگو به روش نزدیکترین همسایگی[^K-Nearest Neighbour] :** در این روش هم اساس کار بر تقسیمبندی تصویر و تبدیل آن به ماتریس متناظر میباشد با این تفاوت که یک مرحلهی پیش پردازش اضافی دارد و روش تقسیمبندی و دستهبندی کردن[^Classification] از روش قبل متفاوت است. ابتدا به توضیح مختصری در ارتباط با الگوریتم نزدیکترین همسایگی(K-NN) میپردازم. هدف این الگوریتم دستهبندی ورودیها بر اساس دستهبندیهای دادههایی از قبل دستهبندی شدهاند، میباشد. اگر D را مجموعه داده هایی در نظر بگیریم که قبلا دستهبندی شده و x را دادهای که قصد دستهبندی کردنشان را داریم، که $ x = ( x\prime , y\prime ) $ که در اینجا $ x\prime $ دادهی مورد بررسی میباشد و $ y\prime $ هم نام دستهاش. ابتدا به محاسبهی فاصلهی بین دادهی مورد بررسی و کل دادههای پیشین میپردازیم که به تشکیل آرایهی k عضوی از نزدیکترین دستهها به نام $ D_Z $ منجر میشود. حال، با در نظر گرفتن رأی اکثریت[^Majority Voting] اقدام به انتخاب دستهی مورد نظر میکنیم، که رأی اکثریت را میتوان از روش زیر به دست آورد. $$ y' = _v^{argmax} \Sigma _{(x_i,y_i) \in D_z} I( v = x_i ) $$ \*در اینجا i ، منظور i امین نزدیکترین همسایه میباشد و v هم نام دسته میباشد و همچنین تابع I هم تابعی است که در صورت درستی عبارت داخلش 1 برمیگرداند و در غیر اینصورت صفر برمیگرداند. تصویر زیر مثالی از این الگوریتم میباشد، بدین صورت که اگر دو دستهی مربعهای آبی و دستهی دایرههای سبز در نظر بگیریم در صورتی که K=3 باشد، سه تا از نزدیکترینها دارای دو مربع آبی و یک دایرهی سبز میباشد که دستهی مربعهای آبی را برمیگزیند. ![تصویر شماره 5 - مثالی از روش نزدیک ترین همسایگی](https://boute.s3.amazonaws.com/192-KNN.png) در ادامه پس ازاین که توسط روش خاص خود به نازک سازی[^Thinning] حروف (پیش پردازش) -که در [5] توضیح بیشتری داده شده- پرداخت، نوبت به توضیح چگونگی تقسیمبندی و تشکیل ماتریس متناظر میرسد. اگر تصویر ورودی را X بگیریم و پیکسلهای پسزمینه را یک بگذاریم و پیکسلهای پیش زمینه را صفر بگذاریم، ابتدا x و y بیشینه و کمینه را بهدست آورده تا تصویر را محدود به حرف مورد نظر کنیم. سپس از تصویر به دست آمده به تشکیل ماتریس $ X_E $ میپردازیم. سپس به تقسیمبندی ماتریس بدست آمده به خانههای هم اندازه میپردازیم که خانههای به دست آمده $ C_i $ نامگذاری میکنیم، اگر در هر کدام از این خانهها نسبت مقادیر یک به مقادیر صفر را بدست بیاوریم، به ازای هر خانه داریم، $ P_i $ . $$ P_i = \frac {n_w}{n_B} $$ اگر نسبتهای بدست آمده را در یک آرایه به نام $ R_X $ بگذاریم، حال آرایهای داریم که مشخصهی هر حرف میباشد، که هر کدام را میتوان به نام حروف مورد نظر نام گذاری کرد مثلا آرایهی A،B،C، ... . از این پس، پس از دریافت ورودی به ساخت ماتریس آن مطابق آنچه در بالا ذکر شد، پرداخته و سپس با استفاده از الگوریتم نزدیکترین همسایگی اقدام به دستهبندی آن میکنیم.البته فاصله را بر اساس فرمول اقلیدس میتوان به دست آورد، به طوری که $ X_S $ ماتریس داده مورد بررسی و $ X_T $ دادههایی که قبلا دستهبندی شدهاند و Q همان مقدار P برای دادهی مورد بررسی میباشد. $$ Distance_{Euclidean}(X_{T1} , X_{S1}) = D_1 = \sqrt{\Sigma_{j=1}^N (P_{1,j} - Q_{1,j})^2} $$ در پیادهسازی این روش از نرمافزار متلب استفاده شده و مقدار K=5 در نظر گرفته شده که در آزمایشهای مختلف مطابق تصویر شماره 7 به تقریبا 95 درصد تشخیص درست دست یافتهاند.[5] ![تصویر شماره 6 - نمونه ای از داده های مورد بررسی و ساخت ماتریس آن](https://boute.s3.amazonaws.com/192-knn4.png) ![تصویر شماره 7 - میانگین درصد تشخیص درست](https://boute.s3.amazonaws.com/192-knn3.png) ## ابزارهای موجود + **موتور Tesseract :** این موتور تشخیص متن، ابتدا سطرها را شناسایی میکند (سطرهای زاویهدار را هم شناسایی میکند و تغییر زاویهای در آنها به وجود نمیآورد که باعث میشود از کیفیت تصویر کاسته نشود). پس از شناسایی سطرها و ایجاد خطوط و مسیر در اطراف حروف نوبت به جداسازی حروف میرسد، بدین ترتیب که نقاطی که تشخیص داده میشود محل جدا شدن هستند را انتخاب میکند مثلا نقاطی که در قسمتهای مقعر هستند، تصویر زیر کلمهای را میبینیم که بر روی آن نقاط انتخابی با پیکان نشان داده شدهاند. ![تصویر شماره 8 - پیکان ها نشان دهنده ی نقاط انتخابی برای برش هستند](https://boute.s3.amazonaws.com/192-tesseract.png) سپس از نقاط مختلف برش را انجام میدهد تا به بهترین حالت دست یابد. بعد از این، نوبت دستهبندی میباشد، در Tesseract دستهبندیها بر اساس شکل چندضلعیها و خطوط ایجاده شدهی اطراف حروف میباشد و پس از مقایسه با دادههایی که قبلا دستهبندی شدهاند با روشی مشابه K-NN دستهی متناسب را برمیگزیند. تفاوت Tesseract با ابزار و روشهای دیگر نحوهی دستهبندی آن میباشد که فونت های مختلف را میتواند شناسایی کند و ویژگیهای دیگر آن، به استفاده از واژهنامهها به عنوان پس پردازش میتوان اشاره کرد.[7] + **کتابخانه [OpenCV](http://opencv.org/) :** این کتابخانه علاوه بر تشخیص متن، کارهایی چون تشخیص چهره، تشخیص حرکت در دوربین و فیلم، خواندن پلاک خودرو و کارهای زیادی که مربوط به پردازش تصویر میشود را انجام میدهد و ادعا شده که دارای 2500 الگوریتم بهینه برای کارهای مختلفش میباشد. این کتابخانه برای تشخیص متن از الگوریتمهایی چون K-NN و [SVM](http://en.wikipedia.org/wiki/Support_vector_machine)[^Support Vector Machine] استفاده میکند و برای زبانهای برنامه نویسی سی، سی پلاس پلاس، پایتون، جاوا و متلب نوشته شده است. + **ابزارهای آنلاین :** وبسایتهای مختلفی وجود دارند که هر کدام از آنها با الگوریتم خودشان میتوانند تصاویری را که ما بارگذاری میکنیم به متن تبدیل کنند، وبسایتهایی چون [onlineocr](http://www.onlineocr.net/) ، [free-ocr](http://www.free-ocr.com/) ، [newocr](http://www.newocr.com/) و ... ، البته خیلی از آنها از الگوریتمهای پیچیدهای استفاده نکردهاند که به همین دلیل گاهی اوقات جواب نادرست میدهند. تصویر زیر نمونهی تست شده میباشد که هر سه وبسایت جواب متفاوتی دادهاند و فقط یکی از آنها کاملا درست بوده است. ![تصویر شماره 9 - مقایسه وبسایت های مختلف در تشخیص متن](https://boute.s3.amazonaws.com/192-test.jpg) # آزمایشها در این قسمت با انجام آزمایشاتی به میزان تشخیص درست و مقایسهی روشهای مختلف میپردازیم. دو روشی که مورد مقایسه قرار میگیرد، استفاده از دو کتابخانهی pytesser و OpenCV میباشد، که در دو مرحلهی تشخیص حروف و تشخیص کلمات مورد بررسی قرار میگیرند. مجموع دادگان[^Data Set] مورد استفاده برای تشخیص حروف که از [اینجا](http://www.boyter.org/wp-content/uploads/2013/07/training.zip) قابل دانلود میباشد، به صورت تصاویری میباشد که برای هر حرف 200 تصویر وجود دارد که ما از 100 تای اول آن به عنوان دادهی آموزش[^Training] و از 100 تای بقیه به عنوان دادهی مورد آزمایش[^Test] استفاده میکنیم ( از این 100 دادهی آموزشی برای آموزش مرحلهی تشخیص کلمات هم استفاده میشود). مجموع دادگان مورد آزمایش برای تشخیص کلمات که از [اینجا](http://www.iapr-tc11.org/dataset/ICDAR2003_RobustReading/TrialTrain/word.zip) قابل دانلود میباشد، به صورت تصاویری از کلمات در حالات مختلف است که مقدار آنها در فایل xml[^Extensible Markup Language] ضمیمه شده در آن درون تگ image با مشخصهی tag نمایش داده میشود. ##تشخیص حروف + **استفاده از pytesser:** ماژول[^Module] [pytesser](http://code.google.com/p/pytesser/wiki/README) که پروژهی منبع باز[^Open source] گوگل میباشد، از موتور تشخیص حروف Tesseract استفاده میکند. استفاده از آن بدین صورت است که با دادن آدرس تصاویر، دستوری تصاویر را میخواند و دستوری دیگر آن را به صورت رشتهی تشخیص داده شده برمیگرداند. from pytesser import * image = Image.open('directory of image') response = image_to_string(image) در این ماژول با هر بار فراخوانی تابع image_to_string موتور Tesseract فراخوانی میشود که با استفاده از آن میتوان حروف را تشخیص داد( روش کار موتور Tesseract در قسمت قبل توضیح داده شد). در آزمایش صورت گرفته در بین حروف بزرگ انگلیسی، برای هر حرف از100 تا به عنوان دادهی مورد آزمایش استفاده کردیم، یعنی 2600 داده مورد آزمایش که قرار گرفت. در این آزمایش از 2600 دادهی مورد بررسی 1741 مورد به درستی تشخیص داده شد. + **استفاده از OpenCV:** به دلیل این که دادگان ما با استفاده از نام فولدرشان باید دسته بندی شوند، ابتدا با استفاده از دو حلقه تمامی دادهها رو در درون یک آرایهی دو بعدی ذخیره میکنیم. آرایه 26 درایه دارد که هر کدام از این درایهها 200 درایهی دیگر که 200 داده از 26 حرف انگلیسی میباشد را دارند. بصورت زیر ابتدا به ساخت آرایهی کلی میپردازیم. for char in alphabet: for i in range(0,200): j = alphabet.index(char) img[j].append(cv2.imread('./training/training/upper/'+char+'/'+str(i)+'.jpg')) gray[j].append(cv2.cvtColor(img[j][-1],cv2.COLOR_BGR2GRAY)) در این روش ما باید تعدادی داده را به عنوان دادههای آموزشی یا یادگیری معرفی کنیم و تعدادی دادهی دیگر را به عنوان دادهی مورد آزمایش. در اینجا 100 تصویر اول هر حرف را دادهی آموزشی و 100 تصویر دوم را دادهی آزمایشی معرفی میکنیم. سپس به هر کدام از دادهها باید مقداری به عنوان پاسخش بدهیم، بدین منظور که به طور مثال، 100 دادهی اول فولدر اول و 100 دادهی دوم فولدر اول هر دو از یک جنس هستند. train = x[:,:100].reshape(-1,400).astype(np.float32) test = x[:,100:200].reshape(-1,400).astype(np.float32) k = np.arange(26) train_labels = np.repeat(k,100)[:,np.newaxis] test_labels = train_labels.copy() حال، با استفاده از الگوریتم نزدیک ترین همسایگی به دستهبندی دادههای آزمایشی بر اساس دادههایی که قبلا از آنها یاد گرفته، میپردازیم. پس از آن با بررسی مقدار به دست آمده با مقدار متنظر با آن در آرایهی از پیش تعیین شده، میتوان درصد درستی را به دست آورد. knn = cv2.ml.KNearest_create() knn.train(train,cv2.ml.ROW_SAMPLE,train_labels) ret,result,neighbours,dist = knn.findNearest(test,k=1) matches = result==test_labels correct = np.count_nonzero(matches) accuracy = correct*100.0/result.size در این روش از 2600 داده ی مورد آزمایش 2493 مورد به جواب درست رسید. |روش|تعداد دادههای مورد آزمایش|تعداد تشخیص درست|درصد درستی| |:----------|:------------------:|:------------------:|:--------:| |pytesser | 2600 | 1741 |66.96%| | OpenCV | 2600 | 2498 |95.88%| ##تشخیص کلمات + **استفاده از pytesser:**همانطور که ذکر شد دادگان مورد استفاده برای تشخیص کلمات، تصاویری هستند که مقدار هر تصویر در فایلی xml ذخیره شده است. در ابتدا باید مقدار هر تصویر را در آرایهای ذخیره نماییم. ![تصویر شماره 10 - قسمتی از فایل xml حاوی اطلاعات تصاویر](http://uupload.ir/files/9am2_xml.png) ابتدا توسط ماژول xml.dom که برای استفاده از فایلهای xml میباشد، سعی در خواندن فایل word.xml داریم. همانگونه که ذکر شد، هر تصویر داخل تگ image میباشد و مقدار آنها در مشخصهی tag نوشته شده است. با استفاده از کد زیر میتوان مقدار هر تصویر را در آرایهای که شماره هر خانهی آن متناسب با نام تصویر آن میباشد، ذخیره نمود. from xml.dom import minidom response = [] doc = minidom.parse("word.xml") images = doc.getElementsByTagName("image") for image in images: word = image.getAttribute("tag") response.append(word) در این آزمایش صورت گرفته از بین 1100 دادهی مورد آزمایش، 522 کلمه درست تشخیص داده شد. + **استفاده از OpenCV:** در این روش ابتدا همانند تشخیص حروف، به تولید محتوای مربوط به دادهی مورد آموزشی میپردازیم. سپس برای پاسخ بهتر لازم است مراحل پیش پردازش را طی کنیم، این مراحل شامل سیاه و سفید کردن تصاویر، تار کردن[^Blurring] تصویر ( به منظور پر کردن برخی از فضاهای خالی نامناسب )، ... و در نهایت جداسازی[^Segmentation] حروف کلمات میباشد. بزرگترین مانع این قسمت، جداسازی حروف میباشد که در اکثر موارد به جواب نادرست منتهی میشود و طبق مراحل قبل میدانیم که پس از جداسازی صحیح حروف میتوان به نتایج مطلوبی رسید. img = cv2.imread('Directory of image') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) gray = cv2.medianBlur(gray,5) ret3,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) thresh_color = cv2.cvtColor(thresh,cv2.COLOR_GRAY2BGR) thresh = cv2.erode(thresh,None,iterations = 1) thresh,contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) ![تصویر شماره 11 - نمونه جداسازی درست ](http://uupload.ir/files/hfa1_corr.jpg) ![تصویر شماره 12 - نمونه جداسازی نادرست](http://uupload.ir/files/7bqt_err.jpg) حال با جداسازی و به دست آوردن حدفاصلها، دستهبندی آن قسمت با استفاده از روش نزدیک ترین همسایگی را انجام میدهیم. سپس مقادیر بدست آمده را در یک رشته ذخیره کرده و با درایهی متناظر آن چک میشود تا درصد درستی محاسبه شود. البته در این قسمت به دلیل مشکل جداسازی حروف در اکثر قریب به اتفاق تصاویر، نبود دادهی مناسب برای یادگیری ( دادههای یادگیری فقط حاوی حروف بزرگ انگلیسی بود و اعداد و کاراکترهای خاص را نداشت) و ... تنها از بین 1100 دادهی مورد آزمایش به 11 جواب درست رسید! roi = cv2.resize(roi,(20,20)) roi = roi.reshape((1,400)) roi = np.float32(roi) retval, results, neigh_resp, dists = knn.findNearest(roi, k = 1) word += chr(results) ![تصویر شماره 13 - یکی از دادههایی که به جواب درست منجر شده](http://uupload.ir/files/1ood_corr1.jpg) |روش|تعداد دادههای مورد آزمایش|تعداد تشخیص درست|درصد درستی| |:----------|:------------------:|:------------------:|:--------:| |pytesser | 1100 | 522 |47.45%| | OpenCV | 1100 | 11 |1%| کدهای پروژه در سایت [گیتهاب](http://github.com/AminMSD/English_OCR.git) در دسترس میباشد. در مرحلهی بعد برای بهبود نتایج، باید مرحله جداسازی را بهبود بخشیم و دادگان آموزشی بیشتری را جمع آوری کنیم تا به جواب درستتری دست یابیم. # کارهای آینده در این فاز به رفع مشکلاتی که باعث درصد تشخیص درستی پایینی شده بودند، میپردازیم. همان طور که قبلا هم گفته شده بود با مشکلاتی چون استفاده نکردن از دادههای یادگیری مناسب و مشکل عمده در جداسازی حروف مواجه بودیم. + **افزودن دادههای بیشتر به دادههای یادگیری:** با استفاده از کدی که قبلا برای یادگیری دادهها استفاده میکردیم، دادههای جدیدی را اضافه کردیم ولی در نتایج تغییر چندانی مشاهده نشد. + **رفع مشکل جداسازی:** در قسمت تشخیص حروف، روش ما به درستی جواب داده بود و به درصد درستی بسیار خوبی رسیده بودیم، اما در تشخیص کلمات یکی از بزرگترین اشکالات جداسازی نادرست حروف در کلمات بود. با بررسی و مقایسهی چند مورد از دادههایی که تشخیص درست داده بود و دادههایی که تشخیص نادرست داده بود، متوجه شدیم که تصاویری که پس زمینهی آنها رنگی تیره و رنگ متن روشن بود، به درستی جداسازی میشود. مشکل ما با تصاویری بود که پس زمینهی آنها رنگی روشن و رنگ متن آن تیره می باشد. برای رفع این مشکل، به جای این که با استفاده از کد زیر تصاویر را سیاه و سفید کنیم: tret3,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) کد برعکس سیاه و سفید کردن را هم در هر مرحله تست کردیم، بدین صورت که در هر مرحله علاوه بر انجام فرایند برای تصویر سیاه و سفید، برای تصویر سیاه و سفیدی که رنگ ها معکوس شده بودند هم انجام میشد. پس از بررسی هر دو آزمایش، هر کدام که به نتیجهی درستی منجر میشود را اعلام میکند. در تصویر زیر چند نمونه از تصاویری که در روش قبل به اشتباه جداسازی شده بودند و در این روش به درستی جداسازی شدهاند را مشاهده میکنید: tret3,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) ![تصویر شماره 14 - نمونه ای از محدوده بندی ها پس از رفع مشکل](http://uupload.ir/files/jw1_err1.jpg) + **رفع مشکلات محدودهها[^Contours] :** حتی با رفع این مشکل گفته شده باز هم نتیجهی کار، چندان تغییری نکرد. با بررسی بیشتر متوجه شدیم که برخی از محدودههای جداسازی داخل یکدیگر قرار میگیرند که باعث ایجاد مشکل در تشخیص و جداسازی میشود. مانند تصویر زیر: ![تصویر شماره 15 - نمونه ای از مشکل محدوده ها](http://uupload.ir/files/jmca_1086eg.jpg) برای رفع این مشکل، مد مکانیابی محدودهها را از حالت معمولی به حالت EXTERNAL تغییر دادیم. این حالت باعث میشود که اگر محدودههایی درون یکدیگر قرار گرفتهاند، محدودهی بیرونی انتخاب شود. thresh,contours,hierarchy=cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) با انجام این تقریبا مشکلات جداسازی حل شد ولی باز هم نتایج تغییر چندانی نکرد. با دقت و جستجوی بیشتر پی بردیم که این کتابخانه برای انتخاب محدودهها ترتیب خاصی قائل نیست، پس برای رفع این مشکل هم مستطیلهای بدست آمده از انتخاب محدودهها را بر اساس محور xها مرتب کردیم، طبق کد زیر: rects = [] for cnt in contours: x,y,w,h = cv2.boundingRect(cnt) rects.append([x,y,w,h]) rects.sort(key = lambda rects:rects[0]) با انجام این کار تغییرات خیلی بیشتری در نتایج حاصل شد که در جدول زیر گزارش شده است: |روش|تعداد دادههای مورد آزمایش|تعداد تشخیص درست|درصد درستی| |:----------|:------------------:|:------------------:|:--------:| | OpenCV | 1100 | 148 |13.45%| حال اگر به عنوان نتیجه گیری بخواهیم خلاصهای از روند کار را ذکر کنیم، بدین صورت است که در ابتدا با انجام پیش پردازشها بر روی تصاویر، تعدادی از دادهها را به عنوان دادههای یادگیری به الگوریتم داده، سپس الگوریتم با جداسازی حروف در کلمات هرکدام از آنها را با دستههای مختلف دادههای یادگیری مقایسه و دستهبندی میکند و نتیجه را گزارش میدهد. در نهایت به عنوان پس پردازش هم میتوان کلمات به دست آمده را با مجموع کلمات موجود در پایگاه داده مقایسه کرد و نزدیکترین کلمه را به کلمهی تشخیص داده شده انتخاب کرد. # مراجع [1] http://en.wikipedia.org/wiki/Optical_character_recognition [2] http://www.nicomsoft.com/optical-character-recognition-ocr-how-it-works [3] Maya R. Gupta, Nathaniel P. Jacobson, Eric K. Garcia "OCR binarization and image pre-processing for searching historical documents" [2006] [4] EUGEN-DUMITRU TĂUTU and FLORIN LEON "OPTICAL CHARACTER RECOGNITION SYSTEM USING SUPPORT VECTOR MACHINES" [2012] [5] Mohammad Imrul Jubair, Prianka Banik "A Simplified Method for Handwritten Character Recognition from Document Image" [6] Faisal Mohammad, Jyoti Anarase, Milan Shingote, Pratik Ghanwat "Optical Character Recognition Implementation Using Pattern Matching" [2014] [7] Ray Smith "An Overview of the Tesseract OCR Engine" [2007] #لینکهای مفید + [ماژول pytesser](https://code.google.com/p/pytesser/) + [کتابخانهی OpenCV برای پایتون](https://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_tutorials.html) + [نصب numpy برای روی ویندوز 64 بیت](https://gehrcke.de/2015/02/how-to-set-up-a-64-bit-version-of-numpy-on-windows/) + [مجموع دادگان تشخیص حروف](http://www.boyter.org/wp-content/uploads/2013/07/training.zip) + [مجموع دادگان تشخیص کلمات](http://www.iapr-tc11.org/dataset/ICDAR2003_RobustReading/TrialTrain/word.zip) + [لینک پروژه در گیتهاب](https://github.com/AminMSD/English_OCR.git)