Issue saving JPG images from binary data in python - python

I am having issues with a function that creates JPG images given a binary string. The program saves two images in quick succession, the first is ~300kb and the second is a cropped version of the same image, around ~30kb
The first (larger) image always saves correctly, however the second image sometimes (Maybe 1 in 4) gets cut off half way, with the lower part of the image being flat grey. Opening the image in notepad++ , it looks like the data just abruptly stops being written
The function that creates the images:
def writeImage(imageData, decoded, imageNumber, config):
if imageNumber == 1:
imageSavePath = image1name
elif imageNumber == 2:
imageSavePath = image2name
print(imageSavePath)
file = open(imageSavePath, 'w+b')
file.write(imageData)
file.close
https://i.imgur.com/T4WSOEX.jpg
This is an example of how the images turn out, the amount that is cut off varies image to image

It sounds like your file isn't being flushed before closing. This should happen automatically when you close it. Your code appears to be missing the () for the close call, e.g. it should be
file.close()
The more pythonic way of handling the file object though is to use a with statement as a context manager. So the code would look like this
def writeImage(imageData, decoded, imageNumber, config):
if imageNumber == 1:
imageSavePath = image1name
elif imageNumber == 2:
imageSavePath = image2name
print(imageSavePath)
with open(imageSavePath, 'w+b') as file:
file.write(imageData)
The file will automatically be closed when you are done with executing the statements nested in the with. This ensures you don't forget to close it and leak the file descriptor.

Related

Python: Create video out of processed images

i have a video where the front view of a car was recorded. The file is an .mp4 and i want to process the single images so i can extract more information (Objects, Lane Lines etc.). The problem is, when i want to create a video out of the processed files, i get an error. Here is what i have done so far:
Opened the video with cv2.VideoCapture() - Works fine
Saved the single frames of the video with cv2.imwrite() - Works fine
Creating a video out of single frames with cv2.VideoWriter() - Works fine
Postprocessing the video with cv2.cvtColor(), cv2.GaussianBlur() and cv2.Canny() - Works fine
Creating a video out of the processed images - Does not work.
Here is the code i used:
enter code here
def process_image(image):
gray = functions.grayscale(image)
blur_gray = functions.gaussian_blur(gray, 5)
canny_blur = functions.canny(blur_gray, 100, 200)
return canny_blur
process_on =0
count =0
video= cv2.VideoWriter("output.avi", cv2.VideoWriter_fourcc(*"MJPG"), 10, (1600, 1200))
vidcap = cv2.VideoCapture('input.mp4')
success,image = vidcap.read()
success = True
while success:
processed = process_image(image)
video.write(processed)
This is the error i get:
OpenCV Error: Assertion failed (img.cols == width && img.rows == height*3) in cv::mjpeg::MotionJpegWriter::write, file D:\Build\OpenCV\opencv-3.2.0\modules\videoio\src\cap_mjpeg_encoder.cpp, line 834
Traceback (most recent call last):
File "W:/Roborace/03_Information/10_Data/01_Montreal/camera/right_front_camera/01_Processed/Roborace_CAMERA_create_video.py", line 30, in
video.write(processed)
cv2.error: D:\Build\OpenCV\opencv-3.2.0\modules\videoio\src\cap_mjpeg_encoder.cpp:834: error: (-215) img.cols == width && img.rows == height*3 in function cv::mjpeg::MotionJpegWriter::write
My suggestion is: The normal images have 3 dimensions because of the RGB-color field. The processed images are only having one dimension. How can i adjust this in the cv2.VideoWriter function.
Thanks for your help
The VideoWriter() class only writes color images, not grayscale images unless you are on Windows, which it looks like you might be judging from the paths in your output. On Windows, you can pass the optional argument isColor=0 or isColor=False to the VideoWriter() constructor to write single-channel images. Otherwise, the simple solution is to just stack your grayscale frames into a three-channel image (you can use cv2.merge([gray, gray, gray]) and write that.
From the VideoWriter() docs:
Parameters:
isColor – If it is not zero, the encoder will expect and encode color frames, otherwise it will work with grayscale frames (the flag is currently supported on Windows only).
So by default, isColor=True and the flag cannot be changed on a non-Windows system. So simply doing:
video.write(cv2.merge([processed, processed, processed])
should patch you up. Even though the Windows variant allows writing grayscale frames, it may be better to use this second method for platform independence.
Also as Zindarod mentions in the comments below there are a number of other possible issues with your code here. I'm assuming you've pasted modified code that you weren't actually running here, or that you would have modified otherwise...if that's the case, please only post minimal, complete, and verifiable code examples.
First and foremost, your loop has no end condition, so it's indefinite. Secondly, you're hard-coding the frame size but VideoWriter() does not simply resize the images to that size. You must provide the size of the frame that you will pass into the VideoWriter(). Either resize the frame to the same size before writing to be sure, or create your VideoWriter using the frame size as defined in your VideoCapture() device (using the .get() methods for the frame size properties).
Additionally, you're reading only the first frame outside the loop. Maybe that was intentional, but if you want to process each frame of the video, you'll need to of course read them in a loop, process them, and then write them.
Lastly you should have some better error catching in your code. For e.g., see the OpenCV "Getting Started with Video" Python tutorial. The "Saving a Video" section has the proper checks and balances: run the loop while the video capture device is opened, and process and write the frame only if the frame was read properly; then once it is out of frames, the .read() method will return False, which will allow you to break out of the loop and then close the capture and writer devices. Note the ordering here---the VideoCapture() device will still be "opened" even when you've read the last frame, so you need to close out of the loop by checking the contents of the frame.
Add isColor=False argument to the VideoWriter.
Adjusting VideoWriter this way will solve the issue.
Code:
video= cv2.VideoWriter("output.avi", cv2.VideoWriter_fourcc(*"MJPG"), 10, (1600, 1200), isColor=False)

Renaming image files if statement is false

So explained briefly I have a folder where I put some images into. The name of the images are integers, so for example I could have 5 images with the names 567.jpg, 568.jpg, 569.jpg, 570.jpg, and 571.jpg. The integer numbers are random, however, they always come in a sequence like above, i.e. increasing by one for every image.
So what I want to do is to go through the images in this folder and look at the image size. If the width of an image is under 600px (as an example) I want to delete this image and move on to the next image. Now, if this image is then above 600px it should be renamed such as it follows the sequence.
So in principle what should happen is:
345.jpg --> Over 600px --> Nothing happens
346-jpg --> Under 600px --> Gets deleted
347.jph --> Over 600px --> Gets renamed to 346.jpg (to follow the 1 step sequence)
And so on...
I have tried with the following code:
import os
from PIL import Image
img_dir_path = "\"
pic_list = range(567,572,1)
for image in pic_list:
img = Image.open("{}/{}.jpg".format(img_dir_path, image))
if img.size[0] < 600:
os.remove("{}/{}.jpg".format(img_dir_path, image))
else:
if os.path.isfile("{}/{}.jpg".format(img_dir_path, int(image)-1)) == False:
os.rename('{}/{}.jpg'.format(img_dir_path, int(image)), '{}/{}.jpg'.format(img_dir_path, int(image)-1))
else:
print "No worries"
However, when doing so I get the follow error:
WindowsError: [Error 32] The process cannot access the file because it is being used by another process
For the rename process that it...
I know this is some sloppy code, but I'm not a Python expert yet, so I usually just make it work first, and then tweak.
In addition, another problem that arises is that when checking if the entry before the true first entry exists / is false, it always is false now since there of course is nothing before the first image. Therefore, the image with the first name in the list should of course not be renamed. However, since I couldn't even get this to work, I wasn't going to try to fix that.
Do close the image which is opened before renaming img = Image.open("{}/{}.jpg".format(img_dir_path, image))
Image.close()
before rename

Passing On Values Between Functions

Hi i'm working on a program where a user selects an image, then it gets turned into an ASCII version of the picture, which displays in both the console and then gets saved to a notepad .txt file. The code below currently does this but I think the way I am using my returns in the functions is messing up the code. Instead of asking for the image and background colour once, it asks for it 3 times before actually drawing out the ASCII image. Then after it has drawn it, it again asks for a 4th time for the image and background colour, before finally asking for the name to give the .txt file then creating the text file.
How do I change this so that it only asks each question once without repeating itself(and what exactly is this happening?). Thanks
def inputs():
image = input('What image file?')
return (image)
def pictureConvert():
a = inputs()
# codefollows
Call the inputs function only once, and store that everywhere. This ensures that the user will be asked for input only once:
def inputs():
image = input('What image file?')
return (image)
def pictureConvert():
a = inputs()
# codefollows

Python/Pygame Converting a .jpg to a string and back to a .jpg: Corruption Issue

I'm making a program in Python using Pygame that will load an image to the screen, open the raw data (as in, the characters you would see if you opened the jpg as a text file), throw some random characters in with the data, and then resave it as a jpg to load into pygame again. This results in a cool looking glitch effect.
I am not having any problems with the desired glitches, but I was finding that despite what kind of random character was placed where, for certain images every time the image went through my glitch function I ended up with a grey bar on the bottom of the image. I simplified my function so that all it did was load the image, open the image as a read binary (even though I'm on a mac), save a string of the raw data, write a new file based on this string and then load that file. The image was not purposefully glitched in any way, and the data was supposedly untouched but I still encountered this grey bar.
Here is the relevant code:
def initializeScreen(x, y):
pygame.display.set_mode((x,y))
return pygame.display.get_surface()
def importImage(fileName):
imgText = open(fileName, 'rb')
imgTextStr = imgText.read()
imgText.close()
return imgTextStr
screenSurf = initializeScreen(800,600)
textOfImg = importImage('/Users/Amoeba/Desktop/GlitchDriving/Clouds.jpg')
newFile = open('/Users/Amoeba/Desktop/GlitchDriving/tempGlitchFile.jpg', 'wb')
newFile.write(textOfImg)
newimgSurf = pygame.image.load('/Users/Amoeba/Desktop/GlitchDriving/tempGlitchFile.jpg')
screenSurf.blit(newimgSurf, (0,0))
pygame.display.flip()
Here is an example of one of the images before and after passing through my function:
It is worth noting that the size of the grey bar depends on the picture. Some pictures even pass through my function visibly unchanged, as they should be. Also, if I open the new version of the jpg written by my program with image viewing software like preview, the grey bar does not appear. My suspicion is that it is a quirk of the pygame image load function or that there is some strange character (or possibly white space) that is being dropped in my conversion from jpg to string or vice-versa. I did compare two of the text files (one with grey bar and one without) and found no difference while using an online "difference finder".
This is my first post here, but I've lurked for answers dozens of times. Any help is greatly appreciated.
You never close the file object you create with open, so probably not all data gets written back (flushed) to your new file.
Either close the file object before trying to read the file again, or better start using the with statement (which will close the file for you) whenever you deal with files:
def importImage(fileName):
with open(fileName, 'rb') as imgText:
return imgText.read()
screenSurf = initializeScreen(800,600)
textOfImg = importImage(r'path/to/file')
with open(r'path/to/otherfile', 'wb') as newFile:
newFile.write(textOfImg)
newimgSurf = pygame.image.load(r'path/to/otherfile')
screenSurf.blit(newimgSurf, (0,0))
pygame.display.flip()

from string of bytes to OpenCV's IplImage in Python?

I am streaming some data down from a webcam. When I get all of the bytes for a full image (in a string called byteString) I want to display the image using OpenCV. Done fast enough, this will "stream" video from the webcam to an OpenCV window.
Here's what I've done to set up the window:
cvNamedWindow('name of window', CV_WINDOW_AUTOSIZE)
And here's what I do when the byte string is complete:
img = cvCreateImage(IMG_SIZE,PIXEL_DEPTH,CHANNELS)
buf = ctypes.create_string_buffer(byteString)
img.imageData = ctypes.cast(buf, ctypes.POINTER(ctypes.c_byte))
cvShowImage('name of window', img)
cvWaitKey(0)
For some reason this is producing an error:
File "C:\Python26\lib\site-packages\ctypes_opencv\highgui_win32.py", line 226, in execute
return func(*args, **kwargs)
WindowsError: exception: access violation reading 0x015399E8
Does anybody know how to do what I'm trying to do / how to fix this crazy violation error?
I actually solved this problem and forgot to post the solution. Here's how I did it, though it may not be entirely robust:
I analyzed the headers coming from the MJPEG of the network camera I was doing this to, then I just read from the stream 1 byte at a time, and, when I detected that the header of the next image was also in the bytestring, I cut the last 42 bytes off (since that's the length of the header).
Then I had the bytes of the JPEG, so I simply created a new Cv Image by using the open(...) method and passing it the byte string wrapped in a StringIO class.
Tyler:
I'm not sure what you are trying to do..i have a few guesses.
if you are trying to simply read an image from a webcam connected to your pc then this code should work:
import cv
cv.NamedWindow("camera", 1)
capture = cv.CaptureFromCAM(0)
while True:
img = cv.QueryFrame(capture)
cv.ShowImage("camera", img)
if cv.WaitKey(10) == 27:
break
are you trying to stream video from an internet cam?
if so, you should check this other post:
opencv-with-network-cameras
If for some reason you cannot do it in any of these ways then may be you can just somehow savethe image on the hard drive and then load it in your opencv program by doing a simple cvLoadImage ( of course this way is much slower).
another approach would be to set the new image pixels by hand by reading each of the values from the byteString, doing something like this:
for(int x=0;x<640;x++){
for(int y=0;y<480;y++){
uchar * pixelxy=&((uchar*) (img->imageData+img->widthStep*y))[x];
*pixelxy=buf[y*img->widthStep + x];
}
}
this is also slower but faster than using the hard drive.
Anyway, hope some of this helps, you should also specify which opencv version are you using.

Categories