{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "93b77493-0a01-4421-b2a0-380991740ff6", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import cv2\n", "import pandas as pd\n", "\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "80b4ff7c-1f3b-4e1d-896c-d88c0966f33e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6868.0 30.03550936578534 848 478\n" ] } ], "source": [ "cap = cv2.VideoCapture('video/video.mp4')\n", "# mendapatkan jumlah frame, fps, lebar, dan tinggi dari video\n", "frames_count, fps, width, height = cap.get(cv2.CAP_PROP_FRAME_COUNT), cap.get(cv2.CAP_PROP_FPS), cap.get(\n", " cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_HEIGHT)\n", "width = int(width)\n", "height = int(height)\n", "print(frames_count, fps, width, height)\n", "\n", "# membuat sebuah frame pandas dengan jumlah baris yang sama dengan jumlah frame\n", "df = pd.DataFrame(index=range(int(frames_count)))\n", "df.index.name = \"Frame\" # menandai kolom frame\n", "\n", "framenumber = 0 # mencatat frame saat ini\n", "carscrossedup = 0 # mencatat mobil yang melintasi jalan ke atas\n", "carscrosseddown = 0 # mencatat mobil yang melintasi jalan ke bawah\n", "carids = [] # daftar kosong untuk menyimpan ID mobil\n", "caridscrossed = [] # daftar kosong untuk menyimpan ID mobil yang sudah melintasi\n", "totalcars = 0 # mencatat jumlah total mobil\n", "\n", "fgbg = cv2.createBackgroundSubtractorMOG2() # membuat pengambil gambar latar belakang\n", "\n", "# informasi untuk mulai menyimpan video\n", "ret, frame = cap.read() # mengimpor gambar\n", "ratio = .5 # rasio ukuran pengubahan ukuran\n", "image = cv2.resize(frame, (0, 0), None, ratio, ratio) # mengubah ukuran gambar\n", "width2, height2, channels = image.shape\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "5c8d5645-9df8-457c-88d7-2d3bbc0fade9", "metadata": {}, "outputs": [ { "ename": "KeyboardInterrupt", "evalue": "", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[3], line 265\u001b[0m\n\u001b[1;32m 260\u001b[0m \u001b[38;5;66;03m# video.write(image) # save the current image to video file from earlier\u001b[39;00m\n\u001b[1;32m 261\u001b[0m \n\u001b[1;32m 262\u001b[0m \u001b[38;5;66;03m# adds to framecount\u001b[39;00m\n\u001b[1;32m 263\u001b[0m framenumber \u001b[38;5;241m=\u001b[39m framenumber \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m--> 265\u001b[0m k \u001b[38;5;241m=\u001b[39m \u001b[43mcv2\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwaitKey\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1000\u001b[39;49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43mfps\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m&\u001b[39m \u001b[38;5;241m0xff\u001b[39m \u001b[38;5;66;03m# int(1000/fps) is normal speed since waitkey is in ms\u001b[39;00m\n\u001b[1;32m 266\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m k \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m27\u001b[39m:\n\u001b[1;32m 267\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n", "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] } ], "source": [ "while True:\n", "\n", " ret, frame = cap.read() # mengimpor gambar\n", "\n", " if ret: # jika ada frame lanjutkan dengan kode\n", "\n", " image = cv2.resize(frame, (0, 0), None, ratio, ratio) # mengubah ukuran gambar\n", "\n", " gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # mengubah gambar ke hitam putih\n", "\n", " fgmask = fgbg.apply(gray) # menggunakan pengambil gambar latar belakang\n", "\n", " # menerapkan berbagai batasan pada fgmask untuk menyaring mobil\n", " # perlu bermain dengan setelan tersebut hingga mobil dapat diidentifikasi dengan mudah\n", " kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # kernel untuk dilakukan pada morphology\n", " closing = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel)\n", " opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)\n", " dilation = cv2.dilate(opening, kernel)\n", " retvalbin, bins = cv2.threshold(dilation, 220, 255, cv2.THRESH_BINARY) # menghapus shadow\n", "\n", " # membuat kontur\n", " contours, hierarchy = cv2.findContours(bins, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]\n", "\n", " # menggunakan konveks hull untuk membuat poligon di sekitar kontur\n", " hull = [cv2.convexHull(c) for c in contours]\n", "\n", " # menggambar kontur\n", " cv2.drawContours(image, hull, -1, (0, 255, 0), 3)\n", "\n", " # garis dibuat untuk menghentikan menghitung kontur, perlu dilakukan karena mobil yang jauh akan menjadi satu kontur besar\n", " lineypos = 225\n", " cv2.line(image, (0, lineypos), (width, lineypos), (255, 0, 0), 5)\n", "\n", " # garis y pos dibuat untuk menghitung kontur\n", " lineypos2 = 250\n", " cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 255, 0), 5)\n", "\n", " # minimum area untuk kontur\n", " minarea = 300\n", "\n", " # maksimum area untuk kontur\n", " maxarea = 50000\n", "\n", " # vektor untuk x dan y lokasi centroid di frame saat ini\n", " cxx = np.zeros(len(contours))\n", " cyy = np.zeros(len(contours))\n", "\n", " for i in range(len(contours)): # mengulangi seluruh kontur dalam frame saat ini\n", "\n", " if hierarchy[0, i, 3] == -1: # menggunakan hierarchy untuk hanya menghitung kontur induk (tidak termasuk dalam kontur lain)\n", "\n", " area = cv2.contourArea(contours[i]) # menghitung area kontur\n", "\n", " if minarea < area < maxarea: # area threshold untuk kontur\n", "\n", " # menghitung centroid dari kontur\n", " cnt = contours[i]\n", " M = cv2.moments(cnt)\n", " cx = int(M['m10'] / M['m00'])\n", " cy = int(M['m01'] / M['m00'])\n", "\n", " if cy > lineypos: # menghapus kontur yang di atas garis\n", "\n", " # mengambil titik teratas, kiri, dan lebar dari kontur untuk membuat kotak\n", " # x,y adalah kiri atas dan w,h adalah lebar dan tinggi\n", " x, y, w, h = cv2.boundingRect(cnt)\n", "\n", " # membuat kotak di sekitar kontur\n", " cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)\n", "\n", " # Menuliskan teks centroid untuk memastikan kembali nanti\n", " cv2.putText(image, str(cx) + \",\" + str(cy), (cx + 10, cy + 10), cv2.FONT_HERSHEY_SIMPLEX,\n", " .3, (0, 0, 255), 1)\n", "\n", " cv2.drawMarker(image, (cx, cy), (0, 0, 255), cv2.MARKER_STAR, markerSize=5, thickness=1,\n", " line_type=cv2.LINE_AA)\n", "\n", " # menambahkan centroid yang lulus pada kriteria ke dalam list centroid\n", " cxx[i] = cx\n", " cyy[i] = cy\n", "\n", " # menghapus entri 0 dari list centroid\n", " cxx = cxx[cxx != 0]\n", " cyy = cyy[cyy != 0]\n", "\n", " # list kosong untuk nanti menyimpan indices centroid yang di tambahkan ke dataframe\n", " minx_index2 = []\n", " miny_index2 = []\n", "\n", " # batas maksimum untuk radius dari centroid dari frame saat ini untuk dianggap sama dengan centroid dari frame sebelumnya\n", " maxrad = 25\n", "\n", " # Bagian ini mengelola centroid dan menetapkan mereka untuk carid lama atau carid baru\n", "\n", " if len(cxx): # jika ada centroid dalam area yang ditentukan\n", "\n", " if not carids: # jika carids kosong\n", "\n", " for i in range(len(cxx)): # melalui semua centroid\n", "\n", " carids.append(i) # menambahkan car id ke list carids kosong\n", " df[str(carids[i])] = \"\" # menambahkan kolom ke dataframe sesuai carid\n", "\n", " # menetapkan nilai centroid ke frame (baris) dan carid (kolom) yang sesuai\n", " df.at[int(framenumber), str(carids[i])] = [cxx[i], cyy[i]]\n", "\n", " totalcars = carids[i] + 1 # menambahkan count car\n", "\n", " else: # jika carids sudah ada\n", "\n", " dx = np.zeros((len(cxx), len(carids))) # array baru untuk menghitung deltas\n", " dy = np.zeros((len(cyy), len(carids))) # array baru untuk menghitung deltas\n", "\n", " for i in range(len(cxx)): # melalui semua centroid\n", "\n", " for j in range(len(carids)): # melalui semua car id yang sudah ada\n", "\n", " # mengambil centroid dari frame sebelumnya untuk carid tertentu\n", " oldcxcy = df.iloc[int(framenumber - 1)][str(carids[j])]\n", "\n", " # mengambil centroid dari frame saat ini yang tidak selalu sesuai dengan centroid frame sebelumnya\n", " curcxcy = np.array([cxx[i], cyy[i]])\n", "\n", " if not oldcxcy: # periksa apakah centroid sebelumnya kosong jika arah sudah tidak ada di layar\n", "\n", " continue # lanjutkan ke carid berikutnya\n", "\n", " else: # hitung delta centroid untuk membandingkan dengan centroid frame saat ini\n", "\n", " dx[i, j] = oldcxcy[0] - curcxcy[0]\n", " dy[i, j] = oldcxcy[1] - curcxcy[1]\n", "\n", " for j in range(len(carids)): # melalui semua car id saat ini\n", "\n", " sumsum = np.abs(dx[:, j]) + np.abs(dy[:, j]) # menghitung delta wrt car id\n", "\n", " # mengambil indeks centroid yang memiliki nilai delta minimum dan ini indeks benar\n", " correctindextrue = np.argmin(np.abs(sumsum))\n", " minx_index = correctindextrue\n", " miny_index = correctindextrue\n", "\n", " # mengambil delta nilai minimum untuk dibandingkan dengan radius\n", " mindx = dx[minx_index, j]\n", " mindy = dy[miny_index, j]\n", "\n", " if mindx == 0 and mindy == 0 and np.all(dx[:, j] == 0) and np.all(dy[:, j] == 0):\n", " # periksa apakah minimum nilai adalah 0 dan semua delta adalah nol\n", " # delta dapat berupa nol jika centroid tidak bergerak\n", "\n", " continue # lanjutkan ke carid berikutnya\n", "\n", " else:\n", "\n", " # jika delta nilai adalah kurang dari maksimal radius maka tambahkan centroid ke carid sebelumnya\n", " if np.abs(mindx) < maxrad and np.abs(mindy) < maxrad:\n", "\n", " # tambahkan centroid ke carid yang sudah ada\n", " df.at[int(framenumber), str(carids[j])] = [cxx[minx_index], cyy[miny_index]]\n", " minx_index2.append(minx_index) # tambahkan semua indeks yang ditambahkan ke carid ke list\n", " miny_index2.append(miny_index)\n", "\n", " currentcars = 0 # current cars on screen\n", " currentcarsindex = [] # current cars on screen carid index\n", "\n", " for i in range(len(carids)): # loops through all carids\n", "\n", " if df.at[int(framenumber), str(carids[i])] != '':\n", " # checks the current frame to see which car ids are active\n", " # by checking in centroid exists on current frame for certain car id\n", "\n", " currentcars = currentcars + 1 # adds another to current cars on screen\n", " currentcarsindex.append(i) # adds car ids to current cars on screen\n", "\n", " for i in range(currentcars): # loops through all current car ids on screen\n", "\n", " # grabs centroid of certain carid for current frame\n", " curcent = df.iloc[int(framenumber)][str(carids[currentcarsindex[i]])]\n", "\n", " # grabs centroid of certain carid for previous frame\n", " oldcent = df.iloc[int(framenumber - 1)][str(carids[currentcarsindex[i]])]\n", "\n", " if curcent: # if there is a current centroid\n", "\n", " # On-screen text for current centroid\n", " cv2.putText(image, \"Centroid\" + str(curcent[0]) + \",\" + str(curcent[1]),\n", " (int(curcent[0]), int(curcent[1])), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 255, 255), 2)\n", "\n", " cv2.putText(image, \"ID:\" + str(carids[currentcarsindex[i]]), (int(curcent[0]), int(curcent[1] - 15)),\n", " cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 255, 255), 2)\n", "\n", " cv2.drawMarker(image, (int(curcent[0]), int(curcent[1])), (0, 0, 255), cv2.MARKER_STAR, markerSize=5,\n", " thickness=1, line_type=cv2.LINE_AA)\n", "\n", " if oldcent: # checks if old centroid exists\n", " # adds radius box from previous centroid to current centroid for visualization\n", " xstart = oldcent[0] - maxrad\n", " ystart = oldcent[1] - maxrad\n", " xwidth = oldcent[0] + maxrad\n", " yheight = oldcent[1] + maxrad\n", " cv2.rectangle(image, (int(xstart), int(ystart)), (int(xwidth), int(yheight)), (0, 125, 0), 1)\n", "\n", " # checks if old centroid is on or below line and curcent is on or above line\n", " # to count cars and that car hasn't been counted yet\n", " if oldcent[1] >= lineypos2 and curcent[1] <= lineypos2 and carids[\n", " currentcarsindex[i]] not in caridscrossed:\n", "\n", " carscrossedup = carscrossedup + 1\n", " cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 0, 255), 5)\n", " caridscrossed.append(\n", " currentcarsindex[i]) # adds car id to list of count cars to prevent double counting\n", "\n", " # checks if old centroid is on or above line and curcent is on or below line\n", " # to count cars and that car hasn't been counted yet\n", " elif oldcent[1] <= lineypos2 and curcent[1] >= lineypos2 and carids[\n", " currentcarsindex[i]] not in caridscrossed:\n", "\n", " carscrosseddown = carscrosseddown + 1\n", " cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 0, 125), 5)\n", " caridscrossed.append(currentcarsindex[i])\n", "\n", " # Top left hand corner on-screen text\n", " cv2.rectangle(image, (0, 0), (250, 100), (255, 0, 0), -1) # background rectangle for on-screen text\n", "\n", " cv2.putText(image, \"Cars in Area: \" + str(currentcars), (0, 15), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 170, 0), 1)\n", "\n", " cv2.putText(image, \"Cars Crossed Up: \" + str(carscrossedup), (0, 30), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 170, 0),\n", " 1)\n", "\n", " cv2.putText(image, \"Cars Crossed Down: \" + str(carscrosseddown), (0, 45), cv2.FONT_HERSHEY_SIMPLEX, .5,\n", " (0, 170, 0), 1)\n", "\n", " cv2.putText(image, \"Total Cars Detected: \" + str(len(carids)), (0, 60), cv2.FONT_HERSHEY_SIMPLEX, .5,\n", " (0, 170, 0), 1)\n", "\n", " cv2.putText(image, \"Frame: \" + str(framenumber) + ' of ' + str(frames_count), (0, 75), cv2.FONT_HERSHEY_SIMPLEX,\n", " .5, (0, 170, 0), 1)\n", "\n", " cv2.putText(image, 'Time: ' + str(round(framenumber / fps, 2)) + ' sec of ' + str(round(frames_count / fps, 2))\n", " + ' sec', (0, 90), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 170, 0), 1)\n", "\n", " # displays images and transformations\n", " cv2.imshow(\"countours\", image)\n", " cv2.moveWindow(\"countours\", 0, 0)\n", "\n", " cv2.imshow(\"fgmask\", fgmask)\n", " cv2.moveWindow(\"fgmask\", int(width * ratio), 0)\n", "\n", " cv2.imshow(\"closing\", closing)\n", " cv2.moveWindow(\"closing\", width, 0)\n", "\n", " cv2.imshow(\"opening\", opening)\n", " cv2.moveWindow(\"opening\", 0, int(height * ratio))\n", "\n", " cv2.imshow(\"dilation\", dilation)\n", " cv2.moveWindow(\"dilation\", int(width * ratio), int(height * ratio))\n", "\n", " cv2.imshow(\"binary\", bins)\n", " cv2.moveWindow(\"binary\", width, int(height * ratio))\n", "\n", " # video.write(image) # save the current image to video file from earlier\n", "\n", " # adds to framecount\n", " framenumber = framenumber + 1\n", "\n", " k = cv2.waitKey(int(1000/fps)) & 0xff # int(1000/fps) is normal speed since waitkey is in ms\n", " if k == 27:\n", " break\n", "\n", " else: # if video is finished then break loop\n", "\n", " break\n" ] }, { "cell_type": "code", "execution_count": null, "id": "af84e6b4-dd55-447e-ac8c-a02a5f6f34be", "metadata": {}, "outputs": [], "source": [ "cap.release()\n", "cv2.destroyAllWindows()\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.10" } }, "nbformat": 4, "nbformat_minor": 5 }