I have a camera on a robot set up. Long story short, the camera outputs the binary array, which I believe is a JPG encoded stream. I say this since a lot of the methods in the library I'm using give hint to it being JPG-encoded, and the first and last 2 bytes are 255 216
and 255 217
respectively, which are the magic numbers of the JPG file format.
How can I convert this array of integers to a JPG file in Python?
I tried writing the array of integers to a .JPG file, but the file is labeled as corrupt.
Long story short, I have an ArduCam hooked to a esp8266 (literally an Arduino board that can connect to WiFi). I have a script that captures a photo, and uploads the raw image data to a server hosted on the esp8266. Here is the main method I am using:
void camCapture(ArduCAM myCAM) {
WiFiClient client = server.client(); // ignore this stuff
uint32_t len = myCAM.read_fifo_length();
if (len >= MAX_FIFO_SIZE) //8M
{
Serial.println(F("Over size."));
}
if (len == 0 ) //0 kb
{
Serial.println(F("Size is 0."));
}
myCAM.CS_LOW();
myCAM.set_fifo_burst();
if (!client.connected()) return;
String response = "[";
i = 0;
while ( len-- ) // this is where the raw image is collection
{
temp_last = temp;
temp = SPI.transfer(0x00);
//Read JPEG data from FIFO
if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
{
buffer[i++] = temp; //save the last 0XD9
response += String(int(temp)) + "], ";
response += String(i);
if (!client.connected()) break;
Serial.print(String(i));
server.send(200,"text/plain",response); // this is where the image is uploaded
is_header = false;
i = 0;
myCAM.CS_HIGH();
break;
}
if (is_header == true)
{
//Write image data to buffer if not full
if (i < bufferSize)
buffer[i++] = temp;
else
{
//Write bufferSize bytes image data to file
if (!client.connected()) break;
i = 0;
buffer[i++] = temp;
}
response += String(int(temp)) + ",";
}
else if ((temp == 0xD8) & (temp_last == 0xFF))
{
is_header = true;
buffer[i++] = temp_last;
buffer[i++] = temp;
response += String(int(temp_last)) + "," + String(int(temp)) + ",";
}
}
}
The raw image data looks like this:
255,216,255,224,0,16,74,70,73,70,0,1,1,1,0,0,0,0,0,0,255,219,74,74,74,74,74,74,74,74,74,74,74,74,74,74 [...] 64,143,255,217
Now I know jpeg files start and end with 255,216 and 255,217 respectively. So I see this as a good sign. Furthermore, when I use the jpeg html code, it actually converts the raw image data into an actual image.
Here is my python script where I attempt to decode the raw image data into a .jpg file:
import cv2
with open('img.txt') as file: # this is where I keep the raw image data
img = file.read()
img = img.split(',')
img = [int(i) for i in img]
tmpfile = open("tmp.jpg", "wb")
for i in img:
tmpfile.write(bytes(i))
tmpfile.close()
img = cv2.imread('tmp.jpg')
print(img)
cv2.imshow('img',img)
cv2.waitKey(0)
You are dumping the string representation of the values (i.e. "2"
, "5"
, and "5"
instead of 0xFF
) into the file since bytes()
will convert your int
to a byte string. You could have noticed that from the file size as it won't match the number of values in tmp.txt
.
The correct way to write bytes to a file is as follows:
import struct
...
for i in img:
tmpfile.write(struct.pack("B", i))
...
Since you are working with 8-bit values in this case, you can also use chr(i)
.