Giới thiệu về Tầm nhìn máy tính với OpenCV và Python


Ngô Yên Sơn
2 năm trước
Hữu ích 7 Chia sẻ Viết bình luận 0
Đã xem 7696

Tầm nhìn máy tính là một môn học thú vị trong khoa học máy tính. Nghiên cứu đã được quan tâm với chủ đề trong nhiều thập kỷ, nhưng chỉ với những phát triển mới nhất về dữ liệu lớn và trí tuệ nhân tạo, nó mới có thể tạo ra các ứng dụng mới tuyệt đẹp. Việc xử lý trở nên nhanh hơn và rẻ hơn nhờ các công nghệ đám mây và GPU mới. Với các mô hình định giá trả tiền, bạn có thể tham gia vào đấu trường mà không gặp rủi ro khi phải đầu tư lớn. Các hệ thống nhúng nhỏ như NVIDIA Jetson tạo ra các thiết bị sáng tạo, di động và thông minh có sức mạnh xử lý cao với mức tiêu thụ điện năng thấp.

Hàng triệu năm trước, cái gọi là vụ nổ Cambrian đã xảy ra. Trong một khoảng thời gian tương đối ngắn, đa dạng sinh học trên trái đất "bùng nổ". Một số nhà nghiên cứu nghĩ rằng một trong những lý do cho điều này là sự phát triển của thị lực và họ nghĩ rằng máy tính đang đi trên một con đường tương tự ngày nay. Nhưng so với sự tiến hóa, sự tiến bộ của khả năng thị giác máy tính đã và đang diễn ra nhanh hơn nhiều.

Ô tô, robot và máy bay không người lái bắt đầu hiểu những gì chúng ta thấy trong ảnh và video. Giao diện máy tính tầm nhìn trực quan giữa các máy và con người sẽ có tầm quan trọng hơn nhiều trong vài năm tới.

Chúng tôi bắt đầu một dự án nhỏ để phát triển một máy bay không người lái tương tác với khả năng thị giác máy tính. Tôi sẽ sử dụng dự án này như một ví dụ để chứng minh một số khái niệm thị giác máy tính cơ bản. 

Đây là video demo từ dự án máy bay không người lái của chúng tôi :

Trong bài viết sau, tôi sẽ cố gắng giải thích từng bước cách chúng tôi thực hiện trình phát hiện người bằng các thuật toán đơn giản.

Bắt đầu

Có nhiều khung khác nhau cho tầm nhìn máy tính. Phổ biến nhất là OpenCV . Tôi cũng sẽ khuyên bạn nên xem dlib .

Theo OpenCV:

"OpenCV được phát hành theo giấy phép BSD và do đó nó miễn phí cho cả mục đích học thuật và thương mại. Nó có giao diện C ++, C, Python và Java và hỗ trợ Windows, Linux, Mac OS, iOS và Android. OpenCV được thiết kế cho hiệu quả tính toán và với tập trung mạnh vào các ứng dụng thời gian thực. Được viết bằng C / C ++ được tối ưu hóa, thư viện có thể tận dụng xử lý đa lõi. Được kích hoạt với OpenCL, nó có thể tận dụng khả năng tăng tốc phần cứng của nền tảng tính toán không đồng nhất cơ bản. " 

Tùy thuộc vào sở thích và kiến ​​thức trước đây của bạn, bạn có thể phát triển trên nhiều nền tảng khác nhau. Để bắt đầu đơn giản, tôi khuyên bạn nên thiết lập môi trường phát triển trên Ubuntu 16.04 với Python 3.x và OpenCV 3.x. Tôi sử dụng máy ảo trên MacBook dựa trên VMware Workstation. (Việc tích hợp phần cứng bên ngoài đôi khi hoạt động tốt nhất so với các giải pháp ảo hóa khác.) Bạn cũng có thể khiến các thành phần đó hoạt động trên các hệ điều hành khác, nhưng trong trường hợp đó, thường cần có kiến ​​thức "phiên bản và phụ thuộc xung đột-xung đột" .

dlib không mạnh bằng OpenCV. Tuy nhiên, một số chức năng đáng để xem xét kỹ hơn. Ví dụ: Máy dò mốc mặt / máy theo dõi tương quan của nó có thể được nhìn thấy trong hành động trong ví dụ này từ máy bay không người lái của chúng tôi:

GPU hay CPU? 

Một số thuật toán dựa trên CUDA để sử dụng GPU. Đối với điều đó, bạn cần một card đồ họa từ NVIDIA. Nếu bạn không có, bạn có thể thuê một phiên bản GPU trên AWS hoặc nhận bảng nhà phát triển (như NVIDIA Jeston TX). Không cần thiết cho sự khởi đầu đầu tiên nhưng các thuật toán tiên tiến hơn (mạng nơ ron, học sâu, v.v.) đang chạy nhanh hơn nhiều với khả năng tăng tốc phần cứng. Trong lĩnh vực này, thường không phải là lựa chọn khôn ngoan nhất để luôn sử dụng phiên bản mới nhất và tuyệt vời nhất. Có thể bạn sẽ cần một phiên bản Ubuntu cũ hơn và không phải là nhân Linux mới nhất để có thể biên dịch tất cả các trình điều khiển và phụ thuộc. Trong thị trường AWS, bạn có thể tìm thấy các phiên bản GPU mà OpenCV, Python, CUDA và các liên kết đã được cài đặt sẵn và sẵn sàng để chạy (dựa trên Ubuntu 14.04 kể từ tháng 5 năm 2017).

Cài đặt OpenCV với Python Wrappers

Tuyên bố miễn trừ trách nhiệm: Trên internet, bạn có thể tìm thấy nhiều hướng dẫn về cách cài đặt OpenCV . Tôi không cố gắng phát minh lại bánh xe ở đây. Chỉ cần thiết lập một máy ảo Ubuntu và sau đó làm theo từng bước của bài viết trên.

OpenCV được viết bằng C, nhưng bắt đầu với trình bao bọc Python dễ dàng hơn nhiều đối với tôi. Nó phụ thuộc vào kiến ​​thức trước đây của bạn, nhưng trong trường hợp của tôi, nó giúp tôi làm việc với các nguyên mẫu nhanh hơn nhiều. Sự khác biệt về hiệu suất không đáng chú ý đối với hầu hết các trường hợp sử dụng.

Khái niệm cơ bản về thị giác máy tính

Sự tiến bộ trong tầm nhìn máy tính chủ yếu xảy ra với sự trợ giúp của mạng lưới thần kinh và học tập sâu. Nhưng để bắt đầu trong lĩnh vực này, bạn nên bao gồm những điều cơ bản trước.

Video ngắn này giải thích những điều cơ bản và hiển thị một số ví dụ về mã. Bạn cũng có thể thấy cách chúng tôi tạo phần đầu tiên của trình phát hiện đối tượng đơn giản từ video bay không người lái:

Hình ảnh là các mảng đa chiều

Một hình ảnh được thể hiện dưới dạng một mảng nhiều chiều. Trong Python, nó thuộc kiểu dữ liệu numpy  trong khi ở C, nó là Mat . Các tọa độ (0, 0) nằm ở góc trên0left. Khi bạn có một hình ảnh màu, sẽ có ba giá trị màu cho mỗi tọa độ. Tùy thuộc vào độ phân giải và độ sâu màu, các mảng đó có thể khác nhau về kích thước. Các giá trị màu đi từ 0 đến 255. Lưu ý rằng với OpenCV, trước tiên bạn chỉ định tọa độ Y và sau đó là tọa độ X (thường gây nhầm lẫn).

Đoạn mã sau đang đọc tệp hình ảnh và thực hiện một số thao tác cơ bản ở mức pixel:

import cv2

# read image from HD
image = cv2.imread("test.png")

# read color values at position y, x
y = 100
x = 50
(b, g, r) = image[y, x]

# print color values to screen
print(b,g,r)

# set pixel color to RED in BGR color scheme
image[y, x] = (0, 0, 255)

# choose region of interest at (x, y) with dimension 50x50 pixel
region_of_interest = image[y:y+50, x:x+50]

# show image on screen
cv2.imshow("Bild", image)

# show region of interest in seperate window
cv2.imshow("ROI", region_of_interest)

# set all ROI pixels to green
region_of_interest[:, :] = (0, 255, 0)

# now show modified image, not that ROI is a "pointer" to the original image
cv2.imshow("Modified Image", image)

# wait for a key - important, otherwise nothing would be visible
cv2.waitKey(0)

Không gian màu

Không gian màu mặc định trong OpenCV là BGR (Blue Green Red). Nó thường được gọi là RGB, do đó có nhiều nhầm lẫn với thứ tự cơ bản trong OpenCV. (Nhưng, tất nhiên, có một lý do chính đáng cho việc này: Vì vậy, đó là cách này và do đó, sẽ duy trì theo cách này.

Tùy thuộc vào bảng màu mà bạn đang làm việc, có những lợi thế và bất lợi cho ứng dụng của bạn. Chẳng hạn, không gian màu của HSV sẽ dễ xử lý hơn nếu bạn lọc các dải màu cụ thể. Nếu bạn muốn lọc mọi thứ "loại màu cam" trong không gian màu BGR, điều đó không dễ dàng gì. HSV cũng không bị ảnh hưởng nặng nề bởi những thay đổi trong ánh sáng. Nếu bạn chuyển đổi một hình ảnh sang thang độ xám, nó sẽ chỉ có một kênh màu. Điều này có ý nghĩa nếu bạn muốn giảm lượng dữ liệu hoặc nếu bạn muốn tối ưu hóa thời gian xử lý.

Đây là một ví dụ nhỏ khác:

import cv2

# init web cam
cam = cv2.VideoCapture(0)

# read frame from web cam
ret, image = cam.read()

# convert image to gray scale
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# show image in seperate window
cv2.imshow("Bild modifiziert", image)

# wait for key press
cv2.waitKey(0)

Các thuật toán và phương pháp phổ biến

Với tầm nhìn máy tính, đôi khi bạn phải suy nghĩ bên ngoài hộp để thực hiện các chức năng phức tạp hơn. Máy tính không thực sự hiểu những gì hiển thị trên hình ảnh - nó chỉ nhìn thấy các chữ số đại diện cho các giá trị màu. Dưới đây là một vài phương pháp không thể thiếu là công cụ cơ bản cho một máy tính nhìn xa trông rộng!

Ngưỡng

Ngưỡng thường được sử dụng để lọc các khu vực cụ thể của hình ảnh với các thuộc tính (màu) cụ thể. Một phương pháp ngưỡng là ngưỡng nhị phân trong đó bạn xác định ngưỡng và nhận hình ảnh đen trắng làm đầu ra. Tất cả các pixel vượt quá ngưỡng là màu trắng trong khi các pixel khác có màu đen. Với phương pháp này, ví dụ, bạn có thể tìm kiếm tất cả các pixel màu cam trong một hình ảnh (như điểm đánh dấu trong video demo của chúng tôi).

Mặt nạ ngưỡng thường là cơ sở để phân tích thêm.

Đây là mã của video:

## check color ranges of tennis ball

import cv2

# init webcam
cam = cv2.VideoCapture(0)

# define region of interest
x, y, w, h = 400, 400, 100, 100

# show webcam stream
while cam.isOpened():
    # read frame from cam
    ret, frame = cam.read()

    # convert frame to HSV color scheme
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # draw rectangle in frame
    cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 255, 255), thickness=1)

    # print color values to screen
    cv2.putText(frame, "HSV: {0}".format(frame[y+1, x+1]), (x, 600),
                cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), thickness=2)

    # show frame
    cv2.imshow("frame", frame)

    # wait for key press
    key = cv2.waitKey(1) & 0xff

    # if ESC, exit
    if key == 27:
        break

Với các giá trị màu, chúng tôi lọc theo phạm vi:

import cv2

# init webcam
cam = cv2.VideoCapture(0)

# define color ranges
lower_yellow = (18, 100, 210)
upper_yellow = (40, 160, 245)

# show webcam stream
while cam.isOpened():
    # read frame from webcam
    ret, frame = cam.read()

    # convert frame to HSV
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # filter image for color ranges
    mask = cv2.inRange(frame, lower_yellow, upper_yellow)

    # show mask
    cv2.imshow("threshold", mask)

    # wait for key 
    key = cv2.waitKey(1) & 0xff

    # if ESC, exit
    if key == 27:
        break

Tìm đường viền

Thường có các thuật toán hiệu quả cho hình ảnh đen trắng để tìm đường viền trong đó. Họ nhận ra các pixel liền kề và nhóm chúng lại với nhau thành các đốm màu. Ngoài ra, bạn có thể sử dụng một số thuộc tính nhất định từ các đường viền này để phân tích thêm - ví dụ: khu vực hoặc cạnh của đường viền và bạn có thể yêu cầu trả lại hộp giới hạn. Chúng tôi sử dụng điều này trong video demo của chúng tôi để tìm vị trí của điểm đánh dấu màu cam. Chúng tôi chỉ tìm kiếm các đường viền với một khu vực tối thiểu nhất định (theo cách này, chúng tôi có thể lọc các pixel pixel ồn ào duy nhất cũng nằm trong vùng màu cam).

Ở đây chúng tôi đang cố gắng tìm quả bóng tennis trong hình ảnh và chúng tôi đang lọc các pixel nhiễu ra khỏi nó:

import cv2

# init webcam
cam = cv2.VideoCapture(0)

# define color ranges
lower_yellow = (18, 100, 210)
upper_yellow = (40, 160, 245)

# show webcam stream
while cam.isOpened():
    # read frame from cam
    ret, frame = cam.read()

    # convert frame to HSV
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # filter for color ranges
    mask = cv2.inRange(frame, lower_yellow, upper_yellow)

    # find contours on mask of "tennis-ball" pixels
    _, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL,
                                      cv2.CHAIN_APPROX_SIMPLE)

    # now find the largest contour, this is most likely the tennis balls
    # for this we use the area of the contour
    if len(contours) > 0:
        tennis_ball = max(contours, key=cv2.contourArea)

        # draw bounding box around tennis ball
        x, y, w, h = cv2.boundingRect(tennis_ball)
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), thickness=3)

    # show frame
    cv2.imshow("frame", frame)

    # wait for key
    key = cv2.waitKey(1) & 0xff

    # if ESC, exit
    if key == 27:
        break

Phép trừ nền

Khi bạn có một camera tĩnh, có nhiều phương pháp (tương đối dễ dàng) để phát hiện chuyển động trong ảnh. Về cơ bản, bạn cho rằng mọi thứ không chuyển động là nền. Nói một cách đơn giản, bạn trừ các giá trị màu pixel khỏi khung hình thực tế với các giá trị từ khung trước đó. Khi không có thay đổi, bạn sẽ nhận được 0 kết quả (không có chuyển động). Nhưng mô hình này quá đơn giản để sử dụng thực tế vì nó quá dễ bị ảnh hưởng bởi những thay đổi nhỏ trong ánh sáng và ảnh hưởng môi trường (như gió). Nhiều thuật toán khác nhau đã được phát triển trong nhiều thập kỷ qua, và mỗi thuật toán đều có những ưu điểm và nhược điểm riêng. Không có thuật toán duy nhất phù hợp với tất cả và hoạt động trong mọi tình huống. Nhưng bạn có thể tìm thấy một danh sách toàn diện các thuật toán đã biết ở đây .

Một thuật toán được sử dụng rất phổ biến đang làm việc với Mô hình hỗn hợp Gaussian (GMM), MoG2 như được gọi trong OpenCV. Các thuật toán mới hơn, ví dụ, là SubSENSE.

Đây là một video demo nhỏ:

Máy dò

Với OpenCV hoặc dlib, bạn có được các máy dò "tiêu chuẩn" khác nhau. Một chương trình có thể phát hiện khuôn mặt trong các luồng webcam có thể bị hack cùng với một vài dòng mã Python. Là các chương trình như vậy phù hợp cho sử dụng thực tế? Tôi không nghĩ vậy. Những máy dò tiêu chuẩn có tỷ lệ lỗi cao (rất nhiều dương tính / âm tính giả). Theo truyền thống, bạn sẽ sử dụng các phương pháp sau để tìm khuôn mặt trong ảnh.

Phân loại tóc Cascade

Các phân loại thác tóc trở lại một bài báo của Viola và Jones từ năm 2000. Thuật toán tương đối nhanh; bạn có thể chạy nó trong thời gian thực với độ phân giải nhỏ và giảm tốc độ khung hình trên Raspberry Pi. OpenCV đã giao hàng với một số phân loại thác tóc được đào tạo trước để nhận diện khuôn mặt của người hoặc mèo ... nhưng tôi phải đề cập rằng bộ phân loại này cũng thường xuyên nhận ra tựa lưng của ghế tôi là mặt.

Máy dò HOG

Với biểu đồ của máy dò độ dốc định hướng (HOG), các phần của hình ảnh được chia thành một lưới. Đối với mỗi hộp của lưới, các cạnh trội được phát hiện và biến đổi thành các giá trị số để chúng có thể được căn chỉnh. Sức mạnh xử lý cần thiết để thực hiện tất cả các tính toán này cao hơn nhiều so với phân loại thác tóc, nhưng độ chính xác cao hơn.

Bạn có thể nghĩ về trực quan hóa đơn giản của một vectơ đặc trưng HOG như sau. Độ dốc chiếm ưu thế trong mỗi hộp có thể dễ dàng nhận ra dưới dạng khuôn mặt:

   ______
  / _  _ \
 |   /    |
 |   __   | 
  \ ____ /

Chúng tôi đang sử dụng một trình phân loại thác tóc trong video demo máy bay không người lái của chúng tôi chỉ tìm kiếm các khuôn mặt gần điểm đánh dấu màu cam được phát hiện. Bằng cách này, chúng tôi có một trình phát hiện thời gian thực nhanh và có thể giảm các tiêu cực sai để có được kết quả tốt.

Đây là một phần của video với trình phát hiện khuôn mặt gần các điểm đánh dấu:

Với Deep Learning, tương lai là ngay bây giờ

Cuối cùng, tôi muốn đưa ra một triển vọng nhỏ trong lĩnh vực học tập sâu. Nhiều nhà nghiên cứu đang làm việc trong lĩnh vực này và bạn có thể tìm thấy các bản demo và thuật toán ấn tượng trên YouTube. Một trong số đó là YOLO. Ý tôi không phải là từ xu hướng của năm 2012 - nó là viết tắt của từ mà bạn chỉ nhìn một lần. Sau lưng đó là một mạng lưới thần kinh tích chập có thể nhận ra các lớp đối tượng khác nhau trong thời gian thực (tùy thuộc vào phần cứng).

Chúng tôi đã thử thuật toán này và để máy bay không người lái của chúng tôi bay qua văn phòng của chúng tôi. Chúng tôi đã thuê một phiên bản GPU trên AWS và cài đặt YOLO ở đó. Chúng tôi đã bắt đầu một hình ảnh TensorFlow được cấu hình sẵn từ Amazon Marketplace và làm theo hướng dẫn này . Tất nhiên, bạn có thể cố gắng tự mình thiết lập một thể hiện, nhưng đây không phải là một nhiệm vụ tầm thường. Bài viết này có thể giúp bạn bắt đầu . Trong thử nghiệm này, chúng tôi đã nhận ra rõ ràng mức độ khác nhau của GPU - thuật toán cần 15-20 giây cho mỗi khung hình trên CPU và với sự hỗ trợ của GPU, điều này đã giảm xuống còn 6 ms!

Bạn có thể xem kết quả từ chuyến bay không người lái của chúng tôi ở đây:

Các ví dụ thú vị khác là:

Bất cứ ai quan tâm đến việc tìm hiểu thêm về lĩnh vực này nên đọc cuốn sách Deep Learning  của Ian Goodfellow et al. Theo cuốn sách này, số lượng ANN tăng gấp đôi sau mỗi 2,4 năm. Vào năm 2015, số lượng tế bào thần kinh trong các mạng lớn như GoogleNet nằm ở đâu đó giữa não của ong và não của ếch. Ngoài ra, một số ANN chuyên biệt đã vượt trội trong một số nhiệm vụ nhất định so với con người. Nếu sự phát triển tiếp tục theo cách này, dự kiến ​​khoảng năm 2056, các mạng lưới thần kinh lớn nhất sẽ có kích thước của bộ não con người.

Hữu ích 7 Chia sẻ Viết bình luận 0
Đã xem 7696