classic.py
classic
import%20marimo%0A%0A__generated_with%20%3D%20%220.17.2%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22%2C%20auto_download%3D%5B%22html%22%2C%20%22ipynb%22%5D)%0A%0Awith%20app.setup%3A%0A%20%20%20%20%23%20Initialization%20code%20that%20runs%20before%20all%20other%20cells%0A%20%20%20%20import%20os%0A%20%20%20%20from%20collections%20import%20Counter%0A%0A%20%20%20%20import%20cv2%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20seaborn%20as%20sns%0A%20%20%20%20from%20sklearn.model_selection%20import%20train_test_split%0A%20%20%20%20from%20sklearn.preprocessing%20import%20StandardScaler%2C%20LabelBinarizer%0A%20%20%20%20from%20sklearn.ensemble%20import%20RandomForestClassifier%0A%20%20%20%20from%20sklearn.svm%20import%20SVC%0A%20%20%20%20from%20sklearn.metrics%20import%20(%0A%20%20%20%20%20%20%20%20accuracy_score%2C%0A%20%20%20%20%20%20%20%20classification_report%2C%0A%20%20%20%20%20%20%20%20confusion_matrix%2C%0A%20%20%20%20%20%20%20%20ConfusionMatrixDisplay%2C%0A%20%20%20%20%20%20%20%20RocCurveDisplay%2C%0A%20%20%20%20)%0A%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20Model%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.function%0Adef%20preprocessing(image_path%2C%20threshold_value%3DNone)%3A%0A%20%20%20%20image%20%3D%20cv2.imread(image_path)%0A%20%20%20%20gray%20%3D%20cv2.cvtColor(image%2C%20cv2.COLOR_BGR2GRAY)%0A%20%20%20%20blur%20%3D%20cv2.GaussianBlur(gray%2C%20(5%2C%205)%2C%200)%20%20%23%20to%20reduce%20noise%0A%0A%20%20%20%20%23%20threshold%20the%20image%20to%20convert%20to%20binary%0A%20%20%20%20if%20threshold_value%20is%20None%3A%0A%20%20%20%20%20%20%20%20threshold_value%20%3D%20np.max(blur)%20%2F%202%20%2B%20np.min(blur)%20%2F%202%0A%20%20%20%20_%2C%20thresholded_image%20%3D%20cv2.threshold(%0A%20%20%20%20%20%20%20%20blur%2C%20threshold_value%2C%20255%2C%20cv2.THRESH_BINARY_INV%0A%20%20%20%20)%0A%0A%20%20%20%20return%20thresholded_image%0A%0A%0A%40app.function%0Adef%20find_contour(thresholded_image)%3A%0A%20%20%20%20contours%2C%20_%20%3D%20cv2.findContours(%0A%20%20%20%20%20%20%20%20thresholded_image%2C%20cv2.RETR_EXTERNAL%2C%20cv2.CHAIN_APPROX_SIMPLE%0A%20%20%20%20)%0A%0A%20%20%20%20if%20not%20contours%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%0A%20%20%20%20%23%20assume%20the%20largest%20contour%20is%20our%20shape%0A%20%20%20%20contour%20%3D%20max(contours%2C%20key%3Dcv2.contourArea)%0A%0A%20%20%20%20%23%20filter%20out%20tiny%20contours%20that%20are%20likely%20noise%0A%20%20%20%20if%20cv2.contourArea(contour)%20%3C%20100%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%0A%20%20%20%20return%20contour%0A%0A%0A%40app.function%0Adef%20extract_features_from_contour(contour)%3A%0A%20%20%20%20%23%20Hu%20moments%0A%20%20%20%20M%20%3D%20cv2.moments(contour)%0A%0A%20%20%20%20if%20M%5B%22m00%22%5D%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20None%20%20%23%20avoid%20divide-by-zero%0A%0A%20%20%20%20hu_moments%20%3D%20cv2.HuMoments(M).flatten()%0A%20%20%20%20%23%20to%20make%20them%20more%20stable%20and%20comparable%0A%20%20%20%20hu_moments%20%3D%20(%0A%20%20%20%20%20%20%20%20-1%20*%20np.copysign(1.0%2C%20hu_moments)%20*%20np.log10(abs(hu_moments)%20%2B%201e-7)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20corners%0A%20%20%20%20perimeter%20%3D%20cv2.arcLength(contour%2C%20True)%0A%20%20%20%20epsilon%20%3D%20(%0A%20%20%20%20%20%20%20%200.03%20*%20perimeter%0A%20%20%20%20)%20%20%23%203%25%20of%20perimeter%20is%20a%20good%20starting%20point%20for%20approximation%0A%20%20%20%20approx%20%3D%20cv2.approxPolyDP(contour%2C%20epsilon%2C%20True)%0A%20%20%20%20num_corners%20%3D%20len(approx)%0A%0A%20%20%20%20%23%20solidity%0A%20%20%20%20area%20%3D%20M%5B%22m00%22%5D%20%20%23%20cv2.contourArea(contour)%0A%20%20%20%20hull%20%3D%20cv2.convexHull(contour)%0A%20%20%20%20hull_area%20%3D%20cv2.contourArea(hull)%0A%20%20%20%20solidity%20%3D%20float(area)%20%2F%20hull_area%20if%20hull_area%20%3E%200%20else%200%0A%0A%20%20%20%20%23%20aspect%20ratio%0A%20%20%20%20x%2C%20y%2C%20w%2C%20h%20%3D%20cv2.boundingRect(contour)%0A%20%20%20%20aspect_ratio%20%3D%20float(w)%20%2F%20h%20if%20h%20%3E%200%20else%200%0A%0A%20%20%20%20%23%20circularity%0A%20%20%20%20circularity%20%3D%20(4%20*%20np.pi%20*%20area)%20%2F%20(perimeter**2)%20if%20perimeter%20%3E%200%20else%200%0A%0A%20%20%20%20%23%20final%20feature%20vector%0A%20%20%20%20features%20%3D%20np.append(%0A%20%20%20%20%20%20%20%20%5Bnum_corners%2C%20solidity%2C%20aspect_ratio%2C%20circularity%5D%2C%20hu_moments%0A%20%20%20%20)%0A%0A%20%20%20%20return%20features%0A%0A%0A%40app.function%0Adef%20extract_features(image_path)%3A%0A%20%20%20%20thresholded_image%20%3D%20preprocessing(image_path)%0A%20%20%20%20contour%20%3D%20find_contour(thresholded_image)%0A%20%20%20%20if%20contour%20is%20None%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20features%20%3D%20extract_features_from_contour(contour)%0A%0A%20%20%20%20return%20features%0A%0A%0A%40app.function%0A%40mo.cache%0Adef%20load_data(dataset_path%2C%20labels)%3A%0A%20%20%20%20all_features%20%3D%20%5B%5D%0A%20%20%20%20all_labels%20%3D%20%5B%5D%0A%0A%20%20%20%20feature_names%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22num_corners%22%2C%0A%20%20%20%20%20%20%20%20%22solidity%22%2C%0A%20%20%20%20%20%20%20%20%22aspect_ratio%22%2C%0A%20%20%20%20%20%20%20%20%22circularity%22%2C%0A%20%20%20%20%20%20%20%20%22hu1%22%2C%0A%20%20%20%20%20%20%20%20%22hu2%22%2C%0A%20%20%20%20%20%20%20%20%22hu3%22%2C%0A%20%20%20%20%20%20%20%20%22hu4%22%2C%0A%20%20%20%20%20%20%20%20%22hu5%22%2C%0A%20%20%20%20%20%20%20%20%22hu6%22%2C%0A%20%20%20%20%20%20%20%20%22hu7%22%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20for%20label%20in%20mo.status.progress_bar(%0A%20%20%20%20%20%20%20%20labels%2C%20title%3Df%22Loading%20dataset%20of%20%7Blen(labels)%7D%20labels%22%0A%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20label_path%20%3D%20os.path.join(dataset_path%2C%20label)%0A%20%20%20%20%20%20%20%20if%20not%20os.path.isdir(label_path)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Warning%3A%20Label%20directory%20not%20found%3A%20%7Blabel_path%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%0A%20%20%20%20%20%20%20%20for%20filename%20in%20mo.status.progress_bar(%0A%20%20%20%20%20%20%20%20%20%20%20%20os.listdir(label_path)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3Df%22Extracting%20features%20for%20label%20'%7Blabel%7D'%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20remove_on_exit%3DTrue%2C%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20filename.endswith(%22.png%22)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20image_path%20%3D%20os.path.join(label_path%2C%20filename)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20features%20%3D%20extract_features(image_path)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20features%20is%20not%20None%20and%20len(features)%20%3D%3D%20len(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20feature_names%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20all_features.append(features)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20all_labels.append(label)%0A%0A%20%20%20%20df%20%3D%20pd.DataFrame(all_features%2C%20columns%3Dfeature_names)%0A%20%20%20%20df%5B%22label%22%5D%20%3D%20all_labels%0A%0A%20%20%20%20return%20df%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20Evaluation%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20DATASET_DIR%20%3D%20mo.notebook_location()%20%2F%20%22dataset_classic%22%0A%20%20%20%20LABELS%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22parallelogram%22%2C%0A%20%20%20%20%20%20%20%20%22triangle%22%2C%0A%20%20%20%20%20%20%20%20%22pentagon%22%2C%0A%20%20%20%20%20%20%20%20%22rectangle%22%2C%0A%20%20%20%20%20%20%20%20%22square%22%2C%0A%20%20%20%20%20%20%20%20%22circle%22%2C%0A%20%20%20%20%20%20%20%20%22trapezoid%22%2C%0A%20%20%20%20%20%20%20%20%22oval%22%2C%0A%20%20%20%20%20%20%20%20%22semicircle%22%2C%0A%20%20%20%20%20%20%20%20%22rhombus%22%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20data%20%3D%20load_data(DATASET_DIR%2C%20LABELS)%0A%0A%20%20%20%20mo.stop(data.empty%2C%20mo.md(%22**Error**%3A%20No%20data%20was%20loaded.%20Exiting!%22))%0A%20%20%20%20mo.output.append(%0A%20%20%20%20%20%20%20%20mo.md(f%22Successfully%20loaded%20and%20processed%20**%7Blen(data)%7D**%20samples.%22)%0A%20%20%20%20)%0A%20%20%20%20return%20LABELS%2C%20data%0A%0A%0A%40app.cell%0Adef%20_(data)%3A%0A%20%20%20%20X%20%3D%20data.drop(%22label%22%2C%20axis%3D1)%0A%20%20%20%20y%20%3D%20data%5B%22label%22%5D%0A%0A%20%20%20%20X_train%2C%20X_test%2C%20y_train%2C%20y_test%20%3D%20train_test_split(%0A%20%20%20%20%20%20%20%20X%2C%0A%20%20%20%20%20%20%20%20y%2C%0A%20%20%20%20%20%20%20%20test_size%3D0.2%2C%0A%20%20%20%20%20%20%20%20random_state%3D42%2C%0A%20%20%20%20%20%20%20%20stratify%3Dy%2C%20%20%23%20to%20ensure%20all%20classes%20are%20represented%20in%20train%2Ftest%20splits%0A%20%20%20%20)%0A%20%20%20%20return%20X%2C%20X_test%2C%20X_train%2C%20y_test%2C%20y_train%0A%0A%0A%40app.cell%0Adef%20_(X)%3A%0A%20%20%20%20X%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_train%2C%20y_train)%3A%0A%20%20%20%20mo.output.append(%22Training%20Random%20Forest%20classifier...%22)%0A%20%20%20%20model%20%3D%20RandomForestClassifier(n_estimators%3D100%2C%20random_state%3D42)%0A%20%20%20%20model.fit(X_train%2C%20y_train)%0A%20%20%20%20mo.output.append(%22Training%20complete.%22)%0A%20%20%20%20return%20(model%2C)%0A%0A%0A%40app.cell%0Adef%20_(X_test%2C%20model%2C%20y_test)%3A%0A%20%20%20%20y_pred%20%3D%20model.predict(X_test)%0A%20%20%20%20acc%20%3D%20accuracy_score(y_test%2C%20y_pred)%0A%20%20%20%20mo.output.append(mo.md(f%22Model%20Accuracy%3A%20**%7Bacc%20*%20100%3A.2f%7D%25**%22))%0A%20%20%20%20return%20(y_pred%2C)%0A%0A%0A%40app.cell%0Adef%20_(LABELS%2C%20y_pred%2C%20y_test)%3A%0A%20%20%20%20mo.output.append(mo.md(%22**Classification%20Report%3A**%22))%0A%20%20%20%20print(classification_report(y_test%2C%20y_pred%2C%20labels%3DLABELS))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(model%2C%20y_pred%2C%20y_test)%3A%0A%20%20%20%20C%20%3D%20confusion_matrix(y_test%2C%20y_pred%2C%20labels%3Dmodel.classes_)%0A%0A%20%20%20%20ax%20%3D%20sns.heatmap(%0A%20%20%20%20%20%20%20%20C%2C%20annot%3DTrue%2C%20fmt%3D%22.0f%22%2C%20cmap%3Dsns.color_palette(%22Blues%22%2C%20as_cmap%3DTrue)%0A%20%20%20%20)%0A%20%20%20%20ax.set_xticklabels(%0A%20%20%20%20%20%20%20%20model.classes_%2C%20rotation%3D45%2C%20rotation_mode%3D%22anchor%22%2C%20ha%3D%22right%22%0A%20%20%20%20)%0A%20%20%20%20ax.set_yticklabels(model.classes_%2C%20rotation%3D0)%0A%20%20%20%20ax.set_xlabel(%22Predicted%20label%22)%0A%20%20%20%20ax.set_ylabel(%22True%20label%22)%0A%20%20%20%20ax.set_title(%22Confusion%20Matrix%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_test%2C%20model%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20label_binarizer%20%3D%20LabelBinarizer().fit(y_train)%0A%20%20%20%20y_onehot_test%20%3D%20label_binarizer.transform(y_test)%0A%20%20%20%20y_score%20%3D%20model.predict_proba(X_test)%0A%0A%20%20%20%20display%20%3D%20RocCurveDisplay.from_predictions(%0A%20%20%20%20%20%20%20%20y_onehot_test.ravel()%2C%0A%20%20%20%20%20%20%20%20y_score.ravel()%2C%0A%20%20%20%20%20%20%20%20name%3D%22micro-average%20OvR%22%2C%0A%20%20%20%20%20%20%20%20curve_kwargs%3Ddict(color%3D%22darkorange%22)%2C%0A%20%20%20%20%20%20%20%20plot_chance_level%3DTrue%2C%0A%20%20%20%20%20%20%20%20despine%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20display.ax_.set(%0A%20%20%20%20%20%20%20%20xlabel%3D%22False%20Positive%20Rate%22%2C%0A%20%20%20%20%20%20%20%20ylabel%3D%22True%20Positive%20Rate%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22Micro-averaged%20One-vs-Rest%5CnReceiver%20Operating%20Characteristic%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20Multi%20object%20image%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.function%0Adef%20find_all_contours(thresholded_image)%3A%0A%20%20%20%20contours%2C%20_%20%3D%20cv2.findContours(%0A%20%20%20%20%20%20%20%20thresholded_image%2C%20cv2.RETR_EXTERNAL%2C%20cv2.CHAIN_APPROX_SIMPLE%0A%20%20%20%20)%0A%0A%20%20%20%20filtered_contours%20%3D%20%5B%0A%20%20%20%20%20%20%20%20contour%20for%20contour%20in%20contours%20if%20cv2.contourArea(contour)%20%3E%3D%20100%0A%20%20%20%20%5D%0A%0A%20%20%20%20return%20filtered_contours%0A%0A%0A%40app.function%0Adef%20extract_features_from_all_shapes(image_path%2C%20threshold_value%3DNone)%3A%0A%20%20%20%20feature_names%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22num_corners%22%2C%0A%20%20%20%20%20%20%20%20%22solidity%22%2C%0A%20%20%20%20%20%20%20%20%22aspect_ratio%22%2C%0A%20%20%20%20%20%20%20%20%22circularity%22%2C%0A%20%20%20%20%20%20%20%20%22hu1%22%2C%0A%20%20%20%20%20%20%20%20%22hu2%22%2C%0A%20%20%20%20%20%20%20%20%22hu3%22%2C%0A%20%20%20%20%20%20%20%20%22hu4%22%2C%0A%20%20%20%20%20%20%20%20%22hu5%22%2C%0A%20%20%20%20%20%20%20%20%22hu6%22%2C%0A%20%20%20%20%20%20%20%20%22hu7%22%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20thresholded_image%20%3D%20preprocessing(image_path%2C%20threshold_value)%0A%20%20%20%20contours%20%3D%20find_all_contours(thresholded_image)%0A%20%20%20%20mo.output.append(f%22Found%20%7Blen(contours)%7D%20contours%20in%20the%20image.%22)%0A%0A%20%20%20%20if%20len(contours)%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%0A%20%20%20%20all_contours_features%20%3D%20%5B%5D%0A%20%20%20%20for%20contour%20in%20contours%3A%0A%20%20%20%20%20%20%20%20all_contours_features.append(extract_features_from_contour(contour))%0A%0A%20%20%20%20df%20%3D%20pd.DataFrame(all_contours_features%2C%20columns%3Dfeature_names)%0A%0A%20%20%20%20return%20df%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20multi_shape_image%20%3D%20mo.notebook_location()%20%2F%20%22public%22%20%2F%20%22shapes_used.png%22%0A%20%20%20%20mo.image(multi_shape_image)%0A%20%20%20%20return%20(multi_shape_image%2C)%0A%0A%0A%40app.cell%0Adef%20_(model%2C%20multi_shape_image)%3A%0A%20%20%20%20all_shapes_features%20%3D%20extract_features_from_all_shapes(%0A%20%20%20%20%20%20%20%20multi_shape_image%2C%20threshold_value%3D250%0A%20%20%20%20)%0A%20%20%20%20predicted_shapes%20%3D%20model.predict(all_shapes_features)%0A%20%20%20%20Counter(predicted_shapes)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
04bb8ed2e02361bcda20677cd8a8d8c0e3d09706a14cf780b32e8c865a7881b1