การตรวจจับสีอย่างง่าย (Simple colors detection)
การตรวจจับสี หรือ การตรวจวัดสี ของวัตถุภายในภาพ เป็นคำถามถูกที่ถูกถามเข้ามาบ่อยมาก
และผมต้องบอกเลยว่า การตรวจวัดสี ในสภาพสิ่งแวดล้อมแบบเปิด เช่น กลางแจ้ง หรือ ในห้องทั่วไป ไม่ใช่เรื่องง่ายๆ เลย พูดตามตรงคือ ยากถึงยากมาก เพราะปกติแล้วรูปภาพจะต้องเป็น 4 เหลี่ยมเสมอ เราไม่สามารถเลือกตัดภาพออกมาเป็นรูปอื่นๆ อย่างเช่น รูปวงกลม หรือรูปดาว เพราะว่ารูปภาพดิจิตอล มันก็คือ อาเรย์ของตัวเลข ชุดหนึ่ง และถ้าคุณผู้อ่านจำได้ กฎข้อแรกของการสร้างอาเรย์เลยก็คือ "ทุกแถวต้องมีคอลัภม์เท่ากัน และทุกคอมลัภม์ต้องมีแถวเท่ากัน" ดังนั้นมันจึงไม่สามารถเป็นรูปทรงอื่นได้นอกจาก 4 เหลี่ยม
และเมื่อภาพที่เราตัดมาได้เป็น 4 เหลี่ยม มันก็ต้องมีส่วนของพื้นหลัง ซึ่งเป็นสีอื่นๆ ที่ไม่ใช่สีของวัตถุที่เราสนใจ ถ้าเราเอาสีพวกนั้นมาคำนวณด้วย คำตอบที่ได้ก็ต้องเพี้ยนแน่นอน ดังนั้นจึงต้องหาวิธีการแยกพื้นหลังออกจากวัตถุที่เราสนใจให้ได้ ซึ่งก็ไม่ใช่เรื่องง่ายเท่าใดนัก
แต่ถ้าเป็นการตรวจจับสีของวัตถุที่มีรูปทรงไม่ซับซ้อน และอยู่ในสภาพแวดล้อมแบบปิด คือ ควบคุมแสงตลอดเวลา การตรวจจับสีก็พอจะทำได้ง่ายๆ ซึ่งบทความนี้ ผมจะแสดงวิธีการตรวจจับสีอย่างง่าย ให้ได้ชมกันครับ ซึ่งผู้อ่านสามารถนำวิธีการนี้ ไปปรับใช้กับรูปภาพที่อยู่ในสภาพแวดล้อมแบบปิด ได้ทุกประเภท (เพียงแต่ต้องวัดค่าสีเอง)
ก่อนอื่น เราต้องเตรียมรูปภาพที่จะใช้ทดสอบกันก่อนนะครับ ซึ่งในตัวอย่างนี้ ผมใช้ภาพนี้ ใครที่อยากทดลองรันก็ก็อปรูปนี้ไปใช้เลยนะครับ
ต่อไปเราก็มาเริ่มเขียนโปรแกรมกันเลยครับ
อย่างเช่นในรูปตัวอย่างนี้ ค่าที่วัดได้ก็คือ R 196 , G 24 , B 14 ซึ่งค่าพวกนี้ พอซ้อนทับกันตามลำดับ RGB จึงแสดงออกมาเป็นสีแดงอย่างที่ตาเรามองเห็น แต่ก็ไม่ใช่ว่าทุกจุดมันจะมีค่าเท่ากันนะครับ ลองเลื่อนดูหลายๆ จุด เพราะว่าพื้นผิวของลูกบอลแต่ละส่วนก็โดนแสงไม่เท่ากัน ดังนั้นค่าสีจึงไม่เท่ากัน
เมื่อเราจิ้มดูหลายๆ จุด เราก็จะเห็นค่าขอบเขตสีคร่าวๆ ของลูกบอลแต่ละลูก ซึ่งในตัวอย่างนี้ ผมวัดค่าสีของลูกบอลแต่ละลูกได้ดังนี้นะครับ
Red , R 200+ G <80 B <80
Blue, R <80 G <10 B 200+
Yellow R 200+ G 200+ B <50
(วิธีการอ่านนะครับ อย่างเช่น สีแดง มีค่า R มากกว่า 200 ขึ้นไป ค่า G น้อยกว่า 80 ค่า B น้อยกว่า 80)
จากนั้นเราก็เอาข้อมูลพวกนี้ลงไปเขียนไว้ในโค้ดครับ ซึ่งในตอนท้าย เราจะใช้เป็นเงื่อนไขในการพิจารณาสี
โดยในการหาขอบเขตของลูกบอลแต่ละลูกนั้น ผมจะใช้วิธีการทาง image processing คือ แปลงภาพสีเป็นขาวดำก่อน แล้วหลังจากนั้นใช้คำสั่ง regionprops เพื่อหาขอบเขตของวัตถุในภาพขาวดำ
ผลรันที่ได้
ซึ่งสีขาวนี้ในทางภาพดิจิตอล มันมีค่าสูงถึง 255 ทั้ง 3 เลเยอร์เลย ดังนั้นหากนำมันมาคำนวณด้วย ผลที่ได้ต้องผิดเพี้ยนแน่นอน ดังนั้น ผมจะแก้ปัญหาโดยการคำนวณแค่ส่วนที่เป็นพื้นที่ของลูกบอลนะครับ โดยการทำให้พื้นหลังมีค่าเป็น 0 หลังจากนั้นดึงข้อมูลสีออกมาทีละเลเยอร์ ซึ่งเราจะได้ข้อมูลเป็นอาเรย์ 2 มิติ จากนั้นแปลงมันให้เป็น 1 มิติ แล้วดึงแค่ตำแหน่งที่มีค่ามากกว่า 0 ออกมาหาค่าเฉลี่ยครับ
เมื่อหาค่าเฉลี่ยของแต่ละเลเยอร์ได้แล้ว เราก็เอาไปเปรียบเทียบกับข้อมูลที่เราวัดได้ในขั้นตอนที่ 2 ซึ่งในการเปรียบเทียบ ก็ใช้คำสั่ง if ธรรมดานี่แหละครับ แค่นั้นเราก็ได้คำตอบออกมาแล้ว
จากนั้นเมื่อเราลองรันโปรแกรมดู ก็จะเห็นผลรันดังนี้ครับ
จะเห็นว่าโปรแกรมของเราบอกสีของลูกบอลแต่ละลูกได้อย่างถูกต้องแล้วนะครับ ถ้าใครทำแล้วไม่ได้ผลตามนี้ ก็ลองไปวัดค่าสีดูอีกรอบนะครับ วัดหลายๆ จุด จะได้เห็นขอบเขตของสีทั้งหมด เวลาเอามาเขียนเป็นเงื่อนไขจะได้ครอบคลุม
เมื่อเราเอาโค้ดของทุกขั้นตอนมารวมกัน ก็จะได้ดังนี้ครับ
จะเห็นว่าถึงแม้ตัวอย่างนี้จะเป็นแค่ตัวอย่างแบบง่ายๆ แต่โค้ดยาวไม่น้อยเลยนะครับ และอัลกอริทึมก็ซับซ้อนพอสมควร ดังนั้นหากใครไม่เข้าใจ ก็แสดงว่าคุณยังมีความรู้ด้านภาพดิจิตอล และอาเรย์ไม่เพียงพอ ลองไปศึกษา 2 เรื่องนี้เพิ่มเติมดูนะครับ
สำหรับใครที่คิดว่ามันยาก ผมจะพูดอีกรอบนะครับว่า image processing ไม่ใช่ โปรแกรมที่มือใหม่ ควรจะใช้ฝึกหัดเขียน หากคุณเพิ่งหัดเขียน MATLAB ผมแนะนำให้คุณไปหัดเขียนโปรแกรมคำนวณเกรดให้ได้ก่อนเป็นอันดับแรกเลยครับ แล้วหลังจากนั้นไปหาอ่านเรื่องภาพดิจิตอลมาให้เยอะๆ เพราะถ้าคุณไม่มีความรู้เรื่องภาพดิจิตอล คุณก็จะงงเหมือนเดิม และที่สำคัญภาพดิจิตอล ไม่ได้มีแค่ภาพ RGB นะครับ ยังมีภาพประเภทอื่นๆ เช่น HSV ด้วย ซึ่งวิธีการก็จะแตกต่างกันออกไปอีก หรือแม้แต่ภาพ RGB นั้นก็ยังแบ่งย่อยออกเป็นหลายประเภท ตามที่เราเรียกว่า นามสกุลของภาพนั่นแหละครับ เช่น jpg , png , bmp , gif
ถ้าคุณยังไม่เข้าใจว่านามสกุลพวกนี้มันทำให้ภาพต่างกันยังไง แสดงว่าคุณยังไม่เข้าใจเรื่องภาพดิจิตอลนะครับ ให้ลองกลับไปอ่านดูอีกรอบ
สุดท้าย หวังว่าบทความนี้จะพอเป็นแนวทางให้ศึกษา หรือนำไปประยุกต์ใช้ในงานของคุณได้นะครับ
และผมต้องบอกเลยว่า การตรวจวัดสี ในสภาพสิ่งแวดล้อมแบบเปิด เช่น กลางแจ้ง หรือ ในห้องทั่วไป ไม่ใช่เรื่องง่ายๆ เลย พูดตามตรงคือ ยากถึงยากมาก เพราะปกติแล้วรูปภาพจะต้องเป็น 4 เหลี่ยมเสมอ เราไม่สามารถเลือกตัดภาพออกมาเป็นรูปอื่นๆ อย่างเช่น รูปวงกลม หรือรูปดาว เพราะว่ารูปภาพดิจิตอล มันก็คือ อาเรย์ของตัวเลข ชุดหนึ่ง และถ้าคุณผู้อ่านจำได้ กฎข้อแรกของการสร้างอาเรย์เลยก็คือ "ทุกแถวต้องมีคอลัภม์เท่ากัน และทุกคอมลัภม์ต้องมีแถวเท่ากัน" ดังนั้นมันจึงไม่สามารถเป็นรูปทรงอื่นได้นอกจาก 4 เหลี่ยม
และเมื่อภาพที่เราตัดมาได้เป็น 4 เหลี่ยม มันก็ต้องมีส่วนของพื้นหลัง ซึ่งเป็นสีอื่นๆ ที่ไม่ใช่สีของวัตถุที่เราสนใจ ถ้าเราเอาสีพวกนั้นมาคำนวณด้วย คำตอบที่ได้ก็ต้องเพี้ยนแน่นอน ดังนั้นจึงต้องหาวิธีการแยกพื้นหลังออกจากวัตถุที่เราสนใจให้ได้ ซึ่งก็ไม่ใช่เรื่องง่ายเท่าใดนัก
แต่ถ้าเป็นการตรวจจับสีของวัตถุที่มีรูปทรงไม่ซับซ้อน และอยู่ในสภาพแวดล้อมแบบปิด คือ ควบคุมแสงตลอดเวลา การตรวจจับสีก็พอจะทำได้ง่ายๆ ซึ่งบทความนี้ ผมจะแสดงวิธีการตรวจจับสีอย่างง่าย ให้ได้ชมกันครับ ซึ่งผู้อ่านสามารถนำวิธีการนี้ ไปปรับใช้กับรูปภาพที่อยู่ในสภาพแวดล้อมแบบปิด ได้ทุกประเภท (เพียงแต่ต้องวัดค่าสีเอง)
ก่อนอื่น เราต้องเตรียมรูปภาพที่จะใช้ทดสอบกันก่อนนะครับ ซึ่งในตัวอย่างนี้ ผมใช้ภาพนี้ ใครที่อยากทดลองรันก็ก็อปรูปนี้ไปใช้เลยนะครับ
ต่อไปเราก็มาเริ่มเขียนโปรแกรมกันเลยครับ
ขั้นตอนที่ 1 อ่านไฟล์ภาพและแสดงผล
ในขั้นตอนนี้ผมใช้คำสั่ง uigetfile เพื่อให้ user ได้เลือกภาพเองตามใจชอบครับ ดังนั้นเวลาก็อปปี้ไปใช้งาน จึงไม่จำเป็นต้องแก้ไขโค้ด%% 1st step: Select and open test image [fn,pt] = uigetfile({'*.png','PNG'},'Select image'); if(fn==0) return; end ffn = fullfile(pt,fn); pic = imread(ffn); figure; imshow(pic);
ขั้นตอนที่ 2 วัดค่าสีในภาพ
เมื่อเรารันโค้ดในขั้นตอนที่ 1 โปรแกรมก็จะเปิดรูปภาพขึ้นมา ให้เราใช้ Cursor Data ใน figure ไปจิ้มที่รูปตามจุดต่างๆ เพื่อวัดค่าสีนะครับอย่างเช่นในรูปตัวอย่างนี้ ค่าที่วัดได้ก็คือ R 196 , G 24 , B 14 ซึ่งค่าพวกนี้ พอซ้อนทับกันตามลำดับ RGB จึงแสดงออกมาเป็นสีแดงอย่างที่ตาเรามองเห็น แต่ก็ไม่ใช่ว่าทุกจุดมันจะมีค่าเท่ากันนะครับ ลองเลื่อนดูหลายๆ จุด เพราะว่าพื้นผิวของลูกบอลแต่ละส่วนก็โดนแสงไม่เท่ากัน ดังนั้นค่าสีจึงไม่เท่ากัน
เมื่อเราจิ้มดูหลายๆ จุด เราก็จะเห็นค่าขอบเขตสีคร่าวๆ ของลูกบอลแต่ละลูก ซึ่งในตัวอย่างนี้ ผมวัดค่าสีของลูกบอลแต่ละลูกได้ดังนี้นะครับ
Red , R 200+ G <80 B <80
Blue, R <80 G <10 B 200+
Yellow R 200+ G 200+ B <50
(วิธีการอ่านนะครับ อย่างเช่น สีแดง มีค่า R มากกว่า 200 ขึ้นไป ค่า G น้อยกว่า 80 ค่า B น้อยกว่า 80)
จากนั้นเราก็เอาข้อมูลพวกนี้ลงไปเขียนไว้ในโค้ดครับ ซึ่งในตอนท้าย เราจะใช้เป็นเงื่อนไขในการพิจารณาสี
%% 2nd step: record color data from pick data cursor % Red , R 200+ G <80 B <80 % Blue, R <80 G <10 B 200+ % Yellow R 200+ G 200+ B <50 Red = [200 80 80]; Blue = [80 30 200]; Yellow = [200 200 50];
ขั้นตอนที่ 3 หาขอบเขตของวัตถุในภาพ
อย่างที่ผมได้อธิบายไปในตอนต้นนั่นแหละครับ โดยปกติแล้ววัตถุที่เราสนใจ มันไม่ได้ถ่ายมาให้เห็นเต็มภาพหรอก อย่างในตัวอย่างนี้ในภาพทดสอบก็มีลูกบอลอยู่ตั้ง 3 ลูก ดังนั้นเราจึงต้องหาขอบเขตที่แน่นอนของลูกบอลแต่ละลูกก่อน แล้วค่อยตัดภาพเฉพาะส่วนนั้นออกมาวิเคราะห์ทีละภาพโดยในการหาขอบเขตของลูกบอลแต่ละลูกนั้น ผมจะใช้วิธีการทาง image processing คือ แปลงภาพสีเป็นขาวดำก่อน แล้วหลังจากนั้นใช้คำสั่ง regionprops เพื่อหาขอบเขตของวัตถุในภาพขาวดำ
%% 3rd step: find object boundary bw = im2bw(pic,0.9); nbw = not(bw); %invert color bbx = regionprops(nbw,'BoundingBox'); BX = cat(1,bbx(:).BoundingBox); IX = insertShape(pic,'Rectangle',BX); figure; imshow(nbw) figure; imshow(IX);
ผลรันที่ได้
ขั้นตอนที่ 4 คำนวณค่าเฉลี่ยสี
เมื่อเราทราบขอบเขตของลูกบอลแต่ละลูกแล้ว ต่อไปเราก็ต้องมาคำนวณค่าเฉลี่ยของสี ในแต่ละเลเยอร์ของภาพ (อย่าลืมนะครับว่าภาพสีมี 3 เลเยอร์ คือ RGB) แต่ก็อย่างที่ผมพูดไปข้างต้นนั่นแหละครับว่า เราไม่สามารถเลือกตัดภาพแค่ส่วนวงกลมของลูกบอลได้ ยังไงก็ต้องตัดมาเป็น 4 เหลี่ยม ดังนั้นผมจึงใช้กรอบ 4 เหลี่ยม แทนกรอบวงกลม เพื่อให้คุณผู้อ่านเห็นว่า เมื่อเราตัดลูกบอลออกมาทีละลูกแล้ว ก็ยังมีส่วนพื้นหลังที่เป็นสีขาว อยู่ตามมุมต่างๆ ทั้ง 4 มุมนะครับซึ่งสีขาวนี้ในทางภาพดิจิตอล มันมีค่าสูงถึง 255 ทั้ง 3 เลเยอร์เลย ดังนั้นหากนำมันมาคำนวณด้วย ผลที่ได้ต้องผิดเพี้ยนแน่นอน ดังนั้น ผมจะแก้ปัญหาโดยการคำนวณแค่ส่วนที่เป็นพื้นที่ของลูกบอลนะครับ โดยการทำให้พื้นหลังมีค่าเป็น 0 หลังจากนั้นดึงข้อมูลสีออกมาทีละเลเยอร์ ซึ่งเราจะได้ข้อมูลเป็นอาเรย์ 2 มิติ จากนั้นแปลงมันให้เป็น 1 มิติ แล้วดึงแค่ตำแหน่งที่มีค่ามากกว่า 0 ออกมาหาค่าเฉลี่ยครับ
เมื่อหาค่าเฉลี่ยของแต่ละเลเยอร์ได้แล้ว เราก็เอาไปเปรียบเทียบกับข้อมูลที่เราวัดได้ในขั้นตอนที่ 2 ซึ่งในการเปรียบเทียบ ก็ใช้คำสั่ง if ธรรมดานี่แหละครับ แค่นั้นเราก็ได้คำตอบออกมาแล้ว
%% 4th step: Calculate average color value r = size(BX,1); CLR = cell(1,r); %temp answer for m=1:r rect = BX(m,:); img = imcrop(pic,rect); bw1 = imcrop(nbw,rect); k = reshape(bw1,1,[]); R = img(:,:,1); G = img(:,:,2); B = img(:,:,3); O = reshape(R,1,[]); P = reshape(G,1,[]); Q = reshape(B,1,[]); X = O(k); Y = P(k); Z = Q(k); rd = mean(X); % red gn = mean(Y); % green yw = mean(Z); % blue colors = 'Undefine'; if(rd>Red(1) && gn<Red(2) && yw<Red(3)) colors = 'RED'; end if(rd<Blue(1) && gn<Blue(2) && yw>Blue(3)) colors = 'BLUE'; end if(rd>Yellow(1) && gn>Yellow(2) && yw<Yellow(3)) colors = 'Yellow'; end CLR{m} = colors; end
ขั้นตอนที่ 5 แสดงผล
ในที่สุดก็มาถึงขั้นตอนสุดท้ายแล้วครับ เราก็มาแสดงผลดูว่าโปรแกรมที่เราเขียนมานั้น มันทำงานได้ถูกต้อง และให้ผลลัพธ์ตามที่เราออกแบบไว้หรือไม่ ซึ่งในการแสดงผล ก็ไม่มีอะไรยุ่งยากครับ เราแค่อยากรู้ว่าโปรแกรมเห็นลูกบอลทั้ง 3 ลูกเป็นสีอะไร ดังนั้นเราก็แค่ต้องแสดงตัวอักษรบนลูกบอลทั้ง 3 ลูกก็พอครับ ซึ่งในที่นี้ ผมใช้คำสั่ง insertObjectAnnotation เพื่อเขียนข้อความกำกับ object ในภาพ%% 5th step: Display answer RGB = insertObjectAnnotation(pic,'rectangle',BX,CLR,'TextBoxOpacity',0.9,... 'FontSize',18); figure; imshow(RGB);
จากนั้นเมื่อเราลองรันโปรแกรมดู ก็จะเห็นผลรันดังนี้ครับ
จะเห็นว่าโปรแกรมของเราบอกสีของลูกบอลแต่ละลูกได้อย่างถูกต้องแล้วนะครับ ถ้าใครทำแล้วไม่ได้ผลตามนี้ ก็ลองไปวัดค่าสีดูอีกรอบนะครับ วัดหลายๆ จุด จะได้เห็นขอบเขตของสีทั้งหมด เวลาเอามาเขียนเป็นเงื่อนไขจะได้ครอบคลุม
เมื่อเราเอาโค้ดของทุกขั้นตอนมารวมกัน ก็จะได้ดังนี้ครับ
% ----------------------- MATLAB Programing --------------------------% % Example: Simple color detection method % % Create by: Kritthanit % % Create date: 07-01-2019 % % Blog: https://loglike.blogspot.com % % Fanpage: https://www.facebook.com/Matlab-Programing-194695677296190 % % --------------------------------------------------------------------% clc;clear;close all; %% 1st step: Select and open test image [fn,pt] = uigetfile({'*.png','PNG'},'Select image'); if(fn==0) return; end ffn = fullfile(pt,fn); pic = imread(ffn); figure; imshow(pic); %% 2nd step: record color data from pick data cursor % Red , R 200+ G <80 B <80 % Blue, R <80 G <10 B 200+ % Yellow R 200+ G 200+ B <50 Red = [200 80 80]; Blue = [80 30 200]; Yellow = [200 200 50]; %% 3rd step: find object boundary bw = im2bw(pic,0.9); nbw = not(bw); %invert color bbx = regionprops(nbw,'BoundingBox'); BX = cat(1,bbx(:).BoundingBox); IX = insertShape(pic,'Rectangle',BX); figure; imshow(nbw) figure; imshow(IX); %% 4th step: Calculate average color value r = size(BX,1); CLR = cell(1,r); %temp answer for m=1:r rect = BX(m,:); img = imcrop(pic,rect); bw1 = imcrop(nbw,rect); k = reshape(bw1,1,[]); R = img(:,:,1); G = img(:,:,2); B = img(:,:,3); O = reshape(R,1,[]); P = reshape(G,1,[]); Q = reshape(B,1,[]); X = O(k); Y = P(k); Z = Q(k); rd = mean(X); % red gn = mean(Y); % green yw = mean(Z); % blue colors = 'Undefine'; if(rd>Red(1) && gn<Red(2) && yw<Red(3)) colors = 'RED'; end if(rd<Blue(1) && gn<Blue(2) && yw>Blue(3)) colors = 'BLUE'; end if(rd>Yellow(1) && gn>Yellow(2) && yw<Yellow(3)) colors = 'Yellow'; end CLR{m} = colors; end %% 5th step: Display answer RGB = insertObjectAnnotation(pic,'rectangle',BX,CLR,'TextBoxOpacity',0.9,... 'FontSize',18); figure; imshow(RGB);
จะเห็นว่าถึงแม้ตัวอย่างนี้จะเป็นแค่ตัวอย่างแบบง่ายๆ แต่โค้ดยาวไม่น้อยเลยนะครับ และอัลกอริทึมก็ซับซ้อนพอสมควร ดังนั้นหากใครไม่เข้าใจ ก็แสดงว่าคุณยังมีความรู้ด้านภาพดิจิตอล และอาเรย์ไม่เพียงพอ ลองไปศึกษา 2 เรื่องนี้เพิ่มเติมดูนะครับ
สำหรับใครที่คิดว่ามันยาก ผมจะพูดอีกรอบนะครับว่า image processing ไม่ใช่ โปรแกรมที่มือใหม่ ควรจะใช้ฝึกหัดเขียน หากคุณเพิ่งหัดเขียน MATLAB ผมแนะนำให้คุณไปหัดเขียนโปรแกรมคำนวณเกรดให้ได้ก่อนเป็นอันดับแรกเลยครับ แล้วหลังจากนั้นไปหาอ่านเรื่องภาพดิจิตอลมาให้เยอะๆ เพราะถ้าคุณไม่มีความรู้เรื่องภาพดิจิตอล คุณก็จะงงเหมือนเดิม และที่สำคัญภาพดิจิตอล ไม่ได้มีแค่ภาพ RGB นะครับ ยังมีภาพประเภทอื่นๆ เช่น HSV ด้วย ซึ่งวิธีการก็จะแตกต่างกันออกไปอีก หรือแม้แต่ภาพ RGB นั้นก็ยังแบ่งย่อยออกเป็นหลายประเภท ตามที่เราเรียกว่า นามสกุลของภาพนั่นแหละครับ เช่น jpg , png , bmp , gif
ถ้าคุณยังไม่เข้าใจว่านามสกุลพวกนี้มันทำให้ภาพต่างกันยังไง แสดงว่าคุณยังไม่เข้าใจเรื่องภาพดิจิตอลนะครับ ให้ลองกลับไปอ่านดูอีกรอบ
สุดท้าย หวังว่าบทความนี้จะพอเป็นแนวทางให้ศึกษา หรือนำไปประยุกต์ใช้ในงานของคุณได้นะครับ