Cảm biến 3D – TOF

Từ khi ipornX Iphone X của Apple được giới thiệu, cảm biến 3D trở thành 1 hot keyword, làm cho nhà nhà đua nhau tích hợp 3D vào điện thoại, người người thi nhau mua điện thoại có cảm biến 3D. Ông trùm SONY về mảng điện tử một thời của Nhật, mà hiện giờ mảng cảm biến đang mang lại một trong những nguồn sống chính, đã nhanh chân mở ví ra mua luôn Softkinetic, một startup của Bỉ với tầm 77 nhân sự vào 2015.
Tôi vô tình cũng bị cuốn vào xu thế đó khi tham gia dự án làm platform & driver cho con hợi này. Do đó vừa học vừa viết lại đôi điểm cho đỡ quên, nội dung rút ra từ tài liệu [1] ở phần tham khảo. Có thể học thêm về TOF ở kho tài liệu của Texas Instrument [2], rất phong phú và chi tiết.

Trong vấn đề xử lý hình ảnh, việc thu và tái hiện lại hình 3D không phải vấn đề gì mới, trước TOF đã có 2 phương pháp được sử dụng để tái tạo hình ảnh 3D.
Stereo Vision : Sử dụng 2 camera để tái hiện lại 3D, vị trí của 1 điểm sẽ được xác định nhờ vị trí tương đối của nó đối với 2 camera. Phương pháp này sử dụng tương tự hệ thống mắt người.
stereo-vision
Nguyên lý Stereo Vision (Ảnh lấy trong tài liệu[1])

Structured-light: Phương pháp này sử dụng 1 chùm tia sáng với mô hình xác định trước (dạng lưới, quét ngang …) , để chiếu lên vật thể cần quan sát, thu lại hình ảnh khi tia sáng đập lên bề mặt vật thể để xây dựng lại hình ảnh 3D của nó.
structure-ligh
Nguyên lý Structured-light  (Ảnh lấy trong tài liệu[1])
Vậy TOF là gì? TOF là viết tắt của Time Of Flight, dựa trên nguyên lý đo khoảng cách bằng sóng điện từ (sóng ánh sáng ở dải mắt người không nhìn thấy). Trong hệ thống TOF, sử dụng 1 nguồn phát sóng ánh sáng, quan sát nguồn phản hồi từ đối tượng quan sát, bằng cách đo thời gian từ lúc phát ra đến lúc phản hồi thì sẽ tính được vị trí của đối tượng đó.
tof
Nguyên lý TOF (Ảnh lấy trong tài liệu[1])
Dễ thấy nguyên lý của TOF giống hệt như dùng sóng siêu âm để đo độ sâu đáy biển, tất nhiên từ lý thuyết đến thực tiễn là 1 khoảng cách không nhỏ về thời gian cũng như tiền bạc.

Bảng dưới đây cho thấy hình ảnh tổng quan của TOF khi so sánh với 2 kỹ thuật còn lại. Công nghệ thay đổi chóng mặt chả khác gì “chó chạy ngoài đồng” , nên cần chú ý rằng thông tin trong bảng này chỉ đúng với tầm thời điểm hiện tại (~2018).
tof

So sánh TOF với Stereo Vision và Structured-light (Ảnh lấy trong tài liệu[1])

Tham khảo:
[1] http://www.ti.com/lit/wp/sloa190b/sloa190b.pdf
[2] http://www.ti.com/3dtof
[3] https://en.wikipedia.org/wiki/Structured_light
[4] https://en.wikipedia.org/wiki/Time-of-flight_camera
[5] https://en.wikipedia.org/wiki/Computer_stereo_vision

Biên dịch Linux OS từ mã nguồn

Bắt đầu đi làm vào giữa 2009, tôi bắt đầu được tiếp xúc với Linux, nhưng trải qua gần 9 năm từ đó đến nay, hiểu biết về Linux rất ổn định như tham nhũng, không có gì thay đổi. Lý do thứ nhất là vì các dự án làm với Linux chỉ là sử dụng môi trường Linux để phát triển, nên cơ bản chỉ biết cái Makefile là xong, chẳng lúc nào cần mó tay một cách chỉnh chu thấu đáo vào các thứ như device driver, hay memory management …, để biết cái gì đủ hay ho mà kể chuyện. Thật ra là có một chút nhưng không ăn thua, mấy thứ cơ bản như system call, char driver, block driver … thì nó phổ cập cmnr, với lại khoảng cách giữa biết tên và biết để kể chuyện được nó cũng quá xa. Lý do thứ 2 là lười & nhác tìm hiểu.

Đang ôn tập lại chút kiến thức để chuẩn bị do dự án làm driver trên Linux, thì bắt gặp trang dưới (ko hiểu sao giờ mới biết, chắc lại do nhác & lười).
http://linuxfromscratch.org/lfs

Trước giờ hay có sở thích, là thử distribution (Linux OS) của Linux, hết Ubuntu đến Backtrack, với mục đích rất đáng sợ là để học sâu thêm về Linux nhưng cài xong  chiêm ngưỡng cái desktop xong  rồi thì sau đó là chỉ để duyệt web ~.~

Việc hay thử Linux OS, nó cũng bắt nguồn từ hồi mới ra trường, được bố mẹ bán hết lúa gạo lợn gà cho mua con hp pavilion, không nhớ chính xác tên nhưng giá là 12 hay 17 chai gì đấy. Đen đủi thế nào lúc ra lúc bắt đầu đi làm 1 năm thì bị trộm nó thó mất, lương thấp ăn chưa đủ nói gì đến mua lại máy. May thay ông anh trong dự án có con T40 thinkpad hỏng main đang đắp chiếu, bảo cầm về sửa mà dùng. Sau thay main cũ hết 1 triệu, cài luôn ubuntu, có hơi rùa bò tí những nó vẫn dùng để kết nối tốt với internet. Vì rùa bò nên nghĩ ra việc dùng các bản Linux OS cho máy cấu hình thấp, nhưng rồi cũng thấy chẳng ăn thua lắm nên mới sinh ra trò thử cài Ubuntu xong gỡ hết gói màu mè về giao diện ra thì tốc độ có thấy cải thiện kha khá. Từ đó có thêm trò ngược đời là cài xong Linux OS thì gỡ bớt gói không cần thiết ra, đôi khi không biết nên gỡ quá tay làm nó chẳng start up luôn được cả giao diện =]]. Dùng được hơn 1 năm thì T40 cũng đi vì cái màn hình hỏng.

Đến 2011, đi công tác ở Nhật, nhịn ăn nên cũng mua được con CF-W5MW8AJR Panasonic đồ cũ cùi bắp, mua xong cài luôn Ubuntu đè lên con Win Vista bản quyền luôn.
Riêng con này từ lúc mua ngoài làm mất thứ vớ vẩn thì cũng kiếm đc chút từ việc
data hiding, do muốn mua con len Nikon 50mm f/1.8G, nhưng lương chỉ đủ nuôi con nên đành kiếm tí việc đánh thêm, đỡ phạm đến phần tiền mua sữa cho con 😦

Nói dài dòng, nhưng tóm lại giờ có con CF5 cùi bắp, tính  cho mấy thằng em ở quê, nhưng chắc là nó không thèm lấy, mà công nhận đồ của Panasonic rất chất, nhẹ, pin trâu vẫn duy trì hơn tầm 1.5h, phím bấm cực đã không bị rung, ai dùng dòng Let’s Note của Panasonic rồi thì biết, chỉ có điều giá trên trời. Như  đề cập ở trên, tự dưng biết 1 dự án rất có tâm, hướng dẫn biên dịch hẳn Linux OS từ source code (có khi google đủ các kiểu + bỏ thời gian ra chắc cũng tự build được), tóm lại từ Linux kernel, đến các gói mã nguồn GNU & non-GNU …

Dù là việc biên dịch chỉ là việc chân tay, nhưng trải nghiệm 1 lần biên dịch ra OS từ mã nguồn nó cũng hay hay, dù sao cũng tạo hứng thú vượt qua lười & nhác để có thể bắt đầu tìm hiểu thêm chút về Linux. Thêm vào đó hi vọng do tự điều chỉnh được các gói cần thiết nên sẽ có được con OS nhỏ gọn để chạy với cái đồ cùi bắp như CF-W5MW8AJR.

Có 1 điểm chú ý khi bắt đầu biên dịch là nên chuẩn bị 1 con USB stick hoặc CD live có thể chạy live Linux OS nào đó (ex: ubuntu), để phòng trường hợp lỗi khi chia ổ cứng này nọ thì còn có cách mà xử lý.
Về phần dữ liệu quan trọng trong máy thì theo tôi không cần phải backup, vì nếu bị lỗi thì có cơ hội học cách cứu dữ liệu, nếu cứu không được thì coi như cái giá phải trả để nhớ bài học hơn =]].

Raspberry PI transcodes video

1. Transcode là gì.
Transcode là quá trình chuyển đổi từ định dạng mã hóa hiện tại sang một dạng mã hóa khác, ví dụ như chuyển đổi video, audio coding format …
Thông thường thì việc thực hiện transcode có 2 lý do chính:
– Giảm dung lượng lưu trữ mà có thể đảm bảo được chất lượng video, bằng việc sử dụng video coding format tân tiến hơn, tăng khả năng nén hơn, nên các dữ liệu video đang sử dụng coding format cũ thường chuyển sang format mới để giảm dung lượng lưu trữ.
– Thiết bị đầu cuối không hỗ trợ video or audio hiện tại, do đó cần convert sang cái dạng mà nó hỗ trợ.

Trên thực tế hiện nay thì dễ thấy nhất là khi upload 1 file video lên facebook hoặc youtube, thì file video đó luôn được youtube/facebook xử lý thành định dạng khác trong quá trình upload.

Cơ bản thì quá trình transcode có thể hình dung đơn giản như dưới.

input file/stream → |decode| → |encode| → output file/stream

Từ dữ liệu đầu vào, có thể là multimedia file format (MKV, MP4 …) hoặc stream (udp, tcp/ip …). Bước đầu tiên là giải mã (decode) thành phần đầu vào (video or audio tùy thuộc vào cái nào cần transcode) thành raw data. Tiếp đó thực hiện mã hóa (encode) thành định dạng mới ở dữ liệu đầu ra.

Transcode có thể realtime or non-realtime tùy thuộc vào từng yêu cầu của hệ thống. Trong các dữ liệu cần transcode thì video là phức tạp, tốn nhiều hiện năng nhất, và thường quan tâm nhất , do việc nén video theo chuẩn mới có thể tiết kiệm rất nhiều dung lượng or bitrate.

2. Video decoder/encoder trên RPI.
Raspberry pi(RPI) hỗ trợ cả hardware decoder lẫn encoder, do đó rất phù hợp nếu sử dụng nó để thực hiện transcode.
2 bộ decoder và encoder trên RPI được thiết kế theo API của openmax mà cụ thể là openmax-il.
Openmax là chuẩn các api do khronos đưa ra để thiết kế hệ thống xử  lý multimedia, nó gồm có 3 phần tương được với 3 layer, nhìn hình dưới lấy từ trang chủ có nó thì hiểu, khỏi cần giải thích.
media_portability
Cái gì mà được chuẩn hoá thì mục đích cao nhất của nó là giúp giảm thời gian tích hợp, 2 thằng không cần biết nhau trước đó mà chỉ cần cùng tuân theo 1 chuẩn là dễ dàng tích hợp với nhau.
Openmax-il có thể hiểu là lớp trung gian giữa phần cứng (hardware) và application hay còn gọi là firmware, đây cũng là phần API được sử dụng rộng rãi nhất của openmax. Android media framework, ffmpeg, gstreamer đều đã hỗ trợ openmax-il với tư cách là IL client, tức là layer ngay ở trên openmax-il . Do đó nếu bạn rảnh rỗi thiết kế 1 con chip hỗ trợ video decoder tuân thủ theo đúng api của openmax-il thì bạn có thể nhanh chóng tích hợp được vào các hế thống trên :-), và bán con chip đó luôn được để kiếm xèng.

Chi tiết openmax thì vào trang chủ của nó để tham khảo ở đây[3].
Thông tin các openmax-il component trên RPI thì tham khảo chỗ này[5].

3. Thực hiện transcode video trên RPI.
3.1. Thực hành transcode với RPI.
Omxplayer là cli hoạt động rất ổn định khi dùng play video trên RPI. Mình vẫn dùng để mở clip tàu điện Nhật Bản phục vụ ông con thích tàu xe.
Omxplayer sử dụng hardware decoder của RPI, và tất nhiên là thông qua openmax-il, cấu trúc của nó như hình dưới.
dec_rpi

Xuất phát từ ý tưởng dùng RPI để chuyển đổi video streaming mpeg2-video đang có sẵn thành h.264 để giảm bitrate nhằm tiết kiệm băng thông. Dựa trên omxplayer
với một chút sửa đổi nhỏ sau:
– Bỏ audio decoder, vì audio chiếm băng thông không đáng kể, không cần thiết transcode nên audio vào thế nào cho ra thế đó.
– Cắt bỏ phần xử lý hiện thị (render component), vì chỉ cần chuyển đổi sang video format khác, kết quả sẽ streaming hoặc lưu thành file.
– Thêm component encoder, nối vào ngay sau decoder. Để thực hiện encode lại ảnh, kết quả của bộ decoder sau khi giải mã đầu vào.
– Thêm phần xử lý output streamer, module này có nhiệm vụ lưu audio + video sau khi convert thành file hoặc streaming qua udp hoặc tcp/ip hoặc protocol nào đấy.

Sau khi sửa đổi thì cấu trúc nó thay hình đổi dạng như dưới.
transcode_rpi

Source code & readme hướng dẫn sử dụng chứa ở đây

Kết quả chạy thử, đạt được realtime và bitrate giảm 1 nửa.
input http streaming: mpeg2-video,size 720×576, 25fps, bitrate 4mbps.
output udp streaming: h.264, size 720×576, 25fps, bitrate 2mbps.

Khi play stream gốc và stream sau khi đã transcode thì chất lượng theo đánh giá chủ quan là tương đương nhau. Cần chú ý rằng, đây chỉ là về mặt visual, còn nén video là nén mất dữ liệu (lossly) do đó cái output không bao giờ bằng được input nếu nói trên quan điểm thông tin.

●Hiện tại đang có các điểm hạn chế cần điều tra và cải thiện:
– Chạy được tầm 1h ~ 2h thì tự dưng lăn quay là treo → bug cần điều tra.
– Openmax-il có định nghĩa 2 kiểu kết nối để component truyền data cho nhau, là tunnel (kết nối trực tiếp) và non-tunnel (kết nối gián tiếp thông qua IL client). Định dùng tunnel cho đơn giản, nhưng thử mãi không được nên đang dùng non-tunnel, thành ra quản lý buffer đang hơi stupid nên output buffer của decoder được copy sang encoder. → Cần điều tra lại đển dùng tunnel hoặc improve việc quản lý buffer để bỏ xử lý copy.
–  Phần output streamer module cần design là 1 thread độc lập, nhận video/audio buffer ở component trước đó thông qua queue, nhưng hiện tại đang thực hiện như bằng context của video & audio (được buffer nào thì streaming luôn).

3.2. Dùng ffmpeg.
Đợt này có chút thời gian, định quay lại xử lý mấy hạn chế & bug của 3.1 thì mới để ý thấy ffmpeg nó cũng support openmax rồi, như vậy đơn giản thế này thôi.
– Clone ffmpeg và tham khảo chỗ này để enable omx khi compile.
– Nếu ngại complile từ source code thì có thể cài đặt ffmpeg thông qua repo của RPI, package đã được enable omx: sudo apt-get install ffmpeg

Dùng command dưới là xong :-).

ffmpeg -c:v mpeg2_mmal -i http://address_input -c:a copy -c:v h264_omx -b:v 2000k -f mpegts udp://address:port

Tuy nhiên có vẻ có vấn đề với input streaming interlace, ngoài ra chưa stress test nên chưa biết thế nào. Từ POC (proof-of-concept) đến Mass Product là một khoảng cách lớn, haizz …

●24/07/2018 update: Input stream đang sử dụng là interlace, trong khi đó bộ encoder h.264 trên RPI không support interlace nên aspect ratio output stream bị sai. Có thể cần xử lý de-interlace trước khi encode. Hoặc với video hiện tại thì có thể dùng encoder bằng software encoder (libx264) như command dưới, sử dụng option encode performance gần cao nhất, với video size 720×576 thì vẫn đạt được realtime, tuy nhiên con chip nóng quá, không dám chạy stress-test vì sợ nó ngỏm.

ffmpeg -c:v mpeg2_mmal -i http://address_input
-c:v libx264 -preset superfast -f mpegts -b:v 2000k -s 720×576
udp://address:port

P/S: Lưu ý, RPI mặc định chưa enable hardware mpeg2-video codec, muốn dùng phải bỏ tiền ra mua ở đây.

Tham khảo.
[1]https://en.wikipedia.org/wiki/Transcoding
[2]https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=72260
[3]https://www.khronos.org/openmax/
[4]https://source.android.com/devices/media/
[5]http://www.jvcref.com/files/PI/documentation/ilcomponents/
[6]https://github.com/popcornmix/omxplayer
[7]https://github.com/truongpt/rpi_transcode

Từ khóa register trong C

Trong lập trình nhúng (embedded programming), thi thoảng hay bắt gặp từ khóa register, thường là những chỗ tính toán loằng ngoằng, bit biếc dịch ngược dịch xuôi. Register được dùng đặt trước kiểu dữ liệu khi khai báo biến. Tác dụng của từ khóa register, nói một cách ngắn gọn là làm tăng hiệu năng(performance) của chương trình.

Thêm cái của nợ này vào thì tại sao có thể tăng được hiệu năng?. Mà thực sự tăng thật thì tăng được bao nhiêu?
Để thêm phần sinh động, thử một ví dụ nhỏ dưới xem nó có ra cái gì không?

void main()
{
clock_t start, end;
double t;
int i; // không dùng register
//register int i; // sử dụng register
start = clock();
while(i < 0xFFFFFFFF) i++;
end = clock();

t = ((double) (end – start)) / CLOCKS_PER_SEC;
printf("time used %f\n",t);
}

Biên dịch với gcc version 6.3.0
Chạy trên Raspberry Pi 2B (ARMv7 Processor rev 5 (v7l)).
– Sử dụng từ khóa register: time used 9.583080 giây
– Không sử dụng từ khóa register: time used 38.256520 giây

Ví dụ trên cho thấy dùng từ khóa register, thì hiệu năng nó tăng thật, riêng trong trường hợp này thì cũng đang kể đấy chứ nhỉ :-).Tất nhiên cải thiện được bao nhiêu thì phải tùy vào hoàn cảnh cụ thể khi sử dụng, mức độ sử dụng biến đó … Nhưng mà ngon lành vậy thì toàn bộ khai báo biến trong chương trình cứ mặc định thêm luôn từ khóa này thì chuyện gì xẩy ra?.
Để hiểu được điều đó thì trước hết thử xem tại sao hiệu năng nó tăng?
Quay lại với vấn đề cơ bản hơn một chút, trong kiến trúc của vi xử lý thì ALU (Arithmetic Logic Unit) là con trâu đóng vai trò xử lý các tính toán số học. Dữ liệu đưa vào làm việc với ALU phải chứa trong một vùng đặc biệt, gọi là các thanh ghi(register), và ALU chỉ làm việc với đống thanh ghi đó. Trong khi đó các biến khai báo trong chương trình thì đặt ở bộ nhớ ngoài (RAM chẳng hạn …). Do đó với khai báo biến thông thường, để thực hiện một phép tính thì cần có 3 bước.
① Nạp giá trị từ vùng nhớ chứa biến vào register
➁ Yêu cầu ALU xử lý register vừa được nạp giá trị.
③ Đưa kết quả vừa xử lý của ALU ra ngoài vùng nhớ chứa biến.
Hình dung thô thiển như hình vẽ dưới đây.

alu

Khi thêm từ khóa register để khai báo biến, thì tức là ta đã yêu cầu trình biên dịch ưu tiên đặc biệt dành luôn vùng register để chứa biến đó. Và hiển nhiên khi thực hiện tính toán trên biến đó thì giảm được bước ①&③, giảm bớt thủ tục thì hiệu năng nó tăng lên là chuyện dễ hiểu :-).
Quay lại với ví dụ trên, để đảm bảo đúng như thánh phán, thử thêm option “-save-temps” để lấy tập mã lệnh assembly xem nó có thật vậy không.

gcc register.c -o test -save-temps

Đoạn mã assembly ở dưới tương ứng với vòng while loop ở ví dụ trên. Dễ thấy chỗ bôi màu đó khi không dùng từ khóa register tương ứng với bước ①&③ ở trên. Còn khi dùng từ khóa register thì trình biên dịch nó dùng luôn thanh ghi r4 cho việc chứa biến i.
p/s: Nếu không dễ thấy thì tự tra cứu lại lệnh assembly của ARM

Không sử dụng từ khóa register

ldr r3, [fp, #-8] // bước ①
add r3, r3, #1
str r3, [fp, #-8] // bước ③
.L2:
ldr r3, [fp, #-8]
cmn r3, #1
bne .L3

Sử dụng từ khóa register

.L3:
add r4, r4, #1
.L2:
cmn r4, #1
bne .L3

Đến đây, ta thấy rõ ràng khi dùng từ khóa register, thì thay vì dùng bộ nhớ ngoài đển lưu biến thì chương trình sẽ sử dụng luôn register để lưu biến đó. Cái gì ngon, quí thì dĩ nhiên hiếm, register cũng thấy, số lượng register rất nhỏ so với bộ nhớ ngoài, mà đây còn là tài nguyên dùng chung. Do đó không thể chơi kiểu vô tổ chức để thằng nào thích lấy làm đồ riêng thì lấy được. Tùy từng tình huống, yêu cầu để lựa chọn phần xử lý nào nên sử dụng register để tăng hiệu năng mà có thể chấp nhận cho nó xin một vài register về làm của riêng.
Nếu không tin thì thử thêm đoạn code này “register int k[100*1024*1024];” vào
để chiếm register xem chương trình xem nó có ngỏm không ^.^