Blog #4 : Classify color and Count objects by Computer Vision (Python and OpenCV)

Sr. Sorawit
DOLAB
Published in
3 min readOct 7, 2018

บทความนี้คัดแยกสีและนับจำนวน object แต่ละสีโดยใช้ OpenCV และ Python ในการ implement

Fig 1. Output this project

Introduction

ปัญหาที่เจอกันบ่อยๆ คือการนับชิ้นงาน หรืแอ object สำหรับใช้นับแทนมนุษย์ ซึ่งงานประเภทนี้จะพบได้บ่อยในงาน Automation สำหรับโรงงาน หรือ งานพวก pick and place เป็นต้น ทำให้ผมจึงเลือกนำ workshop ที่ง่ายและใกล้ real world

Roadmap ก่อนเริ่ม dev

ก่อนอื่นเราต้องเริ่มทำความเข้าใจปัญหาก่อนว่า problem นี้เราต้องการแก้ปัญหาอะไรบ้าง ขอแยก Problem เป็นข้อๆ นะกัน

  1. Color Classification : ต้องการคัดแยกสีเป็น 2 สี โดยจะมี สีส้ม และ สีเหลือง ซึ่งถ้าสงสัยไปดูใน Blog #3 จะอธิบายเรื่องการคัดแยกสีไว้แล้ว
  2. Object Counting : เราต้องเขียน Algorithm สำหรับนับจำนวน Object ที่ถูกคัดแยกมาได้แล้ว
  3. Functional Programming : ทำเพื่อไม่ให้เราค้องเขียน code ยาวจนเกินไปส่วนก็สำคัญเหมือนกัน เป็นการช่วยให้เราสามารถ dev ได้อย่าง optimize ที่สุดหรือก็ไม่เขียนโปรแกรมซ้ำซ้อน

Github (code)

สามารถ clone project ในเว็บไซต์นี้ https://github.com/dolabpublic/DOLAB_Blog4 สำหรับคนที่อยากลองแนะนำลองเอารูปภาพที่ได้ไป detect เองก่อนแล้วค่อยมาดูว่านับได้เท่ากันหรือป่าว ? 555

Import libs and Read image

import numpy as np
import cv2

Function สำหรับ read image

ใช้ OpenCV สำหรับอ่านรูปขึ้นมาเก็บไว้ในตัวแปร หรือ จะ return เป็น output ของ ฟังก์ชั่นได้เลย

def load_image(path_img):
return cv2.imread(path_img)

หรือ จะเขียนแบบนี้ก็ได้ถ้างง 555+ ?

def load_image(path_img):
img = cv2.imread(path_img)
return img

Function สำหรับ convert color from BGR to HSV

HSV color model คืออะไรไปอ่านได้จาก บทความก่อนน้า Blog #3

def bgr2hsv(img):
return cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
Fig 2. Bgr to Hsv

Function สำหรับกำหนดช่วง range ของสีที่เราต้องการ

ช่วง range ของสีที่เราต้องการนั้น มีทั้ง upper และ lower

def setRangeColor(hsv, lower_color, upper_color):
return cv2.inRange(hsv, lower_color, upper_color)

Function สำหรับการหา contour

โดยหลายคนอาจจะงง ว่าเราจะรู้ได้ไง contour ตรวจจับอะไร

ขยายความ function findContours ใน OpenCV

เราจะสังเกตุได้ว่าฟังก์ชั่นนี้จะจับเฉพาะพื้นที่สีขาว ดังนั้นถ้าเราต้อง คัดแยกอะไรเราต้องเริ่มจาก หาวัตถุที่เราต้องการให้เจอแล้ว ทำให้มันเป็นพื้นที่สีขาวแล้วค่อย นำไปเข้า function findContours ดังรูป Fig 3.

Fig 3. ภาพซ้าย:แยกให้เหลือแต่พื้นที่ที่เราสนใจแล้ว, ภาพกลาง:เมื่อเราใช้ function findcontour จับวัตถุแล้ว
def contours_img(mask):
_, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
return contours

Function สำหรับกรอง contour ที่ต้องการ

เมื่อเราทำ function findContours เรียบร้อยแล้ว เราจะสามารถนำ contour ที่เราจับได้มาทำการกรองส่วนเราต้องการ หรือ นำส่วนที่ Noise ที่ไม่ต้องการออกได้ ซึ่งในนี้เรา กรองพื้นที่ (area) ที่น้อยกว่า 1000 pixel ออก โดยนำความกว้างและความสูงของ Bounding Box มาคำนวนเพื่อหาพื้นที่

สูตรการหาพื้นที่

พื้นที่ = กว้าง x ยาว

เมื่อได้พื้นที่ที่เราต้องการแล้วก็นำ Count หรือ นับจำนวนของ object ได้แล้ว

def filter_contours_img(contours, img_draw, color_bbox):
count = 0
for c in contours:
rect = cv2.boundingRect(c)
x,y,w,h = rect
area = w * h
if area > 1000:
count = count + 1 # นับ object ที่มีพื้นที่มากกว่า 1000 pixel
cv2.rectangle(img_draw, (x, y), (x+w, y+h), color_bbox, 5)
return img_draw, count

Function สำหรับวาดตัวอักษร (Text) ขึ้นมาบนรูปภาพ

Fig 4. draw text

ฟังก์นี้สำหรับ draw text บน image โดยเฉพาะ ซึ่งจะสามารถกำหนด

  • ตำแหน่ง
  • font
  • ขนาดตัวอักษร
  • ความหนาตัวอักษร
  • สีของอักษร
def draw_text_on_image(img_draw, count_yellow, count_orange):
cv2.rectangle(img_draw, (0, 0), (500, 120), (0,0,0), -1)
cv2.putText(img_draw,'Orange Count : ' + str(count_orange),
(10,50), # bottomLeftCornerOfText
cv2.FONT_HERSHEY_SIMPLEX, # font
1.5, # fontScale
(0,255,255), # fontColor
2) # lineType
cv2.putText(img_draw,'Yellow Count : ' + str(count_yellow),
(10,100), # bottomLeftCornerOfText
cv2.FONT_HERSHEY_SIMPLEX, # font
1.5, # fontScale
(0,255,255), # fontColor
2) # lineType
return img_draw

Function main

ในฟังก์ชั่นนี้จะเป็นฟังก์ชั่นหลักที่เราจะกำหนดว่าเราจะทำอะไรก่อนหลัง โดยจะเรียงลำดับตามนี้ เพื่อให้เข้าใจได้ง่ายขึ้น อธิบายเป็น Flowchart น่าจะเข้าใจง่ายกว่า

Fig 5. flowchart ของโปรเจกนี้
def main():
path_img = ‘/workdir/siri/medium/Blog#4/images/IMG_2688.jpg’
img = load_image(path_img)
img = cv2.resize(img, None,fx=0.5,fy=0.5)
hsv = bgr2hsv(img)
img_draw = img
# define range of Yellow color in HSV
lower_ํYellow = np.array([20,100,100])
upper_Yellow = np.array([45,255,255])
mask = setRangeColor(hsv, lower_ํYellow, upper_Yellow)
contours = contours_img(mask)
color_bbox = (0, 0, 255)
img_draw, count_yellow = filter_contours_img(contours, img_draw, color_bbox)
print(‘Yellow Count:’, count_yellow)
# define range of Orange color in HSV
lower_Orange = np.array([0,150,150])
upper_Orange = np.array([20,255,255])
mask = setRangeColor(hsv, lower_Orange, upper_Orange)
contours = contours_img(mask)
color_bbox = (0, 255, 0)
img_draw, count_orange = filter_contours_img(contours, img_draw, color_bbox)
img_draw = draw_text_on_image(img_draw, count_yellow, count_orange)cv2.imwrite(‘/workdir/siri/medium/Blog#4/output/output.png’, img_draw)
print(‘Orange Count:’, count_orange)
Fig 6. result

ถ้าหากใครสนใจ Image Processing อีก สามารถอ่านการเพิ่มเติมได้ที่ลิ้งค์ด้างล่างนะครับ

Follow me.

--

--

Sr. Sorawit
DOLAB
Editor for

Data Experience @Guru Square • Software Engineer • From @Thailand • founder dolab • Website:http://dolab.cc/ Email:dolab.founder@gmail.com • Facebook:dolab