Cách tạo dãy số từ hình ảnh

Liên hệ QC

giaiphap

==(^o^)==
Tham gia
12/3/07
Bài viết
5,769
Được thích
6,240
Donate (Momo)
Donate
Giới tính
Nam
Tôi có tham khảo cách mà người ta tạo ra file hình bằng những con số liên tục trong một danh sách (như file bên dưới). Vậy các anh (chị) cho tôi hỏi làm cách nào mà từ một hình ảnh tôi có thể tạo được một danh sách các số như ô A1 trong file dưới. Mục đích là để từ một ảnh bất kỳ tôi có thể đưa nó sang một danh sách số như trong file để sang máy khác tôi sẽ dùng code của file dưới để chuyển danh sách số này sang lại file hình trên máy đó (tránh trường hợp khi người dùng copy sẽ làm mất file hình kèm theo).
 

File đính kèm

  • Tao file hinh.xlsm
    15.5 KB · Đọc: 58
Thấy trong hình của @ongke0711 là 2 hình, hình 1 là BMP, hình 2 là JPEG. Vậy Access hổ trợ gán image trực tiếp từ 1 memory byte buffer hả bạn ?
 
Upvote 0
Thấy trong hình của @ongke0711 là 2 hình, hình 1 là BMP, hình 2 là JPEG. Vậy Access hổ trợ gán image trực tiếp từ 1 memory byte buffer hả bạn ?

Hình trên là table của SQL Server. Dùng ADODB.Stream để đọc file ảnh rồi mới dùng câu lệnh T-SQL lưu trực tiếp vào field của Table.
Còn Access thì không cần. Chỉ cần khai báo field là OLEObject và Access sẽ hỗ trợ nút lệnh "Insert Object" chèn thẳng vào field.
 
Upvote 0
Ý mình hỏi là đối tượng display image thì sao đó ông kẹ
 
Upvote 0
Ý mình hỏi là đối tượng display image thì sao đó ông kẹ
:) À. Vụ hiển thị ảnh trong Access Form thì vẫn phải đi đường vòng bác à. Tức là vẫn phải chuyển từ Binary sang file ảnh và lưu vào folder tạm, sau đó lấy đường dẫn đó gán cho Image control trên Form Access.
Công nhận bác hay thật nhìn cái hình mã binary suy ra JPG, PNG luôn.

Mã:
adoCon.CursorLocation = 3  'adUseClient
    adoCon.Open "Network Library=DBMSSOCN;" & _
                "PROVIDER=SQLOLEDB;DATA SOURCE=localhost\QUOCBAO-PC,1433" & _
                ";INITIAL CATALOG=Test" & _
                ";User Id=sa;Password=123456;"

    adoRs.Open "SELECT MaNV,Hinh FROM tblPhoto WHERE MaNV = 2", adoCon, 3, 3

    adoStream.Type = 1    'adTypeBinary
    adoStream.Open
    adoStream.Write adoRs.Fields("Hinh").Value    ' Field luu binary data

    adoStream.SaveToFile "C:\" & adoRs.Fields("MaNV").Value & ".png", 2

    adoRs.Close
    adoCon.Close
 
Upvote 0
Hi hi, ruột là JPEG mà bác Ông Kẹ ép nó lưu xuống với đuôi png hen :) MaNV = 2 là JPEG, MaNV = 1 là bitmap, size = 0x00035A06 (219 654) bytes.
JPEG file thì bytes nhận dạng là = FF D8 FF E0, PNG file byte nhận dạng là 89 50 4E 47....
Hồi xưa mình làm bên multimedia, xử lý ảnh, video... mấy cái này làm nát luôn, giờ nhìn thấy là ngán mà. Nhất là ông BMP, sợ nó luôn :)
Lãnh vực forensics bây giờ phải nắm file format rất nhiều. Không biết gì thì cứ hỏi ông Google, ê cho tui xxx file format
 
Upvote 0
Hi hi, ruột là JPEG mà bác Ông Kẹ ép nó lưu xuống với đuôi png hen :) MaNV = 2 là JPEG, MaNV = 1 là bitmap, size = 0x00035A06 (219 654) bytes.
JPEG file thì bytes nhận dạng là = FF D8 FF E0, PNG file byte nhận dạng là 89 50 4E 47....
Hồi xưa mình làm bên multimedia, xử lý ảnh, video... mấy cái này làm nát luôn, giờ nhìn thấy là ngán mà. Nhất là ông BMP, sợ nó luôn :)
Lãnh vực forensics bây giờ phải nắm file format rất nhiều. Không biết gì thì cứ hỏi ông Google, ê cho tui xxx file format

Haha...đúng là làm biếng chút là bị bắt lỗi liền. Đúng ra là trong table còn có 1 field nữa tôi lưu [FileExtension] sau đó khi convert sẽ lấy đuôi đó ghép vô chứ không có ép như code demo.
 
Upvote 0
Không cần đâu field đó đâu bác Ông kẹ, cứ lưu đại xuống với đuôi gì cũng đươc, mọi chuyện file content là gì thì đã có Office "no". Nó sẽ tự detect được file format là gì và phải dùng parser gì
 
Upvote 0
Không cần đâu field đó đâu bác Ông kẹ, cứ lưu đại xuống với đuôi gì cũng đươc, mọi chuyện file content là gì thì đã có Office "no". Nó sẽ tự detect được file format là gì và phải dùng parser gì

Có trường hợp lưu file văn bản dạng .pdf và .doc nữa bác à.
 
Upvote 0
À, thế thì phải thêm field đó rồi. 2 file type khác. Sorry ông kẹ nhen :)
File Office cũ thì luôn là D0 CF 11 E0 ở đầu file. Coder MS code cố tình chọn 4 byte đó, đó là DocFile đó các bạn, nhìn thấy không
Tui cũng hay dùng 0x0BADC0DE (bad code) đễ đánh dấu memory đít và đầu để detect bị dập memory.
File pdf thì 0x25 50 44 46 (%PDF) ở đầu. File Office mới bây giờ thì Zip format, 0x50 4B (PK) đầu tiên.

Các ct forensics, khôi phục file trên đĩa luôn luôn nhận dạng loại file dựa vào các bytes nhận dạng này để rút trích ngược lại file.

Cứ download HxD và mở ra sẽ thấy, coi như biết thêm cho vui :)
Hex editor miễn phí và mạnh thì chỉ có HxD là số 1, được viết = Delphi nhé, các bác download tại chính gốc đây:

Còn trình zip thì bà con đừng dùng WinRAR, lỗi bảo mật nhiều lắm, bị hacker khai thác nhiều. Cứ 7zip free mà dùng, internal viewer của nó support rất nhiều file format độc như PE, msi, các trình setup...
 
Lần chỉnh sửa cuối:
Upvote 0
Vậy là Ông Kẹ không cần fied FileExtension nữa nhen, cứ đọc 4 byte đầu của stream lên rồi detect format.
Còn có việc phải nhờ Ong Kẹ test đấy
Mã:
#If USE_FOR_ACCESS Then     ' Dành riêng cho Ông Ke

Public Property Get aeArrayFromPicture(ByVal objPic As StdPicture, ByVal pic As aePicFileType) As Byte()
    aeArrayFromPicture = ArrayFromPicture(objPic, pic)
End Property

Public Property Get aeResampleGDIP(ByVal Image As StdPicture, ByVal Width As Long, ByVal Height As Long) As StdPicture
    Set aeResampleGDIP = ResampleGDIP(Image, Width, Height)
End Property

Public Property Get aeLoadPictureGDIP(ByVal sFileName As String) As StdPicture
    Set aeLoadPictureGDIP = LoadPictureGDIP(sFileName)
End Property

Public Property Get aeAttachmentToPicture(ByVal strTable As String, ByVal strAttachmentField As String, ByVal strImage As String) As StdPicture
    Set aeAttachmentToPicture = AttachmentToPicture(strTable, strAttachmentField, strImage)
End Property

Public Property Get aeOLEFieldToPicture(ByVal strTable As String, ByVal strNameField As String, ByVal strName As String, ByVal strOLEField As String) As StdPicture
    Set aeOLEFieldToPicture = OLEFieldToPicture(strTable, strNameField, strName, strOLEField)
End Property

' Create a picture object from an OLE Field (BLOB, long binary)
' strTable:              Table containing OLE field with picture contents
' strNameField:          Name field to identify record
' strName:               Unique name of the picture in Name field
' strOLEField:           Name of OLE field in table
' ? OLEFieldToPicture("tblOLE","ImageName","cloudy","Blob").Width
Private Function OLEFieldToPicture(ByVal strTable As String, _
                                  ByVal strNameField As String, _
                                  ByVal strName As String, _
                                  ByVal strOLEField As String) As StdPicture
    Dim rst As ado.Recordset
    Set rst = CurrentDb.OpenRecordset("SELECT " & strOLEField & " FROM " & strTable & " WHERE " & strNameField & "='" & strName & "'", dbOpenDynaset)

    If Not rst.EOF Then
        Set OLEFieldToPicture = ArrayToPicture(rst(strOLEField).Value)
    End If
    
    rst.Close
    Set rst = Nothing
End Function

' Create a picture object from an Access 2007 attachment
' strTable:              Table containing picture file attachments
' strAttachmentField:    Name of the attachment column in the table
' strImage:              Name of the image to search in the attachment records
' ? AttachmentToPicture("ribbonimages","imageblob","cloudy.png").Width
Private Function AttachmentToPicture(ByVal strTable As String, ByVal strAttachmentField As String, ByVal strImage As String) As StdPicture
    Dim strSQL As String
    Dim bin() As Byte
    Dim nOffset As Long
    Dim nSize As Long

    strSQL = "SELECT " & strTable & "." & strAttachmentField & ".FileData AS data " & _
             "FROM " & strTable & _
             " WHERE " & strTable & "." & strAttachmentField & ".FileName='" & strImage & "'"
    
    On Error Resume Next
    
    bin = DBEngine(0)(0).OpenRecordset(strSQL, dbOpenSnapshot)(0)
    If Err.Number = 0 Then
        Dim bin2() As Byte
        
        nOffset = bin(0)    ' First byte of Field2.FileData identifies offset to the file data block
        nSize = UBound(bin)
        ReDim bin2(nSize - nOffset)
        CopyMemory bin2(0), bin(nOffset), nSize - nOffset   ' Copy file into new byte array starting at nOffset
        
        Set AttachmentToPicture = ArrayToPicture(bin2)
        
        Erase bin2
        Erase bin
    End If
End Function

#End If     ' USE_FOR_ACCESS
 
Upvote 0
Vậy là Ông Kẹ không cần fied FileExtension nữa nhen, cứ đọc 4 byte đầu của stream lên rồi detect format.
Còn có việc phải nhờ Ong Kẹ test đấy

Mã:
#If USE_FOR_ACCESS Then     ' Dành riêng cho Ông Ke

Public Property Get aeArrayFromPicture(ByVal objPic As StdPicture, ByVal pic As aePicFileType) As Byte()
    aeArrayFromPicture = ArrayFromPicture(objPic, pic)
End Property

Public Property Get aeResampleGDIP(ByVal Image As StdPicture, ByVal Width As Long, ByVal Height As Long) As StdPicture
    Set aeResampleGDIP = ResampleGDIP(Image, Width, Height)
End Property

Public Property Get aeLoadPictureGDIP(ByVal sFileName As String) As StdPicture
    Set aeLoadPictureGDIP = LoadPictureGDIP(sFileName)
End Property

Public Property Get aeAttachmentToPicture(ByVal strTable As String, ByVal strAttachmentField As String, ByVal strImage As String) As StdPicture
    Set aeAttachmentToPicture = AttachmentToPicture(strTable, strAttachmentField, strImage)
End Property

Public Property Get aeOLEFieldToPicture(ByVal strTable As String, ByVal strNameField As String, ByVal strName As String, ByVal strOLEField As String) As StdPicture
    Set aeOLEFieldToPicture = OLEFieldToPicture(strTable, strNameField, strName, strOLEField)
End Property
...


#End If     ' USE_FOR_ACCESS

- Đưa vô file Access rồi đây bác.
- Code đưa còn thiếu mấy cái hàm: ArrayFromPicture, ResampleGDIP, LoadPictureGDIP
- Báo lỗi "Type mismatch" chỗ biến "bin2"
 

File đính kèm

  • ImageBLOB.rar
    48.1 KB · Đọc: 9
Upvote 0
Hì hì, thôi để mình up đầy đủ lên đây cho Ông Kẹ với các bạn làm tiếp luôn, chứ nói thật tình là mình ngán mấy cái vụ xử lý ảnh với dùng API với VBA tới tận cổ luôn rồi. Không muốn, không có hứng làm chút nào. Làm 1 mình cũng ngán nữa.

Dùng API trên C# mình đã chữi um cái vụ khai báo với UnSafe của nó rồi huống chi VBA.
Mình chỉ dùng API với VC và Delphi thôi, cứ Pointer mà phang thôi, quen rồi. Hỏi bác Tuân thì biết.
Máy mình lại là Office 2007 nữa (VBE 6) nên làm xong phải port, test với VBE7 rồi Office64 nữa, nên "nười" quá bỏ luôn. Mà bỏ thì cũng uổng công đã tìm, port, gọt giũa, nên thôi up lên đây.

Bà con làm tiếp nhen, cơ bản trong file chúng ta đã có các hàm từ memory byte buffer to GDIPlus image và ngược lại. Có StdPicture rồi thì cứ get hay assign vào các đối tượng nào của Office support property StdPicture (IPicture).
Có byte buffer đó rồi các bác muốn làm, muốn chế sang Hex/Base64/xxx và ngược lại gì thì tùy các bác thêm mắm, thêm muối và nhu cầu sử dụng.

À quên, cứ image file format nào mà Excel mở được thì thư viện này mở được hết nhé, còn tại sao thì mình sẽ nói sau. Hồi sau sẽ rõ :)
Tốc độ thì các bác khỏi phải "no". API nó làm ở DLL viết = Visual C++ hết chứ VBA có làm cái gì đâu.
Bài đã được tự động gộp:

Tốt nhất là bác nào nắm được gọi API với VBA và đang có máy Win64 + Office64 ngồi port tiếp là tốt nhất. Win64 + Office64 mà chạy đúng thì Win32 + Office32 chắc chắn sẽ đúng, mà ngược lại thì không chắc nhen.
Có gì thét mét đừng ngại post lên trao đổi nhé các bác, cho nó xong vấn đề này cho rồi.
Mình thì bữa giờ kẹt với bác Tuân quá và không muốn làm cái này nữa.
 

File đính kèm

  • ImgFromHexAndBase64.xlsm
    84.2 KB · Đọc: 27
Lần chỉnh sửa cuối:
Upvote 0
Chổ USE_FOR_ACCESS mình phải bật False và chưa test, debug đó nhen ông kẹ, vì bật True thì khi compile VBA của Excel nó nhảy vào chửi mình, kekeke :)
Ông kẹ cứ bật True lên hay xóa nó đi trong môi trường Access của bạn nhé. Mình test trên Excel 2007 32bit là OK hết rồi đó.

Chổ CopyMemory bin2(0), bin(nOffset), nSize - nOffset, bên mình khai báo là ByVal xxx as Long/LongPtr nhé, nên ông kẹ chịu khó sữa thành sau nhé.
Mã:
CopyMemory VarPtr(bin2(0)), VarPtr(bin(nOffset)), nSize - nOffset
Mình ghét cái ông lanh chanh As Any của VB/VBA lắm, thích tường minh là số, là địa chỉ rõ ràng. ByRef thì không ưng lắm, nhưng phải xài thôi :)
 
Lần chỉnh sửa cuối:
Upvote 0
Không biết các bác @giaiphap, @ongke0711 port tới đâu rồi nhỉ, có làm tiếp hay không nhỉ, có kẹt, vướng mắc gì không ?

Về vụ File IO binary mode thì mình, như đã nói bên topic TextStream rồi, mình đã bo xì, nghĩ chơi VBA IO Funcs và TextStream, đã tìm ra giải pháp thay thế, dùng API của shlwapi.dll (Shell Lighweight API Library) của MS. Nó là các hàm IStream_xxx. shlwapi.dll này nói thật mình rất chi là "ưng cái bụng", hầu như cái gì cũng có cho những nhu cầu thông thường của mọi người. Và nó cũng được coder MS dùng rất nhiều trong các team, soft, component của họ. Ví dụ như ông Excel bà con đang chạy, nó cũng dùng dll này, ở dạng Delay Load (load trễ: kiểu trung gian giữa static link và dynamic call GetProcAddress). Khi trong code VBA bà con gọi nó thì sure 200% nó đã được load lên memory process của Excel.exe rồi. Nên VBDllFuncCall sẽ đở vất vã :)
Bên trong IStream_xxx, nó thao tác với API cấp thấp nhất, ntdll.dll API, nên cực nhanh và mạnh.
Các Object khác, phía trên của MS như ADO Stream, XML Stream... đều phải dùng, call tới các API này bên trong ruột.
Nhưng có 1 cái kẹt nhỏ xíu là nó viết ra với mục đích là dùng, để call cho/từ C++. Nó trả về và nhận vào pointer to IStream interface. Mà IStream interface thì VB/VBA không support, declare.
Nhưng không sao, 1 chút xíu tip, workaround nho nhỏ là mấy em nó phải ngoan ngoãn nghe lời, làm đúng theo anh VBA gọi à :)
Code VBA dùng, call IStream_Read/IStream_Write... không có trên Google đâu, chưa ai làm cả, bà con khỏi tìm mất công.
Bài đã được tự động gộp:

Về vụ Hex, Base64 thì các Dll của Windows không có thằng nào export ra hàm API nào để làm cả. Nên sau đó mình đã tìm trong các COM Object và đã tìm ra. Đó là capicom.dll. Dll của MS, luôn có với mọi version của Windows nhé, nên bà con an tâm. capicom = Crypto API COM Library. Phục vụ các tác vụ về mã hóa, crypto... tùm lum tùm la của MS. Trong CAPICOM có class Utilities, có các method làm các vụ Hex/Binary/Base64 này. Mình đã xem trong mã của nó và phải công nhận, nó code cực nhanh (MS coder siêu quá).
Bà con cứ add nó vào và tham khảo nhé. Trong References, Browse, nó nằm đầu tiên ở vần C đó. Cứ code các hàm nho nhỏ test các method của nó. Thích early bind thì add capicom.dll vào References, new CAPICOM.Utilities. Còn không muốn add Reference thì CreateObject("CAPICOM.Utilities").
Bài đã được tự động gộp:

Interface cho các bác code Delphi nó "như thế lày" đây :)
 

File đính kèm

  • 1.png
    1.png
    17.1 KB · Đọc: 27
  • 2.png
    2.png
    19.1 KB · Đọc: 27
  • 3.png
    3.png
    73.1 KB · Đọc: 26
Lần chỉnh sửa cuối:
Upvote 0
Nhờ các bác kiểm tra giúp, trên Win64,Office 64, mảng động dim arrB() as Byte có thể Redim tối đa được bao nhiêu phần tử ?
 
Upvote 0
Tiếp tục chủ đề các bạn ơi, chưa xong mà.
- Vụ byte array sang Image StdPicture và ngược lại chúng ta đã coi như xong. Chỉ còn chờ các bạn port và test thôi.
- Vụ từ byte array sang hex string và ngược lại thì chúng ta cũng đã xong, bên topic Undocument VBA. Tốc độ tạm tạm "ược" :)
Giờ chỉ còn vụ IO, vì chúng ta không dùng TextStream và Open statement của VBA được.
Tui code mình tui dùng thì không nói gì rồi, nhưng muốn share cho các bạn dùng sau này luôn, nên mới hỏi các bạn cần các tính năng nào của hàm.
Ví dụ tôi giờ chỉ thấy cần dùng 2 hàm tôi đặt tên là
FileBinary_ReadAll(strPath as string, arrData() as Byte) as Boolean: Read tất cả content của 1 file chỉ ra trong strPath vào arrData. 1 lần call duy nhất.
FileBinary_WriteAll(strPath as String, arrData() as Byte) as Boolean: Write, ghi đè toàn bộ nội dung file cũ của file chỉ bởi strPath bằng data trong arrData. 1 lần call duy nhất.
Vì strPath là string VBA Unicode giữ nguyên bản truyền xuống cho các API IStream_ nên các bạn không lo vụ file có dấu.
Và IStream_ support file cực bự, 2^64 byte trên cả Win32 và Win64, các bạn có cần đến không, hay chỉ cần trong phạm vi biến Long dương của VBA, 2GB thôi ?
Và các bạn cần hàm nào nữa để dùng sau này thì cứ liệt kê ra, mình viết 1 lần vào modUtils.pas cho sau này các bạn mang đi độc lập xài luôn, không dính dáng gì tới clsGDIPlug và modAPITypes ? Cứ môi trường VBA của Office xài được !
 
Upvote 0
- Đưa vô file Access rồi đây bác.
- Code đưa còn thiếu mấy cái hàm: ArrayFromPicture, ResampleGDIP, LoadPictureGDIP
- Báo lỗi "Type mismatch" chỗ biến "bin2"
đang làm cái File lưu CMND vào Data Access xem lại bài này ... thì thấy kiểu như đính kèm file Ảnh vào Access ý

Còn 1 cách khác nữa là lưu vào Access cũng xem ok xem hình dưới tên 22,33,777 là tên ảnh đó
vậy cách nào hay bạn nhỉ ???

1600347703799.png
 
Upvote 0
đang làm cái File lưu CMND vào Data Access xem lại bài này ... thì thấy kiểu như đính kèm file Ảnh vào Access ý

Còn 1 cách khác nữa là lưu vào Access cũng xem ok xem hình dưới tên 22,33,777 là tên ảnh đó
vậy cách nào hay bạn nhỉ ???

Hình 1 là dùng kiểu dữ liệu "Attachment" có sẳn từ Access 2007 trở lên, đem qua các máy xài Office phiên bản thấp hơn là không chạy được.
Hình 2 là dùng kiểu dữ liệu OLE Object, kiểu dữ liệu này thì Access 2003 cũng có nên tính tương thích cao hơn.
Muốn thao tác nhanh thì dùng Attachment, tải file ảnh lên để lưu nhanh. Còn cách 2 thì dùng phương thức GETCHUNK/ APPENDCHUNK của thư viện DAO hoặc ADO để convert file ảnh thành dạng BInary rồi lưu xuống Field OLE Object.
2 cách trên đều lưu file ảnh vào hệ thống Table của Access sẽ làm file tăng dung lượng nhanh. Tại sao bạn không dùng đường dẫn file ảnh để nhẹ nhàng hơn. Tôi thường dùng cách 2 (lưu xuống Table) cho các file ảnh Logo, Icon nút lệnh dung lượng vài chục kB để khi truy xuất sẽ nhanh hơn thôi.
 
Upvote 0
Web KT
Back
Top Bottom