Đầu tiên, đây không phải dự án freelance đầu tiên của mình và $1000 cũng không phải con số lớn nhất mình từng kiếm được, nhưng:

  • Đây là công việc đầu tiên mình chủ động tìm kiếm thông qua UpWork, mọi thứ được thực hiện và giám sát một cách có quy trình.
  • Lần đầu làm việc với người nước ngoài: từ viết proposal, thuyết phục họ để nhận dự án, triển khai công việc một cách độc lập và báo cáo tới khách hàng thông qua report và meeting. Tất cả mọi thứ đều được thực hiện một mình.

Nhân dịp nhận được món quà từ client, mình muốn viết bài chia sẻ cho mọi về hành trình tìm việc và kiếm tiền trên đó, hy vọng bài viết có thể tới tay và tiếp thêm động lực cho những bạn đang loay loay với những dòng tìm kiếm “Làm sao để kiếm tiền trên upwork”, “làm sao để nhận được công việc đầu tiên trên upwork…” như mình lúc trước.

À ngoài ra mình sẽ đi sâu thêm một chút kỹ thuật mảng xử lý ảnh, hy vọng có thể hữu ích cho những ai đang gặp bài toán tương tự sẽ có keyword để research.

Giờ thì vào bài thôi 🚀

I. Chuẩn bị

Có lẽ mình biết đến upwork và freelance thông qua bài viết Kí sự Freelancer ở Upwork từ năm 2022 của tác giả Thanh Le (một bài viết hay với góc nhìn chân thật của 1 freelancer vất vả để tìm được công việc đầu tiên được thể hiện bởi 1 lối hành văn rất bánh cuốnn). Còn việc mình bắt đầu mò mẫm lên nền tảng đó là đầu năm nay - trong thời gian vừa nghỉ ở cty thứ nhất chờ onboard cty thứ hai, trước khi bắt đầu mình đã tìm kiếm các bài viết, video chia sẻ kinh nghiệm kiếm job trên Upwork, bằng chứng cho sự nghiêm túc tìm kiếm đó là mình đã xem hết video 7 tiếng này (có tua đôi chút nhưng thật sự là mình đã xem hết video đó) 🙂‍↕️

Mình bắt tay vào việc tìm việc bằng cách tạo tài khoản, chau chuốt profile và nạp $15 cho việc mua connections với tâm thế:

  • Xác định việc này sẽ không dễ dàng, cần kiên trì.
  • Mục tiêu kiếm được 1 công việc đầu tiên (bao nhiêu tiền cũng được, có người thuê là mừng lắm rồi)

Giải thích chút:

  • Proposal là nội dung mình gửi cho client nhằm show trình độ, năng lực của bản thân rồi van xin họ cho mình nhận job
  • Connection: một đơn vị tiền tệ trên upwork, dùng để gửi proposal, boost profile lên top… Phải bỏ tiền thật ra để mua.

Sau phần chuẩn bị này là chúng ta có thể lả lướt trên upwork để kiếm job rồi.

gif lướt lướt

II. Công việc đầu tiên

Dù đã chuẩn bị tâm thế là sẽ cần thời gian để có được công việc đầu tiên rồi nhưng không thể tránh khỏi tâm lý háo hức, hi vọng ban đầu, cứ chốc chốc lại mở app lên xem có ai phản hồi proposal không (mặc dù app đã bật thông báo ?!?), ngó hết job này tới job nọ xem có cái nào phù hợp không để lưu lại. Nếu bạn hỏi tại sao phải lưu mà không apply luôn thì đó là do mình theo chiến lược của các youtuber bày:

Tìm kiếm và chọn lọc các job thật kỹ càng trước khi apply - vì mỗi lần apply tốn connections chứ không phải free.

Mỗi ngày gửi 2-3 proposal phù hợp nhất - cũng nhằm tiết kiệm proposal và tránh tình trạng burndown. Thử hình dung bạn gửi cả 15-20 proposal mà không có lấy một phản hồi, khi đó thì “tiền mất tật mang”, không phải chúng ta lên upwork kiếm tiền nữa mà upwork đang kiếm tiền từ chúng ta :))

Sau tầm 2-3 ngày mình may mắn nhận được 1 phản hồi từ 1 job khá phù hợp, nhưng cũng phải sau khoảng 3-4 tuần mới chốt được dự án, lý do là client hình như ít vào check upwork, và mình đòi $1500 cho dự án so với budget họ đề ra ban đầu là $1000 (nếu bạn có hỏi tại sao ban đầu mục tiêu là có việc là được bao nhiêu cũng nhận mà giờ lại đòi gấp rưỡi budget người ta thì nguyên nhân là do tính chất dự án khá khó và cần thời gian để nghiên cứu và triển khai). Cuối cùng, để có job đầu tiên mình chấp nhận $1000 như họ đề xuất.

III. Có job rồi, code thôi!!!

Nói sơ qua về cái job này:

  • Client: US, huấn luận viên bóng đá, non-tech nhưng có mindset của 1 engineer, tính cách khá rạch ròi, tử tế và hào phóng.
  • Dự án: Với input là 2 video quay 1 trận bóng, cần thiết kế một thuật toán có khả năng:
    • Milestone 1: Ghép 2 input thành 1 video toàn cảnh liền mạch (như tạo ảnh panorama)
    • Milestone 2: Tạo ra video mô phỏng camera được di chuyển thủ công “dí” theo những khu vực tập trung bóng và cầu thủ trên sân (bạn có thể hình dung output sẽ như là có một cameraman liên tục điều chỉnh góc quay thủ công để giữ cho khu vực có bóng trên sân luôn nằm trong khung hình)
    • Milestone 3: Tự động tạo ra những clip highlight ngắn (như ghi bàn, sút phạt…) để người dùng có thể lấy đăng lên các nền tảng short video như Tiktok, Youtube Short…
    • Milestone 4: Trích xuất các thông số, dữ liệu của trận đấu, từ những dự liệu đó xây dựng các mô hình phân tích data để huấn luận viên có được insight trận đấu cũng như của các cầu thủ.

Overall

Tính chất dự án hoàn toàn phù hợp với knowledge domain dòi, lôi bàn phím lên gõ gõ xong đóng gói gửi khách rồi lụm $ thôi 🐣.

Nhưng mà đời không như là mơ, vấn đề ở milestone 1 không phải dùng ffmpeg hay opencv concatenate hai video lại cái là xong, đây là một bài toán image stitching (chính xác hơn là video stitching vì phải đảm bảo tính consistency trên toàn video). Gòi xong, đã làm bài này bao giờ đâu, giờ sao?

Lúc viết proposal lỡ cam kết chắc nịch là sẽ làm được rồi thì giờ phải cầm keyword đó mà đi research rồi tìm cách giải quyết thôi chứ sao 🥹.

Sau một vòng research, mình rút ra được một số thông tin:

  • Bài toán image stitching không phải mới, bản thân nó là các thuật toán xử lý điểm ảnh, tính toán ma trận biến đổi… không có yếu tố Machine Learning, Deep Learning hay AI gì ở đây cả. Rất dễ để tìm thấy những bài viết, video giải thích, bao gồm cả sample code.
  • Tuy nhiên từ image stitching lên video stitching thì lại là một vấn đề lớn, các repo cho bài toán này đều khoảng 9-10 năm trước, thường do các PhD researcher viết bằng C++ và cho mục đích học thuật (nhưng chất lượng khá thấp, còn rất nhiều vấn đề và lỗi trên video demo của họ). Mọi thứ khá bế tắc nhưng mình vẫn tiếp tục tìm kiếm và thử nghiệm vì biết chắc bài toán này có lời giải (vì đã có software thực hiện được việc đó và họ đang bán giải pháp với giá khoảng $100-200 mỗi năm)

Khi đã biết bài toán chắc chắn có nghiệm rồi thì bắt tay vào giải thôi.

Đầu tiên mình thử triển khai khai chức năng image stitching, chỉ khoảng 30 - 1 tiếng mình đã triển khai xong (nếu bạn có thắc mắc tiếp là sao mà nhanh vậy thì mình trả lời luôn là trên mạng có nhiều sample code và các thuật toán được hỗ trợ trong OpenCV rồi nên mình lụm về chạy thui 🐣). Code chạy được rồi đó, nhưng làm sao để điều chỉnh kích thước output, khả năng tìm điểm chung, thực hiện ghép nối… điều bắt buộc mình phải tìm hiểu, đi sâu vào giá trị, ý nghĩa của từng tham số.

Sau nhiều bài viết, video giải thích, mình dừng lại và tập trung vào series giải thích Image Stitching của thầy Shree Nayar, đúng như mô tả của kênh nội dung thầy truyền tải luôn theo nguyên tắc First Principles, tức là sẽ đi từ bản chất cốt lõi của vấn đề đi lên, với 12 video trong series đã giúp mình hiểu một cách sâu sắc về bài toán image stitching cũng như các phép biến đổi ảnh.

Link series Image Stitching: https://www.youtube.com/watch?v=J1DwQzab6Jg&list=PL2zRqk16wsdp8KbDfHKvPYNGF2L-zQASc

Sau khi đã giải quyết được bài toán cốt lõi image stitching rồi việc triển khai cho video chắc chỉ cần đọc từ frame –> xử lý –> lưu lại, nhưng nếu làm như vậy sẽ gặp ngay vấn đề: output cà giật cà giật qua từng frame. Nguyên nhân là mỗi lần xử lý thuật toán sẽ dự vào 2 ảnh input để tính toán ra ma trận biến đổi mới, mà mỗi frame mỗi khác nên ma trận cũng khác theo, bởi vậy video về tổng thể thì có vẻ oke nhưng nó đang bị liên tục xê dịch một chút so với frame liền kề, vì vậy video output nó giật giật mà nó tiền đình mà nó chóng mặt lắm.

Lúc này mình giải định, do góc quay camera là cố định, sân bóng cũng cố định nên sẽ tồn lại một ma trận biến đổi M cố định có thể sử dụng cho toàn bộ bước ghép nối. Kết quả thật tuyệt vời, output như đúng như mong đợi. Mình đóng gói code, quay demo và viết report gửi cho khách, họ rất vui và bày tỏ sự ấn tượng với kết quả và tiến độ thực hiện, tuy nhiên họ muốn test trên nhiều video input hơn.

Và bùm… sau khi test thêm đã phát hiện một lỗi về chất lượng khá nghiêm trọng, phần ghép nối ở giữa không thật sự được matching với nhau 1 cách hợp lý. Đây đúng là một vấn đề lớn, mình khá hoảng vì chưa hình dung được nguyên, nhưng rất may mắn là khách hàng họ đã nghiên cứu bài toán này khoảng 1 năm và họ có một giả thiết cho vấn đề này. Vậy vấn đề này là gì?

Đó là input được quay bởi GoPro với mode camera góc rộng (fish) tức là video bị biến dạng để có góc quay rộng hơn, chính vì hình ảnh đã bị biến dạng nên nếu mình cầm đúng hình ảnh đó đi xử lý thì sẽ gặp tình trạng không khớp tại điểm nối. Vậy có thể đưa ảnh bị biến dạng về bình thường không? Câu trả lời là “Có”, và keyword cho bài toán này là “Undistoring images”, để thực hiện được nó lại dẫn tới 1 bài toán nữa “Camera Calibration” (tìm hệ số biến dạng gây ra bởi camera).

sma-architecture-Stitching-Video-drawio.png

Nếu muốn đi sâu hơn các bạn có thể cầm 2 keyword trên để research nhé. À ở đây mình cung cấp thêm 1 keyword nữa là “Gyroflow” - Gyroflow là một phần mềm xử lý hậu kỳ như khử rung, khử biến dạng… cho các esport camera như GoPro. Trước khi cắm đầu cắm cổ triển khai 2 bài toán “Undistoring images” và “Camera Caliration” mình thử dùng Gyroflow để khử biến dạng của video input trước khi đưa vào pipeline “Video Stitiching”, vấn đề đã được giải quyết. Tới đây mình mới quay lại triển khai 2 thuật toán “Undistoring images” và “Camera Caliration”, cũng gặp chút vấn đề và tốn kha khá thời gian nhưng cuối cùng mọi thứ đã hoạt động đúng như mong đợi.

Tới đây mọi thứ vẻ oke rồi, khách muốn mình làm luôn cái app để có giao diện cho dễ sử dụng, vì khách hàng non-tech nên việc thao tác các dòng lệnh sẽ khá khó khăn cho họ.

Thực ra trước giờ mình chưa code GUI App bao giờ nhưng may mắn là đây là năm 2025 - The age of AI, khứa Cursor làm mấy task này khá tốt. Bởi vậy mình nhận và triển khai luôn, cũng nhờ vậy mình học được thêm framework CustomTkinter dùng để làm GUI App với Python và build thành công trên cả 3 môi trường phổ biến MacOS, Ubuntu, Windows Mọi thứ khá suôn sẻ, App chạy mượt, UI/UX khá oke khách hàng hài lòng, mỉm cười mãn nguyện vì tìm được đúng thằng dev được việc (chỗ này là mình bịa thôi chứ họ có mỉm cười không thì mình không biết 🙂‍↔️)

Ngoài ra còn một số problems mình phải đối mặt như:

  • Đồng bộ hóa 2 video theo 1 mốc thời gian trong thời gian thực,
  • Tìm và thử nghiệm video encoder phù hợp, thiết kế pipline cho đọc/ghi video riêng phù hợp với kích thước input và output rất lớn và dài

  • Mỗi vấn đề là 1 challenge rất cụ thể nhưng với tinh thần “tới là đón, đụng là chạm” cùng với sự hỗ trợ đắc lực của đàn em: ChatGPT, Grok, Curosr… may mắn là các vấn đề đều đã được giải quyết (vẫn chưa thật sự là tốt nhất nhưng tạm ở thời điểm hiện tại thì mọi thứ đang hoạt động ổn) 👊.

Các bạn có thể xem qua demo tại: https://www.youtube.com/watch?v=ZNacjRtL9JQ

Tới đây thì mọi thứ khá ổn rồi, chỉ còn 1 vấn đề cuối mà mình đang ráng xử lý để sớm kết thúc dự án đó là performance. Thời gian xử lý cho toàn pipeline hện tại đang rất rất là chậm, nguyên nhân là kích thước input rất lớn (độ phân giải input là video 5k3, sau khi stitching nó có thể lên tới 8-9k), các thuật toán xử lý ảnh sử dụng rất nhiều của OpenCV lại đang chạy trên CPU (gần như chưa tận dụng được GPU) và thời lượng input cho 1 trận đấu khá dài (khoảng 25 phút). Mặc dù khách bảo thời gian xử lý như hiện tại vẫn chấp nhận được chỉ cần output ra chính xác là được, nhưng mình lại thấy thời gian xử lý như vậy là quá lâu, khá khó để thương mại hóa, và cũng do mình từng có kinh nghiệm tối ưu hóa bài toán xử lý ảnh này trước đó nên mình đề xuất là cứ để mình làm thử, có kết quả tốt thì mới tính tiền.
Chắc là vì cảm động trước tinh thần tận tâm của mình nên khách đã gửi tặng mình chiếc Laptop của ổng, phải nói là rất vui khi mình cầm trên tay một món quà được gửi từ bên US về, nó là một laptop gaming và có cấu hình khá mạnh (thấy ông bảo mua nó 2 năm trước với giá khoảng $2500). Đối với mình, đây là một món quà mang giá trị tinh thần và vật chất rất lớn, với cấu hình mạnh như vậy mình có thể thực hiện dự án một cách dễ dàng hơn, code cũng rất tiện, như các bạn thấy, bây giờ mình còn có thể code trên controler thay vì bàn phím luôn mà

Wukong

IV. Kết

Bài cũng đã dài, mình xin gác phím ở đây (có mấy dòng này mà mình phải viết 2-3 tuần mới xong 🥹). Mặc dù công việc khá bận nhưng mình vẫn cố gắng hoàn thành bài viết, hi vọng nó sẽ mang lại chút giá trị gì đó cho người đọc 🙏.

Nhân tiện, mình cũng đang open cho cho các job freelance, nên nếu có quý khách hàng nào muốn thuê mướn mình thì do not hesitate to contect me 🤙 nha (giai đoạn này đang ôn thi B1 nên chèn tí English zô cho sang 🐥).