Making image brightened in python using putdata - python

This is the question:
Write a program in Python which opens the file 'pixerror.png', removes the S&P noise and will correct the brightness of the image, save the processed photo under another name. You have to look pixel for pixel and cannot use blurb functions.
My fist task is to get the image brighter.
I used this question code, but i cannot solve my error that I have. This error:
_
ruisclasse.py 41
putdata exceptions.SystemError:
new style getargs format but argument is not a tuple
_
And this is my code.
_
from __future__ import division #for floating number
from PIL import Image
import cv2
filename='pixerror.png'
action = 'lighten'
extent = 10
#load the original image into a list
original_image = Image.open(filename, 'r')
pixels = original_image.getdata()
#initialise the new image
new_image = Image.new('RGB', original_image.size)
new_image_list = []
brightness_multiplier = 1.1
if action == 'lighten':
brightness_multiplier += (extent/100)
else:
brightness_multiplier -= (extent/100)
#for each pixel, append the brightened or darkened version to the new image list
for pixel in pixels:
new_pixel = (int(pixel[0] * brightness_multiplier),
int(pixel[1] * brightness_multiplier),
int(pixel[2] * brightness_multiplier))
#check the new pixel values are within rgb range
new_pixel= [max(min(channel, 255), 0) for channel in new_pixel]
new_image_list.append(new_pixel)
#save the new image
new_image.putdata(new_image_list)
new_image.save('colour_brightness.jpg')
cv2.waitKey(0)
cv2.destroyAllWindows()

new_pixel= [max(min(channel, 255), 0) for channel in new_pixel]
new_image_list.append(new_pixel)
You're appending lists to new_image_list, but you should be appending tuples.
new_pixel= [max(min(channel, 255), 0) for channel in new_pixel]
new_image_list.append(tuple(new_pixel))

Related

saving image using matplotlib in python

I'm trying to make a simple code that loads an image, divide the value of each pixel by 2 and stores the image. The image is stored in an array [1280][720][3]. After changing the value of each pixel I've chequed that the values are the expected. For some reason the values are correct but when I store the new image and check it, the values of the pixels are not the same as before...
The image is 1280x720 pixels and each pixel has 3 bytes (one for each color rgb)
import matplotlib.image as mpimg
img = mpimg.imread('image.jpg') # (1280, 720, 3)
myImg = []
for row in img:
myRow = []
for pixel in row:
myPixel = []
for color in pixel:
myPixel.append(color // 2)
myRow.append(myPixel)
myImg.append(myRow)
mpimg.imsave("foo.jpg", myImg)
img is a numpy array, so you can just use img / 2. It's also much faster than using a list loop.
myImg = img / 2
mpimg.imsave("foo.jpg", myImg)

Putpixel function not generating all the pixels

My goal is to generate a color per pixel in order to fill up the whole canvas however the image generated always turns out black with only one of its pixels changed color, I can't seem to figure what I'm doing wrong.
import random
from PIL import Image
canvas = Image.new("RGB", (300,300))
y = random.randint(1, canvas.width)
x = random.randint(1, canvas.width)
r = random.randint(0,255)
g = random.randint(0,255)
b = random.randint(0,255)
rgb = (r,g,b)
for i in range(canvas.width):
canvas.putpixel((x,y), (rgb))
canvas.save("test.png", "PNG")
print("Image saved successfully.")
You really should try and avoid using for loops in any Python image processing - they are slow and error-prone.
The easiest and fastest way to make a random image is using vectorised Numpy functions like this:
import numpy as np
from PIL import Image
# Create Numpy array 300x300x3 of random uint8
data = np.random.randint(0, 256, (300,300,3), dtype=np.uint8)
# Make into PIL Image
im = Image.fromarray(data)
The problem with your code is that you are not iterating over every pixel. I've modified your code to iterate over every pixel, check whether or not it is black (0,0,0), then place a pixel on that iteration with your randomly-generated rgb value. Then, I regenerate 3 new random numbers and place them back into the rgb tuple causing the next pixel in the loop to have a different rgb value.
The x and y definitions are redundant, as you want a random color for every pixel but do not want random pixels, so I have removed them. I added a declaration, pixels = canvas.load() which allocates memory for the pixels so you can iterate over them and change each individual color. I heavily relied on this similar stackoverflow question, if you want further information. Here is my code:
canvas = Image.new("RGB", (300,300))
pixels = canvas.load()
width, height = canvas.size
for i in range(width):
for j in range(height):
if pixels[i,j] == (0,0,0):
r = random.randint(0,255)
g = random.randint(0,255)
b = random.randint(0,255)
rgb = (r,g,b)
canvas.putpixel((i,j), (rgb))
canvas.save("test.png", "PNG")
print("Image saved successfully.")
Here is the output produced:

Change colors of photos in PIL

So in my coursework, I am supposed to change the different variations of colors and get an image that looks like the example provided. I have gotten halfway there, but for the life of me cannot figure out how to get the colors to change, even a little bit. Even if the code runs without errors, the colors will not change
i've tried numpy arrays, editing the pixel color, etc. I can not get anything to work.
import PIL
from PIL import Image
from PIL import ImageEnhance
from PIL import ImageDraw
from PIL import ImageFont
fnt = ImageFont.truetype('readonly/fanwood-webfont.ttf', 75)
# read image and convert to RGB
image=Image.open("readonly/msi_recruitment.gif")
image=image.convert('RGB')
drawing_object = ImageDraw.Draw(image)
# build a list of 9 images which have different brightnesses
enhancer=ImageEnhance.Brightness(image)
images=[]
x = 1
for i in range(0, 10):
pixels = img.load()
print(image.size)
x += 1
z = x
if x%3 == 1 :
z = 9
drawing_object.rectangle((0,450,800,325), fill='black')
drawing_object.text((20,350),'channel intensity o 0.{}'.format(z), font=fnt, fill=(255,255,255))
elif x%3 == 0:
z = 5
drawing_object.rectangle((0,450,800,325), fill='black')
drawing_object.text((20,350),'channel intensity o 0.{}'.format(z), font=fnt, fill=(255,255,255))
else:
z = 1
drawing_object.rectangle((0,450,800,325), fill='black')
drawing_object.text((20,350),'channel intensity o 0.{}'.format(z), font=fnt, fill=(255,255,255))
images.append(enhancer.enhance(10/10))
## create a contact sheet from different brightnesses
first_image=images[0]
contact_sheet=PIL.Image.new(first_image.mode, (first_image.width*3,first_image.height*3))
x=0
y=0
for img in images:
# Lets paste the current image into the contact sheet
contact_sheet.paste(img, (x, y) )
# Now we update our X position. If it is going to be the width of the image, then we set it to 0
# and update Y as well to point to the next "line" of the contact sheet.
if x+first_image.width == contact_sheet.width:
x=0
y=y+first_image.height
else:
x=x+first_image.width
# resize and display the contact sheet
contact_sheet = contact_sheet.resize((int(contact_sheet.width/2),int(contact_sheet.height/2) ))
display(contact_sheet)
So in general you can use OpenCVs ColorMap to change colors in your image:
import cv2
img = cv2.imread(r"<IMAGE PATH>")
img = cv2.applyColorMap(img, cv2.COLORMAP_JET) # Change colors of image with predefined colormap
# Display the new image
cv2.imshow("img", img)
cv2.waitKey()
Now if you want to create your own colormap instead of using a predefined one in OpenCV you can do it with cv2.LUT() (OpenCV Documentation)
Example Input:
Example Output:
And here would be a quick example of how to change Gamma with this approach:
def adjust_gamma(img, gamma=1.0):
assert (img.shape[0] == 1)
invGamma = 1.0 / gamma
table = np.array([((i / 255.0) ** invGamma) *
255 for i in np.arange(0, 256)]).astype("uint8")
new_img = cv2.LUT(np.array(img, dtype=np.uint8), table)
return new_img
It looks like you have a typo in your loop:
for i in range(0, 10):
# if-elses
...
images.append(enhancer.enhance(10/10))
You're enhancing the image with a factor of 1.0 at each iteration, which according to the docs:
Adjust image brightness.
This class can be used to control the brightness of an image. An enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the original image.
Using z, which I suspect was your intention, will give you the tiers of brightness your comments describe:
for i in range(0, 10):
# if-elses
...
images.append(enhancer.enhance(z/10))
Edit: changed assumed value from i to z, based on the text you're writing.
I should also point out that you probably want to create a temp "watermark" image during the for-loop to apply the text/rectangle to. Since the ImageDraw objects modify the image in-place, you're applying the text on top of each other at each iteration, causing some weird text on the later images.

Python - Ocr with Knn-Algorithm expand testdata

I try to write a Ocr for numbers in Python using OpenCV and the Knn-Algorithm. The code works pretty well but i want to expand my input data to move on to handwritten digits. Training / input of data works like that: You run the script, where the path of a image is, then the image opens with a rectangle around the number and you have to press the number on the keyboard. At the end it saves the classifications and flattened images in a textfile.
The problem is that it overrides the old textfiles so this data gets lost.
Is it possible / does it work to append the new and the old textfile?
This is the code:
import sys
import numpy as np
import cv2
import os
MIN_CONTOUR_AREA = 35
RESIZED_IMAGE_WIDTH = 20
RESIZED_IMAGE_HEIGHT = 30
def main():
imgTrainingNumbers = cv2.imread("training_chars.png")
if imgTrainingNumbers is None: # if image was not read successfully
print "error: image not read from file \n\n" # print error message to std out
os.system("pause") # pause so user can see error message
return # and exit function (which exits program)
# end if
imgGray = cv2.cvtColor(imgTrainingNumbers, cv2.COLOR_BGR2GRAY) # get grayscale image
imgBlurred = cv2.GaussianBlur(imgGray, (5,5), 0) # blur
# filter image from grayscale to black and white
imgThresh = cv2.adaptiveThreshold(imgBlurred, # input image
255, # make pixels that pass the threshold full white
cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # use gaussian rather than mean, seems to give better results
cv2.THRESH_BINARY_INV, # invert so foreground will be white, background will be black
11, # size of a pixel neighborhood used to calculate threshold value
2) # constant subtracted from the mean or weighted mean
cv2.imshow("imgThresh", imgThresh) # show threshold image for reference
imgThreshCopy = imgThresh.copy() # make a copy of the thresh image, this in necessary b/c findContours modifies the image
imgContours, npaContours, npaHierarchy = cv2.findContours(imgThreshCopy, # input image, make sure to use a copy since the function will modify this image in the course of finding contours
cv2.RETR_EXTERNAL, # retrieve the outermost contours only
cv2.CHAIN_APPROX_SIMPLE) # compress horizontal, vertical, and diagonal segments and leave only their end points
# declare empty numpy array, we will use this to write to file later
# zero rows, enough cols to hold all image data
npaFlattenedImages = np.empty((0, RESIZED_IMAGE_WIDTH * RESIZED_IMAGE_HEIGHT))
intClassifications = [] # declare empty classifications list, this will be our list of how we are classifying our chars from user input, we will write to file at the end
# possible chars we are interested in are digits 0 through 9, put these in list intValidChars
intValidChars = [ord('0'), ord('1'), ord('2'), ord('3'), ord('4'), ord('5'), ord('6'), ord('7'), ord('8'), ord('9'),
ord('-'), ord('o'), ord('c')]
for npaContour in npaContours: # for each contour
if cv2.contourArea(npaContour) > MIN_CONTOUR_AREA: # if contour is big enough to consider
[intX, intY, intW, intH] = cv2.boundingRect(npaContour) # get and break out bounding rect
# draw rectangle around each contour as we ask user for input
cv2.rectangle(imgTrainingNumbers, # draw rectangle on original training image
(intX, intY), # upper left corner
(intX+intW,intY+intH), # lower right corner
(0, 0, 255), # red
2) # thickness
imgROI = imgThresh[intY:intY+intH, intX:intX+intW] # crop char out of threshold image
imgROIResized = cv2.resize(imgROI, (RESIZED_IMAGE_WIDTH, RESIZED_IMAGE_HEIGHT)) # resize image, this will be more consistent for recognition and storage
cv2.imshow("imgROI", imgROI) # show cropped out char for reference
cv2.imshow("imgROIResized", imgROIResized) # show resized image for reference
cv2.imshow("training_numbers.png", imgTrainingNumbers) # show training numbers image, this will now have red rectangles drawn on it
intChar = cv2.waitKey(0) # get key press
if intChar == 27: # if esc key was pressed
sys.exit() # exit program
elif intChar in intValidChars: # else if the char is in the list of chars we are looking for . . .
intClassifications.append(intChar) # append classification char to integer list of chars (we will convert to float later before writing to file)
npaFlattenedImage = imgROIResized.reshape((1, RESIZED_IMAGE_WIDTH * RESIZED_IMAGE_HEIGHT)) # flatten image to 1d numpy array so we can write to file later
npaFlattenedImages = np.append(npaFlattenedImages, npaFlattenedImage, 0) # add current flattened impage numpy array to list of flattened image numpy arrays
# end if
# end if
# end for
fltClassifications = np.array(intClassifications, np.float32) # convert classifications list of ints to numpy array of floats
npaClassifications = fltClassifications.reshape((fltClassifications.size, 1)) # flatten numpy array of floats to 1d so we can write to file later
print "\n\ntraining complete !!\n"
np.savetxt("classifications.txt", npaClassifications) # write flattened images to file
np.savetxt("flattened_images.txt", npaFlattenedImages) #
cv2.destroyAllWindows()
return
Thank you
This should fix it.
print "\n\ntraining complete !!\n"
if os.path.exists("classifications.txt") == False:
np.savetxt("classifications.txt", npaClassifications)
np.savetxt("flattened_images.txt", npaFlattenedImages)
elif os.stat("classifications.txt").st_size == 0:
np.savetxt("classifications.txt", npaClassifications)
np.savetxt("flattened_images.txt", npaFlattenedImages) #
else:
f = open("classifications.txt", 'ab')
np.savetxt(f, npaClassifications)
f.close()
e = open("flattened_images.txt", 'ab')
np.savetxt(e, npaFlattenedImages)

How to count bright pixels in an image?

How do I make this code print the total number of bright pixels that are over 200:
from PIL import Image
img = input("File name: ")
img = Image.open(img);
for y in range(img.height):
for x in range(img.width):
pixel = img.getpixel((x, y))
if pixel >= 200:
print(pixel,"pixels are bright.")
Right now it's printing every single pixel that is over 200 on new lines, but I just want one line that prints the total like this:
File name: slippers.png
121081 pixels are bright.
You don't need loops at all for this. Simply create a mask returning which pixels are above the threshold, and sum the mask.
With numpy
You just need to convert the img from a PIL Image to a numpy array, which you can do with np.array(img). Then create a boolean mask for whenever the pixels are above your threshold, np.array(img) >= 200. This will create an array of the same size as your image with a True or False in each pixel location for whether it meets the criteria. Then if you np.sum() the resulting image, it will convert True to 1 and False to 0, so summing will give the total number of pixels which met the criteria. All of this in one line:
bright_count = np.sum(np.array(img) >= 200)
Pure PIL
For a purely PIL solution that doesn't use numpy, you can use the point() method of the Image class. See this question/answer for a good discussion of the method. The point() method takes in a function which assigns new values to a pixel. Here I've just assigned a value of 1 whenever it's above the threshold. Then I've grabbed just the data from the Image type with the getdata() method, and summed the data with the Python sum() function.
bright_count = sum(img.point(lambda pix: 1 if pix>=thresh else 0).getdata())
Just count the pixels before printing:
from PIL import Image
img = input("File name: ")
img = Image.open(img);
count = 0
for y in range(img.height):
for x in range(img.width):
pixel = img.getpixel((x, y))
if pixel >= 200:
count += 1
print(count,"pixels are bright.")
You can use the getcolors() function from PIL image, this function return a list of tuples with colors found in image and the amount of each one. I'm using the following function to return a dictionary with color as key, and counter as value.
from PIL import Image
def getcolordict(im):
w,h = im.size
colors = im.getcolors(w*h)
colordict = { x[1]:x[0] for x in colors }
return colordict
im = Image.open('image.jpg')
colordict = getcolordict(im)
# get the amount of black pixels in image
# in RGB black is 0,0,0
black_pixels_count = colordict.get((0,0,0))
# get the amount of white pixels in image
# in RGB white is 255,255,255
white_pixels_count = colordict.get((255,255,255))
You can try this one too (Python 3):
from PIL import Image
imgFile = input("File name: ")
img = Image.open(imgFile);
pixels = img.getdata()
total = len(list(filter(lambda i: i >= (200,200,200), pixels)))
print("There are %d bright pixels" % total)
You can use the getdata() method to take all pixels at once end then you can filter the ones which are above the desired value. In Python 2, you can simply write i >= 200.

Categories

Resources