خلفية
بيان المشكلة
أهداف
نِطَاق
تحميل وإعداد الصور
تنسيق الصور وتحويلها
استخراج الميزة واختيارها
الانحدار اللوجستي
التحليل التمييزي الخطي
K أقرب الجيران
أشجار القرار
غابة عشوائية
نايف بايز
آلة دعم المتجهات
مقاييس الدقة والأداء
مقارنة نماذج التعلم الآلي
القيود والتحديات
ملخص الإنجازات
المساهمات والأهمية
العمل المستقبلي والتحسينات
تشكل أمراض النباتات تهديدات كبيرة للإنتاجية الزراعية، مما يؤدي إلى خسائر في الغلة وصعوبات اقتصادية للمزارعين. يعد الكشف الدقيق وفي الوقت المناسب عن أمراض النباتات أمرًا بالغ الأهمية لتنفيذ استراتيجيات فعالة لإدارة الأمراض وتقليل تلف المحاصيل. يمكن أن تكون الطرق اليدوية التقليدية لتشخيص الأمراض مستهلكة للوقت، وغير موضوعية، وعرضة للخطأ. ولذلك، فقد برز تكامل التكنولوجيا، مثل التعلم الآلي ومعالجة الصور، كنهج واعد لأتمتة وتعزيز الكشف عن الأمراض النباتية.
الهدف الرئيسي من هذا المشروع هو تطوير نظام للكشف عن أمراض النبات باستخدام خوارزميات التعلم الآلي وتقنيات معالجة الصور. ويهدف النظام إلى تصنيف أوراق النباتات بدقة على أنها صحية أو مريضة من خلال تحليل الصور الرقمية للأوراق. من خلال أتمتة عملية الكشف، يمكن للمزارعين والخبراء الزراعيين تحديد ومعالجة الأمراض النباتية على الفور، مما يتيح التدخلات في الوقت المناسب وتحسين ممارسات إدارة المحاصيل
الأهداف الأساسية لهذا المشروع هي كما يلي
تطوير نظام قوي ودقيق للكشف عن الأمراض النباتية
تنفيذ خوارزميات التعلم الآلي للتصنيف الآلي لأوراق النباتات
استخدم تقنيات معالجة الصور لاستخراج الميزات ذات الصلة من صور الأوراق
تقييم أداء ودقة نماذج التعلم الآلي المختلفة
توفير واجهة سهلة الاستخدام للتفاعل السهل والبديهي مع النظام
يركز هذا المشروع على الكشف عن الأمراض النباتية وتحديداً في أوراق التفاح. يتم الحصول على مجموعة البيانات المستخدمة لتدريب النماذج واختبارها من مجموعة بيانات Plant-Village، التي تحتوي على صور لأوراق التفاح الصحية والأوراق المتضررة من أمراض مثل Apple Scab وBlack Rot وCedar Apple Rust. ويهدف النظام إلى تحقيق دقة عالية في تصنيف الأمراض وتوفير أداة عملية للمزارعين والمهنيين الزراعيين لتحديد وإدارة الأمراض النباتية بشكل فعال. لا يغطي المشروع اكتشاف الأمراض في الوقت الفعلي في الميدان أو دمج الأجهزة للحصول على الصور
تشتمل مجموعة البيانات المستخدمة لنظام الكشف عن الأمراض النباتية على صور لأوراق التفاح التي تم الحصول عليها من مجموعة بيانات القرية النباتية. تم تنظيم مجموعة البيانات في أربع فئات رئيسية تمثل فئات مختلفة من حالات أوراق التفاح Apple___Apple_scab وApple____Black_rot وApple___Cedar_apple_rust وApple____healthy
Apple___Apple_scab: تحتوي هذه الفئة على 630 صورة، منها 598 صورة مخصصة للتدريب و32 صورة للاختبار
Apple___Black_rot: تتضمن مجموعة البيانات 621 صورة في هذه الفئة، مع تخصيص 589 صورة للتدريب و32 صورة للاختبار
Apple___Cedar_apple_rust: تتكون مجموعة البيانات من 275 صورة للأوراق المتضررة من صدأ تفاح الأرز، مع 261 صورة مستخدمة للتدريب و14 صورة للاختبار
Apple___صحية: تحتوي هذه الفئة على 1645 صورة لأوراق التفاح الصحية. ومن بين هذه الصور، تم تخصيص 1562 صورة للتدريب، و83 صورة محجوزة للاختبار.
يتم استخدام صور التدريب لتعليم نماذج التعلم الآلي التعرف على الأنماط والتمييز بين الأوراق السليمة والمريضة. يتم استخدام صور الاختبار لتقييم أداء ودقة النماذج المدربة على البيانات غير المرئية. من خلال الاستفادة من مجموعة البيانات المتنوعة هذه، يهدف نظام الكشف عن أمراض النبات إلى تصنيف أوراق التفاح بدقة على أنها صحية أو متأثرة بأمراض مثل جرب التفاح أو العفن الأسود أو صدأ التفاح الارز. يتيح تكوين مجموعة البيانات للنظام التعلم من مجموعة واسعة من حالات الأوراق وتحسين قدرته على تعميم الأمراض النباتية وتحديدها بدقة
تحميل وإعداد الصور
في سياق مشروع الكشف عن مرض أوراق التفاح، تتمثل الخطوة الأولى في الحصول على مجموعة بيانات تتكون من صور لأوراق التفاح المصابة بأمراض مختلفة. يتم بعد ذلك تحميل هذه الصور في النظام لتسهيل الوصول إليها لمزيد من المعالجة. بالإضافة إلى ذلك، يتم إعداد الصور عن طريق إجراء التعديلات الضرورية مثل تغيير حجمها إلى دقة متسقة، أو اقتصاص الأجزاء غير الضرورية، أو تطبيع توزيع الألوان. تنسيق الصور وتحويلها. بمجرد تحميل صور أوراق التفاح، يجب تنسيقها وتحويلها لضمان التوافق مع المراحل اللاحقة للمشروع. يتضمن ذلك توحيد تنسيق الصورة عن طريق تحويلها إلى نوع ملف معين مثل JPEG أو PNG. علاوة على ذلك، قد يتم إجراء تعديلات على دقة مساحة اللون، أو سمات الصورة الأخرى لضمان الاتساق وتسهيل التحليل الدقيق
استخراج الميزة واختيارها
يعد استخراج الميزة خطوة حاسمة في اكتشاف الأمراض في أوراق التفاح. يتم استخدام تقنيات مختلفة لاستخراج الميزات ذات الصلة من صور الأوراق. تتضمن هذه التقنيات تحليل النسيج لالتقاط الأنماط التركيبية المرتبطة بالأمراض، وفحص اللون لتحديد الاختلافات المرتبطة بأمراض معينة، ودراسة الشكل لاكتشاف المخالفات في مورفولوجيا الأوراق. ومن خلال استخلاص هذه السمات المميزة، يمكن لخوارزميات التعلم الآلي اللاحقة أن تفرق بشكل فعال بين أوراق التفاح السليمة والمريضة
اختيار الميزة
تتضمن هذه الخطوة اختيار مجموعة فرعية من الميزات المستخرجة بناءً على أهميتها وقوتها التمييزية. يساعد تحديد الميزة على تقليل أبعاد مجموعة البيانات عن طريق التخلص من التشويش أو المعلومات الزائدة عن الحاجة. ومن خلال اختيار الميزات الأكثر إفادة، يمكن تحسين كفاءة ودقة نموذج الكشف عن المرض
يستخدم مشروع الكشف عن مرض أوراق التفاح مجموعة من خوارزميات التعلم الآلي لتطوير نموذج فعال لتصنيف الأمراض. يتم استخدام الخوارزميات التالية
الانحدار اللوجستي: يستخدم الانحدار اللوجستي للتنبؤ باحتمالية أن تكون ورقة التفاح صحية أو مريضة بناءً على الميزات المستخرجة
التحليل التمييزي الخطي: يساعد التحليل التمييزي الخطي في تصنيف أوراق التفاح من خلال إيجاد مجموعة خطية من الميزات التي تفصل بين العينات السليمة والمريضة بشكل أفضل
K أقرب الجيران (KNN): يصنف K أقرب الجيران أوراق التفاح من خلال مقارنة ميزاتها بتلك الخاصة بأقرب الجيران في مساحة الميزات
أشجار القرار: تستخدم أشجار القرار سلسلة من شروط if-else لتصنيف العينات بناءً على ميزاتها وعلاقاتها الهرمية
الغابة العشوائية: الغابة العشوائية هي طريقة تعلم جماعية تجمع بين أشجار القرار المتعددة لتعزيز دقة التصنيف
Naïve Bayes: Naïve Bayes هي خوارزمية احتمالية تحسب احتمالية انتماء ورقة تفاح إلى فئة مرضية معينة
آلة دعم المتجهات (SVM): تقوم آلة دعم المتجهات ببناء طائرات مفرطة في مساحة ميزات عالية الأبعاد لتصنيف أوراق التفاح
بعد اختيار خوارزميات التعلم الآلي، يتم تدريب النماذج باستخدام مجموعة بيانات مصنفة تتكون من صور أوراق التفاح مع تسميات الأمراض المقابلة. تتعلم النماذج كيفية التعرف على الأنماط والعلاقات بين الميزات وفئات الأمراض خلال مرحلة التدريب هذه. ولضمان موثوقية النماذج وتعميمها، يتم تنفيذ عملية التحقق من الصحة. يتم تقييم النماذج المدربة باستخدام مجموعة بيانات التحقق المنفصلة التي لم يتم استخدامها أثناء التدريب. ويساعد هذا في تقييم قدرة النماذج على تصنيف عينات أوراق التفاح غير المرئية بدقة
بمجرد تدريب النماذج والتحقق من صحتها، يتم اختبارها على مجموعة بيانات اختبار منفصلة تحتوي على صور جديدة غير مرئية لأوراق التفاح. تتنبأ النماذج بفئة المرض لكل عينة، ويتم حساب مقاييس تقييم الأداء مثل الدقة والدقة والاستدعاء ودرجة F1 لقياس فعالية النماذج في الكشف عن المرض
في [1]:
# -----------------------------------# استخراج الميزات العالمية # --------- -------------------------- من sklearn.preprocessing استيراد LabelEncoderfrom sklearn.preprocessing import MinMaxScalerimport numpy as npimport mahotasimport cvimport osimport h5py# ----- ---------------# tunable-parameters# --------------------images_per_class = 800fixed_size = tuple(( 500 , 500 ))train_path = "../dataset/train"test_path = "../dataset/test"h5_train_features = "../embeddings/features/features.h5"h5_train_labels = "../embeddings/labels/labels. h5"bins = 8##### BGR To RGB ConversionIn [2]:# تحويل كل صورة إلى RGB من BGR formatdef rgb_bgr(image):rgb_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)return rgb_img##### RGB إلى HSV (قيمة تشبع اللون) ConversionIn [3]:# التحويل إلى تنسيق صورة HSV من RGBdef bgr_hsv(rgb_img) ):hsv_img = cv2.cvtColor(rgb_img, cv2.COLOR_RGB2HSV)return hsv_img##### Image SegmentationIn [4]:# لاستخراج اللون الأخضر والبني def img_segmentation(rgb_img, hsv_img):lower_green = np.array([ 25 , 0 , 20) ])upper_green = np.array([ 100 , 255 , 255 ])healthy_mask = cv2.inRange(hsv_img, Lower_green, Upper_green)result = cv2.bitwise_and(rgb_img, rgb_img, Mask=healthy_mask)lower_brown = np.array([ 10 , 0 , 10) ])upper_brown = np.array([ 30 , 255 , 255 ])disease_mask = cv2.inRange(hsv_img, Lower_brown, Upper_brown)disease_result = cv2.bitwise_and(rgb_img, rgb_img, Mask=disease_mask)final_mask = Healthy_mask + Disease_maskfinal_result = cv2.bitwise_and(rgb_img, rgb_img, Mask=final_mask)return Final_result##### تحديد واصفات الميزات###### 1.Hu MomentsIn [5]:# feature-descriptor-1: Hu Momentsdef fd_hu_moments( صورة):صورة = cv2.cvtColor(صورة، cv2.COLOR_BGR2GRAY)feature = cv2.HuMoments(cv2.moments(image)).flatten()return feature###### 2. Haralick TexturesIn [6]:# feature-descriptor-2: Haralick Texturedef fd_haralick(image) :رمادي = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)haralick = mahotas.features.haralick(gray).mean(axis= 0)return haralick###### 3. Color HIstogramIn [7]:# feature-descriptor-3: Colour Histogramdef fd_histogram(image, Mask=None):image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)hist = cv2.calcHist( [صورة]، [ 0 ، 1 ، 2 ]، لا شيء، [صناديق، صناديق، صناديق]، [ 0 , 256 , 0 , 256 , 0 ,256 ] )cv2.normalize(hist, hist)return hist.flatten()##### تحميل مجموعة بيانات التدريب في [8]:# الحصول على ملصقات التدريبstrain_labels = os.listdir(train_path)# فرز ملصقات التدريبstrain_labels.sort() print(train_labels)# قوائم فارغة للاحتفاظ بمتجهات الميزات والتسمياتglobal_features = []labels = [] ['Apple___Apple_scab', 'Apple___Black_rot', 'Apple___Cedar_apple_rust','Apple___healthy']##### إنشاء الميزات وتضمينات الملصقات من مجموعة البيانات في [9]:# حلقة فوق المجلدات الفرعية لبيانات التدريب لـ Training_name في Train_labels:# join مسار بيانات التدريب ومجلد تدريب كل نوعimg_dir_path = os.path.join(train_path, Training_name)# احصل على تسمية التدريب الحاليةcurrent_label = Training_name# قم بتكرار الصور في كل مجلد فرعي لـ img في os.listdir(img_dir_path):# احصل على ملف الصورة namefile = os.path.join( img_dir_path, img)# اقرأ الصورة وقم بتغيير حجمها إلى حجم ثابتimage = cv2.imread(file)image = cv2.resize(image,fix_size)# تشغيل البت الوظيفي بواسطة BitRGB_BGR = rgb_bgr(image)BGR_HSV = bgr_hsv(RGB_BGR)IMG_SEGMENT = img_segmentation(RGB_BGR, BGR_HSV)# دعوة لواصفات الميزات العالميةfv_hu_moments = fd_hu_moments(IMG_SEGMENT)fv_haralick = fd_haralick(IMG_SEGMENT)fv_histogram = fd_histogram(IMG_SEGMENT)# تسلسل الميزات العالميةglobal_feature = np.hstack([fv_histogram, fv_haralick,fv_hu_moments])# تحديث قائمة التسميات والميزات Vectorslabels.append(current_label)global_features.append(global_feature)print("المجلد الذي تمت معالجته [الحالة]: {}".format(current_label))طباعة("[الحالة] اكتمل استخراج الميزات العالمية...") [الحالة] المجلد الذي تمت معالجته: Apple___Apple_scab [الحالة] المجلد الذي تمت معالجته: Apple___Black_rot[الحالة] المجلد الذي تمت معالجته: Apple___Cedar_apple_rust[الحالة] المجلد الذي تمت معالجته: Apple___healthy[الحالة] أكمل استخراج الميزات العامة...في [10]:# print(global_features)في [ 41]:# احصل على حجم ناقل الميزة الإجماليprint("[STATUS] feature Vector الحجم {}".تنسيق (np.array(global_features).shape)) [الحالة] حجم ناقل الميزة (3010، 532)في [12]:# احصل على حجم تسمية التدريب الإجمالي# print(labels)print("[STATUS] ملصقات التدريب {}".format(np.array(labels).shape )) [الحالة] تسميات التدريب (3010،)
تسمية القيمة المشفرةApple___Apple_scab 0Apple___Black_rot 1Apple___Cedar_apple_rust 2Apple____healthy 3
في [13]:
targetNames = np.unique(labels) le = LabelEncoder() target = le.fit_transform(labels) print(targetNames) print("[STATUS] تسميات التدريب مشفرة...") ['Apple___Apple_scab' 'Apple___Black_rot' 'Apple___Cedar_apple_rust' ' Apple___healthy'] [الحالة] تم ترميز تسميات التدريب...
في [14]:
من sklearn.preprocessing استيراد MinMaxScalerscaler = MinMaxScaler(feature_range=( 0 , 1 ))rescaled_features =scaler.fit_transform(global_features)print("[STATUS] ناقل الميزة الذي تم تطبيعه...")rescaled_features[STATUS] ناقل الميزة الذي تمت تطبيعه...خارج [14]:مصفوفة([[0.8974175، 0.03450962، 0.01845123، ...، 0.02027887، 0.12693291،0.96573218]، [0.89815922، 0.13025558، 0.02774864، ...، 0.02027767، 0.12692423،0.96573354]، [0.56777027، 0.، 0.01540143، ...، 0.02027886، 0.12693269،0.96573218]، ...، [0.95697685، 0.01228793، 0.00548476، ...، 0.02027886، 0.12693346،0.96573218]، [0.97704002، 0.10614054، 0.03136325، ...، 0.02027885، 0.12692424،0.96573217]، [0.95214074، 0.03819411، 0.03671892، ...، 0.02027886، 0.12692996،0.96573217]])print("[الحالة] التسميات المستهدفة: {}".format(target))print("[STATUS] شكل التسميات المستهدفة: {}".تنسيق(target.shape)) [الحالة] تسميات الهدف: [0 0 0 ... 3 3 3] [الحالة] شكل تسميات الهدف: (3010،)
أ. سمات
h5f_data = h5py.File(h5_train_features, "w")h5f_data.create_dataset("dataset_1", data=np.array(rescaled_features))Out[16]:<HDF5 dataset "dataset_1": الشكل (3010, 532)، اكتب " <f8">
h5f_label = h5py.File(h5_train_labels, "w")h5f_label.create_dataset("dataset_1", data=np.array(target))Out[17]:<مجموعة بيانات HDF5 "dataset_1": الشكل (3010،)، اكتب "< i8">في [43]:h5f_data.إغلاق()h5f_label.إغلاق()
# -----------------------------------# تدريب النموذج الخاص بنا # --------- -------------------------- استيراد h5pyimport numpy كـ npimport osimport cvimport تحذيرات من matplotlib استيراد pyplotfrom sklearn.model_selection import Train_test_split, cross_val_scorefrom sklearn.model_selection import KFold, استيراد StratifiedKFoldfrom sklearn.metrics ارتباك_مصفوفة، دقة_النتيجة، تصنيف_تقريرمن sklearn.linear_model استيراد الانحدار اللوجستي من sklearn.tree استيراد DecisionTreeClassifier من sklearn.ensemble استيراد RandomForestClassifier من sklearn.neighbors استيراد KNeighborsClassifier من sklearn.discriminant_analyse استيراد تحليل خطي من sklearn.naive_bayes GaussianNBfrom sklearn.svm import SVCimport joblibwarnings.filterwarnings("ignore")# --------------------# tunable-parameters# ---------- ---------- num_trees = 100test_size = 0.seed = 9scoring = "accuracy"# احصل على ملصقات التدريبstrain_labels = os.listdir(train_path)# قم بفرز ملصقات التدريبstrain_labels.sort()if not os.path.exists(test_path):os.makedirs(test_path)# إنشاء جميع نماذج التعلم الآليmodels = []models.append(("LR", LogisticRegression(random_state=seed)))models.append(("LDA" ، تحليل التمييز الخطي()))models.append(("KNN", KNeighborsClassifier()))models.append(("DTC", DecisionTreeClassifier(random_state=seed)))models.append(("RF", RandomForestClassifier(n_estimators=num_trees,random_state=seed)))models.append(("NB", GaussianNB()))models.append(("SVM" "، SVC(random_state=seed)))# متغيرات للاحتفاظ بالنتائج والأسماءresults = []names = []# استيراد الميزة ناقلات وملصقات مدربةsh5f_data = h5py.File(h5_train_features, "r")h5f_label = h5py.File(h5_train_labels, "r")global_features_string = h5f_data["dataset_1"]global_labels_string = h5f_label["dataset_1"]global_features = np.array(global_features_string)global_labels = np.array(global_labels_string)h5f_data. Close()h5f_label. Close()# التحقق من شكل ناقل الميزة والتسمياتprint("شكل الميزات [STATUS]: {}".format(global_features. الشكل))طباعة("شكل التسميات [الحالة]: {}".format(global_labels.shape))print("بدأ التدريب [الحالة]...")print(global_labels, len(global_labels), len(global_features)) [الحالة] ميزات الشكل: (3010، 532) [الحالة] شكل التسميات: (3010،) [الحالة] بدأ التدريب... [0 0 0 ... 3 3 3] 3010 3010
في [38]:
(تدريب DataGlobal، testDataGlobal، TrainLabelsGlobal، testLabelsGlobal، ) = Train_test_split(np.array(global_features), np.array(global_labels),test_size=test_size, Random_state=seed)print("[STATUS] بيانات القطار والاختبار المقسمة...")print("بيانات القطار: {} ".format(trainDataGlobal.shape))print("بيانات الاختبار: {}".format(testDataGlobal.shape)) [الحالة] تقسيم بيانات القطار والاختبار...بيانات القطار: (2408, 532)بيانات الاختبار: (602, 532)In [40]:trainDataGlobalOut[40]:array([[9.47066972e-01, 1.97577832e-02) , 5.34481987e-04, ...,2.02788613e-02, 1.26936845e-01, 9.65732178e-01], [9.67673181e-01، 4.20456024e-02، 5.76285634e-02، ...،2.02788294e-02، 1.26933581e-01، 9.65732217e-01]، [9.84705756e-01، 2.97800312e-02، 1.34500344e-02، ...،2.02788553e-02، 1.26941878e-01، 9.65732187e-01]، ...، [8.64347882e-01، 5.89053245e-02، 4.27430333e-02، ...،2.02791643e-02، 1.26961451e-01، 9.65733689e-01]، [9.85818416e-01، 1.47428536e-03، 3.35008392e-03، ...،2.02767694e-02، 1.26792776e-01، 9.65732951e-01]، [9.93152188e-01، 1.31020292e-03، 8.50637768e-04، ...،2.02910354e-02، 1.27475382e-01، 9.65721108e-01]])
في [22]:
بالنسبة للاسم، النموذج في النماذج:kfold = KFold(n_splits= 10)cv_results = cross_val_score(model, TrainDataGlobal, TrainLabelsGlobal, cv=kfold,scoring=scoring)results.append(cv_results)names.append(name)msg = "%s : %f (%f)" % (الاسم، cv_results.mean(), cv_results.std())print(msg)LR: 0.900346 (0.020452)LDA: 0.892038 (0.017931)KNN: 0.884978 (0.019588)عربة التسوق: 0.886210 (0.014771)RF: 0.967191 (0.012676)ملاحظة: 0.839293 (0.014065)SVM: 0.885813 (0.021190)
في [23]:
Fig = pyplot.figure()fig.suptitle("مقارنة خوارزمية التعلم الآلي")ax =Fig.add_subplot(111)pyplot.boxplot(results)ax.set_xticklabels(names)pyplot.show()
من النتيجة أعلاه يمكننا أن نرى أن نموذج Random Forest Classifier يتمتع بأعلى دقة بنسبة 96.7% ونموذج Gaussian NB لديه أقل دقة بنسبة 83.9%.
<div class="highlight Highlight-source-python notranslate location-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content=" في [24]: clf = RandomForestClassifier(n_estimators=num_trees, Random_state= بذرة) في [25]: clf.fit(trainDataGlobal, TrainLabelsGlobal) len(trainDataGlobal)، len(trainLabelsGlobal) خارج[25]: (2408، 2408) في [34]: y_predict = clf.predict(testDataGlobal) testLabelsGlobal Out[34]: array([3, 3, 1, 3, 0) , 3, 1, 1, 2, 1, 1, 0, 1, 3, 3, 3, 3, 2, 0, 2, 0, 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, 1, 3, 3, 1, 3, 3, 3, 2, 3, 1, 3, 3, 3, 1, 0, 0, 3, 1, 3, 3, 0, 3, 3, 2, 3, 0, 3, 1, 0, 3, 0, 3, 3, 1, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 2, 1, 1, 2, 0, 2, 1, 1, 0, 0, 3, 2, 0, 3, 2, 3, 3, 2, 3, 3, 1, 1, 3, 2, 0, 2, 1, 1, 2, 3, 3, 3, 1, 1, 0, 3, 0, 3, 3, 0, 3, 3, 3, 1, 2, 3, 2, 3, 0, 3, 0, 3, 1, 3, 3, 3, 3, 3, 2, 1, 0, 1, 3, 3, 3, 1, 3, 3, 0, 0, 3, 3, 3, 0, 2, 3, 3, 0, 1, 1, 3, 0, 0, 3, 1, 3, 3, 1, 3, 2, 1, 0, 0, 3, 0, 1, 0, 1, 0, 1, 2, 3, 3, 3, 3, 3, 2, 1, 1, 3, 3, 1, 3, 1, 3, 2, 1, 3, 3, 0, 3, 0, 3, 3, 3, 1, 1, 3, 2, 3, 0, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 1, 1, 0, 3, 0, 3, 1, 3, 3, 3, 1, 3, 3, 0, 3, 0, 2, 3, 3, 0, 3, 3, 3, 3, 0, 2, 3, 1, 3, 3, 3, 0, 3, 1, 3, 3, 3, 1, 3, 0, 2, 0, 3, 3, 3, 2, 3, 3, 3, 0, 0, 1, 1, 3, 3, 0, 3, 2, 0, 1, 1, 3, 0, 3, 1, 1, 3, 2, 2, 2, 3, 3, 3, 1, 1, 3, 0, 3, 0, 1, 3, 3, 0, 3, 1, 0, 3, 0, 3, 3, 2, 3, 3, 3, 3, 0, 3, 3, 3, 1, 0, 3, 2, 1, 3, 0, 1, 1, 0, 1, 3, 2, 0, 3, 0, 3, 1, 0, 3, 2, 0, 3, 0, 0, 2, 1, 3, 0, 3, 3, 0, 0, 3, 3, 1, 3, 0, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 1, 1, 3, 3, 0, 3, 3, 3, 1, 0, 1, 3, 0, 1, 3, 0, 3, 3, 3, 0, 3, 3, 0, 1, 3, 3, 1, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3, 1, 0, 3, 3, 0, 3, 3, 1, 0, 3, 1, 1, 3, 3, 3, 2, 3, 0, 0, 3, 3, 3, 3, 3, 3, 2, 1, 3, 3, 0, 3, 0, 1, 3, 1, 3, 3, 1, 1, 1, 1, 3, 3, 3, 1, 3, 0, 3, 3, 3, 2, 3, 1, 3, 3, 1, 1, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 3, 1, 1, 0, 3, 3, 3, 3, 0, 3, 1