Đoạn phim stop-motion người ta dùng Macro hay VB???

Liên hệ QC
Có lẽ thật dại dột khi cố thực hiện lại công việc của một bậc thầy nào đó +-+-+-+.
Tuy nhiên mình cũng đã cố thử để tìm 1 câu trả lời cho bản thân mình...là liệu Excel có thể chiếu được 1 đoạn phim một cách "mượt mà" ở mức chấp nhận được hay không? hix

Thực ra ta đang bàn về phim của anh chàng nọ.
Và tôi cho là rất khó để làm cái gọi là phim. Vì nếu là phim thực sự thì ngoài chuyện chạy mượt ta còn phải có những cảnh với các chi tiết. "Bóng ma" chuyển động cũng còn chấp nhận được chứ nếu chỉ là hoạt họa những hình thù ngẫu nhiên, các hình vuông, hình tròn thì lại quá dễ.
Để là phim thì phải có chi tiết vậy không thể là 40x40 được, còn nếu lớn quá thì Excel không chiếu mượt được. Có thể vẽ hoạt hình vd. trên nền trắng bằng các đường viền của người và vật bằng mầu đen, mắt mũi ví dụ là những chấm đen, các đường cong đơn giản. Khi ta scan thì chỉ những cell tương ững với các điểm đen trên hình ta mới thay đổi mầu. Như thế thì vd. với hình 40x40 ta không phải thay đổi mầu cho 1600 cell mà chỉ một số ít, nhờ vậy tốc độ animation tăng rõ rệt.
Nếu là animation ngẫu nhiên cái gì đó thì tôi cũng góp vui chút. Nói cho cùng thay cho hình tròn, hình sao ta có thể vẽ những hình đơn giản, chỉ vẽ đường viền của người và vật mà thôi.
 

File đính kèm

1. Tôi thử đọc ảnh 24 bit của tôi vào Paint --> ghi lại ở dạng 256 mầu (tất nhiên sẽ bị mất mầu). Cũng ảnh kích thước như thế nhưng là 256 mầu thì bây giờ vòng lặp
Mã:
    For r = 1 To bmp.bmHeight
        For c = 1 To bmp.bmWidth
            ActiveSheet.Cells(r, c).Interior.color = GetPixel(DC, c - 1, r - 1)
        Next c
    Next r
chạy nhanh hơn gấp 6 lần.

2. Bây giờ tôi lại lưu ảnh nguồn ở dạng 16 mầu thì vòng lặp trên chạy lâu tương tự như vòng lặp cho ảnh ban đầu 24 bit

Phải chăng nếu dùng đúng 56 mầu của Excel thì việc thay đổi thuộc tính "Interior.color" là nhanh nhất???

Nếu là vầy thì sẽ nhanh hơn rất nhiều
PHP:
For r = 1 To height Step lResol
  For c = 1 To width Step lResol
    r1 = Int(r / lResol) + 1
    c1 = Int(c / lResol) + 1
    Arr(r1, c1) = GetPixel(DC, c - 1, r - 1)
  Next c
Next r
Với biến lResol ta cho = 5 chẳng hạn thì việc scan cái hình em be.bmp của bạn gần như diễn ra trong tích tắc
--------------------------
Mình thì không có hứng thú với việc làm video nhưng lại rất hứng thú với code của bạn (scan pic) và muốn nghiên cứu nó để ứng dụng cho những việc khác sau này. Vậy nhân đây mình xin bạn tư vấn giúp 3 câu hỏi dưới đây:
1> Nếu ta dùng file jpg (chứ không phải bmp) thì phải sửa code thế nào?
2> Nếu ta scan hình là 1 object nằm trên bảng tính thì sẽ sửa code thế nào?
3> Chuyển từ Interior.Color sang
Interior.ColorIndex bằng cách nào?
 
Lần chỉnh sửa cuối:
Thực ra ta đang bàn về phim của anh chàng nọ.
Và tôi cho là rất khó để làm cái gọi là phim. Vì nếu là phim thực sự thì ngoài chuyện chạy mượt ta còn phải có những cảnh với các chi tiết. "Bóng ma" chuyển động cũng còn chấp nhận được chứ nếu chỉ là hoạt họa những hình thù ngẫu nhiên, các hình vuông, hình tròn thì lại quá dễ.
Để là phim thì phải có chi tiết vậy không thể là 40x40 được, còn nếu lớn quá thì Excel không chiếu mượt được. Có thể vẽ hoạt hình vd. trên nền trắng bằng các đường viền của người và vật bằng mầu đen, mắt mũi ví dụ là những chấm đen, các đường cong đơn giản. Khi ta scan thì chỉ những cell tương ững với các điểm đen trên hình ta mới thay đổi mầu. Như thế thì vd. với hình 40x40 ta không phải thay đổi mầu cho 1600 cell mà chỉ một số ít, nhờ vậy tốc độ animation tăng rõ rệt.
Nếu là animation ngẫu nhiên cái gì đó thì tôi cũng góp vui chút. Nói cho cùng thay cho hình tròn, hình sao ta có thể vẽ những hình đơn giản, chỉ vẽ đường viền của người và vật mà thôi.

Nếu đi theo quan điểm chỉ dùng chính Excel để trình diễn thì mình có mấy ý sau:

Với hình tròn cỡ 40x40 (khoảng 90 điểm ảnh hữu dụng), còn hình ngôi sao thì ước chừng 110-130 điểm ảnh hữu dụng.

Trên khung hình biểu thị cỡ 45x80=3600. Tỷ lệ điểm ảnh hữu dụng so với diện tích khung hiển thị là 90/3600=2,5%. Với tỷ lệ tương đối lý tưởng này nhưng tốc độ hiện thị vẫn chưa đủ đánh lừa được thị giác. Người xem vẫn cảm giác được rõ là hình đang được vẽ từ trên xuống.

Vậy giả sử hiển thị 1 khung hình có tỷ lệ điểm ảnh hữu dụng là 50% (gấp 20 lần) hoặc bình quân khoảng 10% (gấp 4 lần) thì tốc độ hiển thị sẽ ra sao? Nếu làm như cách anh nói thì chắc chắn tốc độ hiển thị sẽ không đều lúc nhanh (với hình nhỏ) và chậm (nếu hình lớn). Mình có thử chỉnh timer tăng/giảm đi như tốc độ hiển thị cũng không khả quan hơn.

Một vấn đề nữa có liên quan đến ý trước đây là tốc độ hiện thị khung ảnh trong một giây tối thiểu là bao nhiêu để đủ đánh lừa thị giác người xem. Sau khi bắt tay vào làm thử thì mình phát hiện ra rằng:


  1. Với 1 chuyển động tốc độ trung bình thì cũng cần ít nhất khoảng 9 khung ảnh.
  2. Còn với 3 khung ảnh chỉ phù hợp nếu diễn tả 1 chuyển động nhanh hoặc rất nhanh kiểu như 1 chiếc siêu thanh chỉ kịp lưu lại trên võng mạc 1 tấm ảnh của nó trong tích tắc.

Như vậy thì yêu cầu về tốc độ máy tính sẽ ko phải là 24 lần mà phải là 72 lần nhanh hơn. Cứ tạm cho định luật Moore đúng mãi mãi thì cứ sau 1,5 năm tốc độ máy tính nhanh gấp đôi thì chắc cũng cỡ 10 năm nữa 1 cái máy tính bình thường mới có thể làm nhãn quan chúng ta thoả mãn... trừ trường hợp chúng ta có tiền mua 1 chiếc siêu máy tính.

Tuy nhiên nếu có ai đó đang quan tâm vấn đề này thì cũng đừng bi quan như mình... Hiện tại chúng ta vẫn có thể dùng các ô Excel để thực hiện việc chiếu 1 đoạn clip đấy nhé kể cả với 1 độ phân giải không tưởng. Nhưng cách làm sẽ rất khác và sẽ cần nhiều lần của 5 tiếng đồng hồ.
 
Nếu là vầy thì sẽ nhanh hơn rất nhiều
PHP:
For r = 1 To height Step lResol
  For c = 1 To width Step lResol
    r1 = Int(r / lResol) + 1
    c1 = Int(c / lResol) + 1
    Arr(r1, c1) = GetPixel(DC, c - 1, r - 1)
  Next c
Next r
Với biến lResol ta cho = 5 chẳng hạn thì việc scan cái hình em be.bmp của bạn gần như diễn ra trong tích tắc
--------------------------
Mình thì không có hứng thú với việc làm video nhưng lại rất hứng thú với code của bạn (scan pic) và muốn nghiên cứu nó để ứng dụng cho những việc khác sau này. Vậy nhân đây mình xin bạn tư vấn giúp 3 câu hỏi dưới đây:
1> Nếu ta dùng file jpg (chứ không phải bmp) thì phải sửa code thế nào?
2> Nếu ta scan hình là 1 object nằm trên bảng tính thì sẽ sửa code thế nào?
3> Chuyển từ Interior.Color sang
Interior.ColorIndex bằng cách nào?

Bạn xem tạm, tôi sẽ viết cụ thể sau
 

File đính kèm

Bạn xem tạm, tôi sẽ viết cụ thể sau
Lúc nào cũng có những kiến thức mới lạ. Cảm ơn bạn nhé
Hỏi thêm: Có phải hàm GetPictureObject dùng được với Picture nằm trên đĩa hoặc Picture là 1 Object trên bảng tính không?
(hỏi thế là vì mấy cái ghi chú bạn ghi bằng font gì tôi đọc không ra ---> Chỉ đoán)
 
Lúc nào cũng có những kiến thức mới lạ. Cảm ơn bạn nhé
Hỏi thêm: Có phải hàm GetPictureObject dùng được với Picture nằm trên đĩa hoặc Picture là 1 Object trên bảng tính không?
(hỏi thế là vì mấy cái ghi chú bạn ghi bằng font gì tôi đọc không ra ---> Chỉ đoán)

Tôi dùng trong VBE Times: Tools --> OPtions --> thẻ Editor Format --> chọn Times New Roman (vietnamese)
--------------
' loadOpt = 0 --> image là đường dẫn tới tập tin trên đĩa,
' loadOpt = 1 --> image là index của ảnh trong ImageList và obj là ImageList
' loadOpt <> 0 và <> 1--> image là tên của ảnh trên Sheet và obj là tên Sheet đó
--------------
Riêng th nạp từ ImageList và từ Sheet tôi mới viết code, chưa test
-----------------
Handle trong Windows là gì? Nó chẳng qua là một số nguyên. Thế thôi.
Trong công việc hàng ngày bạn có Mã Sản Phẩm, Mã Nhân Viên, ... Để làm gì? Đơn giản để phân biệt. Khi bạn truyền vào hàm Mã Sản Phẩm, Mã Nhân Viên thì hàm biết bạn cần thao tác trên sản phẩm, nhân viên cụ thể nào, bạn muốn truy cập tới sản phẩm, nhân viên nào v...v
Ông Windows thấy "mẹo" của bạn hay quá nên ổng quyết định: tất cả mọi cửa sổ (window), tài nguyên nào được "ta" tạo ra thì "ta" gán cho chúng 1 con số gọi là Handle để phân biệt. Khi người dùng cần thao tác gì (ghi, vẽ, thay đổi các thuộc tính ...) trên một đối tượng nào đó thì gọi hàm thích hợp (hàm ghi tài liệu, vẽ, đọc thuộc tính ...) và truyền Handle của nó vào dưới dạng thông số để "ta" biết được là "ta" cần phải thao tác (ghi tài liệu, vẽ, đọc thuộc tính ...) trên đối tượng nào.
Những combobox, Button, Checkbox ... của Windows đều là window (cửa sổ). Trong notepad chẳng hạn cửa sổ notepad với thanh tiêu đề là 1 window. Nó chứa trong lòng nó window con (lớp - class Edit) - vùng làm việc mầu trắng. Khi bạn chọn Save thì trong cửa sổ Save dòng nhập tên tập tin, nút Save, Cancel, ... đều là window. Tất cả mọi window khi Windows tạo ra đều được "gán" cho 1 con số gọi là Handle (window handle).
Bạn có thể mở 1 tập tin vd. TXT hoặc tạo mới bằng cách gọi hàm CreateFile (phải truyền đường dẫn của tập tin cần mở hoặc cần tạo). Nếu CreateFile mở hoặc tạo thành công thì nó trả về Handle - "handle to the specified file":
Mã:
hFile1 = CreateFile(filename1, ...)
hFile2 = CreateFile(filename2, ...)
Những Handle trả về này Windows dùng để phân biệt. Nếu bạn cần đóng những file được mở được tạo thì bạn gọi hàm CloseHandle. Nhưng để biết bạn cần đóng file nào thì bạn phải truyền Handle vào thì Windows mới biết được ý bạn. Không có Handle thì system không biết bạn định ghi hay đóng file nào:
Mã:
CloseHandle hFile1
Tới đây bạn chỉ có thể thao tác với filename2. Các bước:
Mã:
hFile = CreateFile(filename, ...)
.... các thao tác đọc, ghi gì đó
CloseHandle hFile
Tương tự khi bạn tạo, nạp bitmap, icon, cursor, font, ... thì chúng cũng được gán cho 1 con số gọi là Handle. Trong vd. ở bài trước tôi dùng LoadImage để nạp ảnh vào bộ nhớ. Hàm LoadImage trả vể Handle gọi là "handle of the newly loaded image" mà tôi nhớ vào biến hbmp:
Mã:
hbmp = LoadImage(...)
Tôi nhớ để các bước sau có thể sử dụng bitmap, và về sau giải phóng tài nguyên bitmap:
Mã:
DeleteObject hbmp
Để có thể thao tác thì trước tiên ta phải có Picture. Trong bài trước tôi nạp ảnh BMP bằng LoadImage (chỉ cho icon, cursor, bitmap). Trong bài này để nạp ảnh JPG (tất nhiên không chỉ cho JPG) từ đĩa thì tôi dùng LoadPicture. Nó trả về StdPicture - IPictureDisp, mà StdPicture.Handle - IPictureDisp.Handle chính là "image handle" đã nói ở trên. Tức nếu bài trước tôi có:
Mã:
hbmp = LoadImage(...)
oldBitmap = SelectObject(DC, hbmp)
GetObject hbmp, Len(bmp), bmp
chiều dài ảnh = bmp.bmWidth
chiều cao ảnh = bmp.bmHeight
thì ở bài này tôi làm:
Mã:
Set Pic = LoadPicture(...)
oldPic = SelectObject(srcDC, Pic.Handle)
GetObject Pic.Handle, Len(bmp), bmp
chiều dài ảnh = bmp.bmWidth
chiều cao ảnh = bmp.bmHeight
Tóm lại nếu ta có "bitmap handle" thì coi như "xong". Ở bài trước ta có được Handle bằng cách LoadImage, ở bài này ta gọi LoadPicture để có StdPicture - IPictureDisp, mà StdPicture.Handle - IPictureDisp.Handle chính là cái cần có.
Thế nếu ta có sẵn ảnh ở trên Sheet hoặc trong ImageList rồi thì sao? Thì đọc ra StdPicture - IPictureDisp
Tôi soạn 1 hàm đọc và trả về StdPicture - IPictureDisp khi ta có đường dẫn, ảnh trên Sheet, hoặc trong ImageList.
-------------------
Còn về "Chuyển từ Interior.Color sang Interior.ColorIndex bằng cách nào" thì lẽ ra tôi phải hỏi bạn. Vì bạn có thâm niên trong Excel gấp 9, 10 lần tôi. Nhưng tôi thử "nhìn kỹ" xem vấn đề này như thế nào.
Nếu tôi không lầm thì Excel có 1 bảng mầu "mặc định". Bảng mầu mặc định này chỉ có 56 mầu.
Tôi có thể viết:
Interior.Color = RGB(r, g, b) với r, g, b thuộc [0; 255]. Như thế tôi có thể nhập vào 1 trong 16777216 mầu. Dĩ nhiên trong 16777216 mầu đó có hằng hà sa số mầu không có trong bảng mầu mặc định của Excel nên không có ColorIndex tương ứng. Chỉ khi mầu có mặt trong bảng mầu mặc định thì mới có ColorIndex tương ứng. Vd. mầu có giá trị RGB(0, 0, 0) hoặc RGB(255, 255, 255) có mặt trong bảng mầu mặc định nên tôi có thể:
Mã:
Interior.Color = RGB(0, 0, 0)
hoặc
Interior.ColorIndex = 1
Interior.Color = RGB(255,255, 255)
hoặc
Interior.ColorIndex = 2
Nếu tò mò muốn biết mầu có chỉ số từ 1 tới 56, hay nói cách khác các mầu trong bảng mầu mặc định có các thành phần r, g, b (red, green, blue) như thế nào thì dễ thôi. Bạn biết rồi nhưng tôi mạn phép viết để nhiều bạn nào đọc bài này mà chưa biết thì tham khảo
Mã:
Sub Button1_Click()
Dim k As Long, r As Long, g As Long, b As Long, mau As Long, tmp As Long, Arr(1 To 56) As String
    For k = 1 To 56
        Cells(k, 1).Interior.ColorIndex = k
        mau = Cells(k, 1).Interior.Color
        b = mau \ 65536
        tmp = mau Mod 65536
        g = tmp \ 256
        r = tmp Mod 256
        Arr(k) = "ColorIndex = " & k & ", Color = " & mau & " = RGB(" & r & ", " & g & ", " & b & ")"
    Next k
    Range("B1:B56").Value = Application.WorksheetFunction.Transpose(Arr)
End Sub

Riêng khoản Interior này tôi chỉ viết như tôi tạm hiểu. Chưa chắc tôi đã hiểu đúng hoặc hiểu hết.
 
Cảm ơn bạn về cái Handle, trước đây đã biết, bây giờ lại biết thêm 1 chút
Giải thích rất chi tiết nhưng nói thật lòng là tôi hiểu chắc cở.. 10%
Nhưng không sao... thề với lòng, cái gì tôi muốn biết thì sẽ cố gắng tìm hiểu ---> Thông qua các thí nghiệm để ngộ ra vấn đề (đơn giản vì tôi chưa học qua bất cứ trường lớp nào về Excel hay lập trình)
Một lần nữa cảm ơn bạn! Nếu có vấn đề gì tôi sẽ lại làm phiền tiếp
(Riêng về thằng em Interior.ColorIndex chắc ta sẽ bàn vào 1 topic khác thích hợp. Nói chung là tôi muốn chuyển từ 16 triệu màu sang 56 màu chứ không phải ngược lại)
 
Cảm ơn bạn về cái Handle, trước đây đã biết, bây giờ lại biết thêm 1 chút
Giải thích rất chi tiết nhưng nói thật lòng là tôi hiểu chắc cở.. 10%
Nhưng không sao... thề với lòng, cái gì tôi muốn biết thì sẽ cố gắng tìm hiểu ---> Thông qua các thí nghiệm để ngộ ra vấn đề (đơn giản vì tôi chưa học qua bất cứ trường lớp nào về Excel hay lập trình)
Một lần nữa cảm ơn bạn! Nếu có vấn đề gì tôi sẽ lại làm phiền tiếp
(Riêng về thằng em Interior.ColorIndex chắc ta sẽ bàn vào 1 topic khác thích hợp. Nói chung là tôi muốn chuyển từ 16 triệu màu sang 56 màu chứ không phải ngược lại)

Có một chút gõ thiếu trong hàm GetPictureObject
Dòng
Mã:
If [COLOR=#ff0000]obj[/COLOR].progID = "Forms.Image.1" Then
đổi thành
Mã:
If [COLOR=#ff0000]objOLE[/COLOR].progID = "Forms.Image.1" Then
Tức đổi chỗ mầu đỏ. Cái này ai tinh thì sẽ phát hiện ra dựa trên ngữ cảnh

Tập tin đã sửa, đã test cả 3 kiểu nhập tôi đính kèm ở dưới
 

File đính kèm

Cảm ơn bạn về cái Handle, trước đây đã biết, bây giờ lại biết thêm 1 chút
Giải thích rất chi tiết nhưng nói thật lòng là tôi hiểu chắc cở.. 10%
Nhưng không sao... thề với lòng, cái gì tôi muốn biết thì sẽ cố gắng tìm hiểu ---> Thông qua các thí nghiệm để ngộ ra vấn đề (đơn giản vì tôi chưa học qua bất cứ trường lớp nào về Excel hay lập trình)
Một lần nữa cảm ơn bạn! Nếu có vấn đề gì tôi sẽ lại làm phiền tiếp
(Riêng về thằng em Interior.ColorIndex chắc ta sẽ bàn vào 1 topic khác thích hợp. Nói chung là tôi muốn chuyển từ 16 triệu màu sang 56 màu chứ không phải ngược lại)

Cái chuyện mầu này thì tôi nghĩ "triết lý" đơn giản thôi. Nếu bạn có 1 ảnh nhiều mầu vd. k mầu và bạn muốn convert để nó dùng ít mầu hơn vd. n mầu với k > n thì bao giờ bạn cũng bị mất mầu. Bạn cứ thử nạp vào phần mềm đồ họa bất kỳ rồi ghi lại với ít mầu hơn thì sẽ thấy chất lượng ảnh giảm đi đáng kể. Nhưng các phần mềm đó cũng còn có "lựa chọn" là nó sẽ thay một số mầu bằng những mầu "gần đúng". Còn bạn không có lựa chọn vì rất có thể những mầu có thể gọi là gần đúng đó cũng không có trong bảng 56 mầu của Excel.
Bạn đọc 1 Pixel trong bitmap vd. có mầu là 1800040. Bạn có 56 lựa chọn ColorIndex. Dù chọn ColorIndex nào thì cũng không có mầu nào giống mầu 1800040.
Mã:
Sub Button2_Click()
Dim k As Long
    For k = 1 To 56
        Cells(k, 1).Interior.ColorIndex = k
        Cells(k, 2).Interior.Color = 1800040
    Next k
End Sub
So sánh 2 cột A, B sẽ thấy.
Dù chọn thế nào chăng nữa thì không có chỉ số nào là tốt cả. Bao giờ chất lượng ảnh cũng giảm, có khi rất đáng kể vì bạn chỉ có 56 mầu.
Cái này nó giống như bạn có 56 bút mầu và bạn phải vẽ lại bức tranh mà tác giả dùng 1000 mầu khác nhau. Có thể có những mầu mà 56 bút mầu của bạn không diễn tả được, thậm chí chỉ là gần đúng.
 
Lần chỉnh sửa cuối:
Bạn đọc 1 Pixel trong bitmap vd. có mầu là 1800040. Bạn có 56 lựa chọn ColorIndex. Dù chọn ColorIndex nào thì cũng không có mầu nào giống mầu 1800040.
Mã:
Sub Button2_Click()
Dim k As Long
    For k = 1 To 56
        Cells(k, 1).Interior.ColorIndex = k
        Cells(k, 2).Interior.Color = 1800040
    Next k
End Sub
So sánh 2 cột A, B sẽ thấy.
Dù chọn thế nào chăng nữa thì không có chỉ số nào là tốt cả. Bao giờ chất lượng ảnh cũng giảm, có khi rất đáng kể vì bạn chỉ có 56 mầu.
Cái này nó giống như bạn có 56 bút mầu và bạn phải vẽ lại bức tranh mà tác giả dùng 1000 mầu khác nhau. Có thể có những mầu mà 56 bút mầu của bạn không diễn tả được, thậm chí chỉ là gần đúng.
Nhưng nếu tôi làm vầy thì lại khác:
PHP:
Sub Button2_Click()
  Cells(1, 1).Interior.Color = 1800040
  Cells(1, 2).Interior.ColorIndex = Cells(1, 1).Interior.ColorIndex
End Sub
Cái này là tự Excel nó chuyển lấy và ý tôi muốn tìm cái nguyên tắc này (dù giống hay hơi hơi giống cũng không sao)
 
Cái này là tự Excel nó chuyển lấy và ý tôi muốn tìm cái nguyên tắc này (dù giống hay hơi hơi giống cũng không sao)

1. Dùng hàm của siwtom có thể lấy ra 3 tham số RGB của 56 màu Excel
2. Thử bằng 1 phần mềm đồ họa nào đó (Paint chằng hạn) như sau:

- mở cửa sổ chọn màu có 3 ô RGB
- gõ tham số RGB của 1 màu Excel vào
- thay đổi lần lượt từng tham số R, G, B từng đơn vị một, đến khi nào màu không chấp nhận được nữa.
- Ghi lại phạm vi thay đổi các tham số có thể chấp nhận
- Dùng cách tính ngược với hàm ở bước 1, tính ra phạm vi màu có thể quy về 1 màu thử của Excel.
- chuyển qua màu thử thứ 2 của Excel.
 
Web KT

Bài viết mới nhất

Back
Top Bottom