import numpy as np import cv2 import pandas as pd cap = cv2.VideoCapture('video/video.mp4') frames_count, fps, width, height = cap.get(cv2.CAP_PROP_FRAME_COUNT), cap.get(cv2.CAP_PROP_FPS), cap.get( cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_HEIGHT) width = int(width) height = int(height) print(frames_count, fps, width, height) # membuat data frame pandas dengan jumlah baris sama dengan jumlah frame df = pd.DataFrame(index=range(int(frames_count))) df.index.name = "Frame" # frame dalam bahasa indonesia framenumber = 0 # mencatat frame saat ini carscrossedup = 0 # mencatat mobil yang melintasi atas carscrosseddown = 0 # mencatat mobil yang melintasi bawah carids = [] # list kosong untuk menambah id mobil caridscrossed = [] # list kosong untuk menambah id mobil yang telah melintasi totalcars = 0 # mencatat total mobil fgbg = cv2.createBackgroundSubtractorMOG2() # membuat subtractor latar belakang MOG2 # informasi untuk memulai menyimpan file video ret, frame = cap.read() # impor gambar ratio = .5 # rasio pengubah ukuran image = cv2.resize(frame, (0, 0), None, ratio, ratio) # ubah ukuran gambar width2, height2, channels = image.shape # video = cv2.VideoWriter('penghitung_kendaraan.avi', cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'), fps, (height2, width2), 1) while True: ret, frame = cap.read() # impor gambar if ret: # jika ada frame lanjutkan kode image = cv2.resize(frame, (0, 0), None, ratio, ratio) # ubah ukuran gambar gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # konversi gambar ke warna abu-abu fgmask = fgbg.apply(gray) # menggunakan pengurangan latar belakang MOG2 # menerapkan tingkat kesulitan pada fgmask untuk mencoba mengisolasi mobil # perlu mencoba berbagai pengaturan hingga mobil mudah diidentifikasi kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # membuat kernel untuk operasi morfologi closing = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel) opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel) dilation = cv2.dilate(opening, kernel) retvalbin, bins = cv2.threshold(dilation, 220, 255, cv2.THRESH_BINARY) # menghapus shadow # membuat kontur contours, hierarchy = cv2.findContours(bins, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:] # menggunakan konveks hull untuk membuat poligon kait dengan kontur hull = [cv2.convexHull(c) for c in contours] # menggambar kontur cv2.drawContours(image, hull, -1, (0, 255, 0), 3) # garis dibuat untuk menghentikan penghitungan kontur, diperlukan karena mobil jauh menjadi kontur satu lineypos = 100 cv2.line(image, (0, lineypos), (width, lineypos), (255, 0, 0), 5) # garis y posisi dibuat untuk menghitung kontur lineypos2 = 125 cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 255, 0), 5) # area minimal untuk kontur agar tidak dihitung sebagai rumit minarea = 400 # area maksimal untuk kontur, dapat cukup besar untuk bus maxarea = 40000 # vektor untuk x dan y lokasi tengah kontur dalam frame saat ini cxx = np.zeros(len(contours)) cyy = np.zeros(len(contours)) for i in range(len(contours)): # melakukan iterasi pada semua kontur dalam frame saat ini # menggunakan hierarki untuk hanya menghitung kontur induk (kontur yang tidak berada dalam kontur lain) if hierarchy[0, i, 3] == -1: area = cv2.contourArea(contours[i]) # menghitung luas kontur if minarea < area < maxarea: # menggunakan area sebagai garis pembatas untuk kontur # menghitung centroid dari kontur cnt = contours[i] M = cv2.moments(cnt) cx = int(M['m10'] / M['m00']) cy = int(M['m01'] / M['m00']) if cy > lineypos: # menghapus kontur yang berada di atas garis (y dimulai dari atas) # mengambil titik koordinat untuk membuat kotak lingkaran x, y, w, h = cv2.boundingRect(cnt) # membuat kotak lingkaran dari kontur cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2) # Menambahkan teks centroid untuk memverifikasi pada tahap selanjutnya cv2.putText(image, str(cx) + "," + str(cy), (cx + 10, cy + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (0, 0, 255), 1) cv2.drawMarker(image, (cx, cy), (0, 0, 255), cv2.MARKER_STAR, markerSize=5, thickness=1, line_type=cv2.LINE_AA) # menambahkan centroid yang telah memenuhi kriteria ke dalam list centroid cxx[i] = cx cyy[i] = cy # menghapus nol dalam vector centroid yang tidak dihitung (centroid yang tidak dikirim ke dataframe) cxx = cxx[cxx != 0] cyy = cyy[cyy != 0] # list kosong untuk nanti mencatat indeks centroid yang dikirim ke dataframe minx_index2 = [] miny_index2 = [] # jumlah maksimum yang diizinkan untuk centroid dalam frame saat ini untuk dikaitkan dengan centroid dari frame sebelumnya maxrad = 25 # bagian berikut mengelola centroid dan mengasignasinya ke id mobil lama atau id mobil baru # jika terdapat centroid dalam area yang ditentukan if len(cxx): # jika ada centroid dalam area yang ditentukan if not carids: # jika daftar carids kosong for i in range(len(cxx)): # melakukan loop sebanyak centroid yang ada carids.append(i) # menambahkan id mobil ke dalam daftar kosong df[str(carids[i])] = "" # menambahkan kolom ke dalam dataframe berdasarkan id mobil # mengisi nilai centroid pada frame saat ini dan id mobil yang sesuai df.at[int(framenumber), str(carids[i])] = [cxx[i], cyy[i]] totalcars = carids[i] + 1 # menambahkan 1 pada jumlah mobil else: # jika sudah ada id mobil dx = np.zeros((len(cxx), len(carids))) # array untuk menghitung deltanya dy = np.zeros((len(cyy), len(carids))) # array untuk menghitung deltanya for i in range(len(cxx)): # melakukan loop sebanyak centroid yang ada for j in range(len(carids)): # melakukan loop sebanyak id mobil yang ada # mengambil centroid dari frame sebelumnya untuk id mobil tertentu oldcxcy = df.iloc[int(framenumber - 1)][str(carids[j])] # mengambil centroid dari frame sekarang yang tidak selalu sesuai dengan centroid dari frame sebelumnya curcxcy = np.array([cxx[i], cyy[i]]) if not oldcxcy: # jika centroid dari frame sebelumnya kosong karena mobil keluar layar continue # lanjutkan ke id mobil selanjutnya else: # hitung deltanya untuk dibandingkan dengan centroid dari frame sekarang dx[i, j] = oldcxcy[0] - curcxcy[0] dy[i, j] = oldcxcy[1] - curcxcy[1] for j in range(len(carids)): # melakukan loop sebanyak id mobil yang ada jumlahjumlah = np.abs(dx[:, j]) + np.abs(dy[:, j]) # menghitung jumlah delta wrt id mobil tertentu # mencari indeks id mobil yang memiliki nilai minimum dan ini indeks yang tepat indeksindextrue = np.argmin(np.abs(jumlahjumlah)) minx_index = indeksindextrue miny_index = indeksindextrue # mengambil nilai delta untuk id mobil yang dipilih deltadeltadx = dx[minx_index, j] deltadeltady = dy[miny_index, j] if deltadeltadx == 0 and deltadeltady == 0 and np.all(dx[:, j] == 0) and np.all(dy[:, j] == 0): # periksa apakah nilai minimum adalah 0 dan periksa apakah semua delta adalah nol karena ini adalah kumpulan kosong # delta dapat berupa nol jika centroid tidak berpindah continue # lanjutkan ke id mobil selanjutnya else: # jika nilai delta kurang dari radius maksimum maka tambahkan centroid ke id mobil yang sesuai if np.abs(deltadeltadx) < maxrad and np.abs(deltadeltady) < maxrad: # menambahkan centroid ke id mobil yang sudah ada df.at[int(framenumber), str(carids[j])] = [cxx[minx_index], cyy[miny_index]] minx_index2.append(minx_index) # menambahkan indeks centroid yang sudah ditambahkan ke id mobil lain miny_index2.append(miny_index) for i in range(len(cxx)): # melakukan loop sebanyak centroid yang ada # jika centroid tidak ada dalam list minindex maka mobil baru perlu ditambahkan if i not in minx_index2 and miny_index2: df[str(totalcars)] = "" # membuat kolom baru untuk mobil baru yang tercatat totalcars = totalcars + 1 # menambahkan jumlah mobil yang tercatat t = totalcars - 1 # t adalah placeholder untuk jumlah mobil carids.append(t) # menambahkan id mobil ke list id mobil df.at[int(framenumber), str(t)] = [cxx[i], cyy[i]] # menambahkan centroid ke mobil yang sudah ada elif curcxcy[0] and not oldcxcy and not minx_index2 and not miny_index2: # jika centroid saat ini ada namun centroid sebelumnya tidak ada # mobil baru perlu ditambahkan jika minindex2 kosong df[str(totalcars)] = "" # membuat kolom baru untuk mobil baru yang tercatat totalcars = totalcars + 1 # menambahkan jumlah mobil yang tercatat t = totalcars - 1 # t adalah placeholder untuk jumlah mobil carids.append(t) # menambahkan id mobil ke list id mobil df.at[int(framenumber), str(t)] = [cxx[i], cyy[i]] # menambahkan centroid ke mobil yang sudah ada # Bagian di bawah menglabel centroid yang ada di layar currentcars = 0 # mobil yang ada di layar currentcarsindex = [] # indeks id mobil yang ada di layar for i in range(len(carids)): # melakukan loops sebanyak jumlah id mobil # memeriksa frame saat ini untuk mengetahui id mobil yang sedang aktif # dengan memeriksa adanya centroid pada frame saat ini untuk id mobil tertentu if df.at[int(framenumber), str(carids[i])] != '': currentcars = currentcars + 1 # menambahkan mobil yang ada di layar currentcarsindex.append(i) # menambahkan id mobil yang ada di layar for i in range(currentcars): # melakukan loops sebanyak jumlah mobil yang ada di layar # mengambil centroid untuk id mobil tertentu pada frame saat ini curcent = df.iloc[int(framenumber)][str(carids[currentcarsindex[i]])] # mengambil centroid untuk id mobil tertentu pada frame sebelumnya oldcent = df.iloc[int(framenumber - 1)][str(carids[currentcarsindex[i]])] if curcent: # jika ada centroid pada frame saat ini # Teks di layar untuk centroid saat ini cv2.putText(image, "Centroid" + str(curcent[0]) + "," + str(curcent[1]), (int(curcent[0]), int(curcent[1])), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 255, 255), 2) cv2.putText(image, "ID:" + str(carids[currentcarsindex[i]]), (int(curcent[0]), int(curcent[1] - 15)), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 255, 255), 2) cv2.drawMarker(image, (int(curcent[0]), int(curcent[1])), (0, 0, 255), cv2.MARKER_STAR, markerSize=5, thickness=1, line_type=cv2.LINE_AA) # Periksa apakah centroid lama ada # Tambahkan kotak radius dari centroid lama ke centroid saat ini untuk visualisasi if oldcent: xmulai = oldcent[0] - maxrad ymulai = oldcent[1] - maxrad xakhir = oldcent[0] + maxrad yakhir = oldcent[1] + maxrad cv2.rectangle(image, (int(xmulai), int(ymulai)), (int(xakhir), int(yakhir)), (0, 125, 0), 1) # Periksa apakah centroid lama di bawah garis dan centroid baru di atas garis # Untuk menghitung mobil dan memastikan mobil tidak dihitung dua kali if oldcent[1] >= lineypos2 and curcent[1] <= lineypos2 and carids[ currentcarsindex[i]] not in caridscrossed: carscrossedup = carscrossedup + 1 cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 0, 255), 5) caridscrossed.append( currentcarsindex[i]) # Tambahkan id mobil ke daftar mobil yang dihitung untuk mencegah penghitungan dua kali # Periksa apakah centroid lama di atas garis dan centroid baru di bawah garis # Untuk menghitung mobil dan memastikan mobil tidak dihitung dua kali elif oldcent[1] <= lineypos2 and curcent[1] >= lineypos2 and carids[ currentcarsindex[i]] not in caridscrossed: carscrosseddown = carscrosseddown + 1 cv2.line(image, (0, lineypos2), (width, lineypos2), (0, 0, 125), 5) caridscrossed.append(currentcarsindex[i]) # menampilkan jumlah mobil yang melintasi atas cv2.putText(image, "Mobil yang Melintasi Atas: " + str(carscrossedup), (0, 15), cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255), 1) # menampilkan jumlah mobil yang melintasi bawah cv2.putText(image, "Mobil yang Melintasi Bawah: " + str(carscrosseddown), (0, 30), cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255), 1) # # menampilkan jumlah total mobil yang terdeteksi # cv2.putText(image, "Total Mobil yang Terdeteksi: " + str(len(carids)), (0, 60), cv2.FONT_HERSHEY_SIMPLEX, .5, # (255, 255, 255), 1) # menampilkan frame saat ini dan total frame cv2.putText(image, "Frame: " + str(framenumber) + ' dari ' + str(frames_count), (0, 45), cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255), 1) # menampilkan waktu yang sudah berlalu dan total waktu cv2.putText(image, 'Waktu: ' + str(round(framenumber / fps, 2)) + ' detik dari ' + str(round(frames_count / fps, 2)) + ' detik', (0, 60), cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255), 1) # menampilkan images dan transformasi cv2.imshow("Output", image) cv2.moveWindow("Output", 0, 0) cv2.imshow("gray", gray) cv2.moveWindow("gray", int(width * ratio), 0) cv2.imshow("closing", closing) cv2.moveWindow("closing", width, 0) cv2.imshow("opening", opening) cv2.moveWindow("opening", 0, int(height * ratio)) cv2.imshow("dilation", dilation) cv2.moveWindow("dilation", int(width * ratio), int(height * ratio)) cv2.imshow("binary", bins) cv2.moveWindow("binary", width, int(height * ratio)) # adds to framecount framenumber = framenumber + 1 # Menunggu key dari user dalam milidetik, fps adalah frame per detik, dan 0xff adalah binary # bahasa indonesia: Menunggu key dari user dalam milidetik k = cv2.waitKey(int(1000/fps)) & 0xff if k == 27: # bahasa indonesia: Jika key nya adalah 27 (ESC) maka break loop break else: # bahasa indonesia: Jika video selesai maka break loop break cap.release() cv2.destroyAllWindows()