Imageio or OpenCv saves black blank image after altering image array - python

Okay, I am trying to do sort of simple image encryption for my college project, all I need to do is open an image as a numpy array and increment the pixel values by an integer key, then save it as 16 bit image, then open that Image and. But whenever I increment the pixels the result turns into a deformed image of that only has shades of black, and when I try to get the original image by performing the reverse (decremented the pixels with the same integer key), it results in a full black blank image. The dimensions are correct but all the data seems to be lost(though when I print the matrices the values seem to be correct).
I have tried using Imageio with freeimage plugin, and open cv but nothing seems to work. I am also a noob so I don't know if I am missing something else
from tkinter import filedialog
from tkinter import *
from PIL import Image
import cv2 as cv
import os
import numpy as np
def encrypt(k):
iload = filedialog.askopenfilename(parent=Main,initialdir=os.getcwd(),title="Please select a file:",filetypes = (("PNG files","*.png"),("jpeg files","*.jpg"),("all files","*.*")))
im= cv.imread(iload,cv.IMREAD_UNCHANGED)
im = im.astype(np.uint16)
print("After Open File Type : ",im.dtype)
print("Orinigal Image : ",im)
im = im.tolist()
for l in range(len(im)):
for j in range(len(im[l])):
for i in range(len(im[l][j])):
im[l][j][i]+=k
#im.putdata(npxls)
im=np.array(im).astype(np.uint16)
#imen.show()
print("Encrypted Image : ",im)
#imageio.imwrite("encrypted.png",im,format='PNG-FI')
cv.imwrite("encrypted.png",im)
img=cv.imread("encrypted.png",cv.IMREAD_UNCHANGED | cv.IMREAD_ANYCOLOR | cv.IMREAD_ANYDEPTH)
print("After Encrypting Saved File Type :",img.dtype)
def decrypt(k):
iload = filedialog.askopenfilename(parent=Main,initialdir=os.getcwd(),title="Please select a file:",filetypes = (("PNG files","*.png"),("jpeg files","*.jpg"),("all files","*.*")))
im=cv.imread(iload, cv.IMREAD_UNCHANGED | cv.IMREAD_ANYCOLOR | cv.IMREAD_ANYDEPTH)
print("Original Image : ",im)
print("After Decrypting Image Type: ",im.dtype)
im = im.astype(np.uint16)
im = im.tolist()
for l in range(len(im)):
for j in range(len(im[l])):
for i in range(len(im[l][j])):
im[l][j][i]-=k
im=np.array(im).astype(np.uint16)
#imen.show()
print("Decrypted Image : ",im)
cv.imwrite("decrypted.png",im,[CV_LOAD_IMAGE_ANYDEPTH ])
img=cv.imread("decrypted.png", cv.IMREAD_ANYCOLOR | cv.IMREAD_ANYDEPTH)
print("After Decrypting Saved Image type: ",img.dtype)
I just need the Image to get changed by adding a key to the pixel values in way that I can change it back with the reverse operation.

Related

Interpret bytes in a string as RGB in Python

I was wondering if this was possible.
I'm currently drafting a simple project that would transform my text files into images by using the values of the characters to determine the RGB values of the outputted image.
I know it sounds counterintuitive and no, I don't want to print a string into an image file, I want the text itself to determine the RGB values of each pixel. This is just a rough idea and is far from refined.
I just want a simple program that will work as a proof of concept.
Code so far:
#first contact
from ctypes import sizeof
from PIL import Image
import math as m
def test():
f='qran.txt'
file = open(f)
text = file.read()
file.close() # this is dumb, should just read from file instead of dumping it into a
text = list(text) #rudimentary fix, turn text into list so we can manage the characters
size = m.floor(m.sqrt(len(text)//3)) #round the value for a square image
print(size)
# for elem in text:
# print(ord(elem))
img = Image.new('RGB', (size,size))
pixels = img.load() # create the pixel map
c = 0
for i in range(img.size[0]): # for every col:
for j in range(img.size[1]): # For every row
pixels[i,j] = (ord(text[c]), ord(text[c+1]), ord(text[c+2])) # set the colour accordingly
c+=1
c+=1
img.show()
img.save('qran.png')
test()
As you can see right now my idea is working as a rough concept. You can copy the quran in plaintext and paste it in the same folder as this simple py program to see this output
The image comes out as dull, since characters are converted into integers and their values are too high, and so most colors come off as light-dark gray.
Are there some libraries that could help with exaggerating the values so that they would come off as more representative? I've thought of multiplying by 10 and truncating the result of inverting the values then applying some filters.
I know its pretty much trial and error by this point (as well as polishing the actual code to provide usable functions that allow tweaking images without editing the function over and over again) but I'd like some outside input from people that have dwelved into image processing and such in python.
I apologize in advance if this post was too wordy or contained some unnecessary tidbits, it's my first post in this community.
Just implementing Christoph's idea in the comments:
#!/usr/bin/env python3
from PIL import Image
import math as m
import pathlib
import numpy as np
# Load document as bytes
qran = pathlib.Path('qran.txt').read_bytes()
size = m.floor(m.sqrt(len(qran))) #round the value for a square image
# Make palette image from bytes
img = Image.frombuffer('P', (size,size), qran, "raw", 'P', 0, 1)
# Add random palette of 256 RGB triplets to image
palette = np.random.randint(0,256, 768, np.uint8)
img.putpalette(palette)
img.save('qran.png')

why is every pixel of an image are equal to 255?

I am experimenting with PIL and trying to analyze the image I attached.
Since my goal is to eventually be able to recognize it via neural networks,
I expect all pixels to have different intensities and therefore different values
ranging from 0 to 255. I am not sure why, every single pixel of this image is equal to
255. How so? What exactly am I doing wrong?
import numpy as np
import pandas as pd
import PIL
from PIL import Image
img = Image.open(r'key_1.jpg')
print(img.format)
print(img.size)
print(img.mode)
img # displays the image
img_sequence = img.getdata()
img_array = np.array(img_sequence)
print((img_array)) # all pixels = 255
Actually, they are not. You can see the top and the last few rows only where it shows 255 because the pixels there are white. If you try reading the same array using PIL/OpenCV, the result will show properly.
However, you can see all the results of the NumPy array using this method-
img_sequence = img.getdata()
img_array = np.array(img_sequence)
np.set_printoptions(threshold=np.inf)
print(img_array)
Sample screenshot of a random part of the output I got for the same image -

Python tesseract cannot read numbers from image

I have a python script that works for some images with numbers, it reads them correctly.
The type of images that work are here :Working image
I'm trying to use the script with a new kind of images with numbers only but it is not working. The new images type is here:Non working image
My script is as following:
try:
from PIL import Image
from PIL import ImageEnhance
except ImportError:
import Image
import pytesseract
black = (0,0,0)
white = (255,255,255)
threshold = (160,160,160)
# Open input image in grayscale mode and get its pixels.
img = Image.open("./in/web_search.jpg").convert("LA")
# multiply each pixel by 1.2
out = img.point(lambda i: i * 1.3)
enh = ImageEnhance.Contrast(out)
enh.enhance(1.3).show("30% more contrast")
pixels = out.getdata()
newPixels = []
# Compare each pixel
for pixel in pixels:
if pixel < threshold:
newPixels.append(black)
else:
newPixels.append(white)
# Create and save new image.
newImg = Image.new("RGB",out.size)
newImg.putdata(newPixels)
newImg.save("./out/web_search.jpg")
pytesseract.pytesseract.tesseract_cmd = r'/usr/bin/tesseract'
print("-----------------------")
print(pytesseract.image_to_string(Image.open('./out/web_search.jpg'), lang='eng', config='--psm 10 --oem 3 -c tessedit_char_whitelist=1234567890 --tessdata-dir="/usr/share/tesseract-ocr/4.00/tessdata/"'))
print("-----------------------")
The result with my new image is:
-----------------------
Riemer gaat bee 6 eee
-----------------------
Any help please?
Thanks.
You'll probably need to do some work to get it to pick that up. Some things you can do are:
Tesseract allows you to limit the character range which may be used. Set it to numbers only.
Use some form of preprocessing to remove the noise. Either Python Pillow noise removal function, or using morphological opening/closing.
Perform fine tuning training on the network.

Python add noise to image breaks PNG

I'm trying to create a image system in Python 3 to be used in a web app. The idea is to load an image from disk and add some random noise to it. When I try this, I get what looks like a totally random image, not resembling the original:
import cv2
import numpy as np
from skimage.util import random_noise
from random import randint
from pathlib import Path
from PIL import Image
import io
image_files = [
{
'name': 'test1',
'file': 'test1.png'
},
{
'name': 'test2',
'file': 'test2.png'
}
]
def gen_image():
rand_image = randint(0, len(image_files)-1)
image_file = image_files[rand_image]['file']
image_name = image_files[rand_image]['name']
image_path = str(Path().absolute())+'/img/'+image_file
img = cv2.imread(image_path)
noise_img = random_noise(img, mode='s&p', amount=0.1)
img = Image.fromarray(noise_img, 'RGB')
fp = io.BytesIO()
img.save(fp, format="PNG")
content = fp.getvalue()
return content
gen_image()
I have also tried using pypng:
import png
# Added the following to gen_image()
content = png.from_array(noise_img, mode='L;1')
content.save('image.png')
How can I load a png (With alpha transparency) from disk, add some noise to it, and return it so that it can be displayed by web server code (flask, aiohttp, etc).
As indicated in the answer by makayla, this makes it better: noise_img = (noise_img*255).astype(np.uint8) but the colors are still wrong and there's no transparency.
Here's the updated function for that:
def gen_image():
rand_image = randint(0, len(image_files)-1)
image_file = image_files[rand_image]['file']
image_name = image_files[rand_image]['name']
image_path = str(Path().absolute())+'/img/'+image_file
img = cv2.imread(image_path)
cv2.imshow('dst_rt', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Problem exists somewhere below this line.
img = random_noise(img, mode='s&p', amount=0.1)
img = (img*255).astype(np.uint8)
img = Image.fromarray(img, 'RGB')
fp = io.BytesIO()
img.save(fp, format="png")
content = fp.getvalue()
return content
This will popup a pre-noise image and return the noised image. RGB (And alpha) problem exists in returned image.
I think the problem is it needs to be RGBA but when I change to that, I get ValueError: buffer is not large enough
Given all the new information I am updating my answer with a few more tips for debugging the issue.
I found a site here which creates sample transparent images. I created a 64x64 cyan (R=0, G=255, B=255) image with a transparency layer of 0.5. I used this to test your code.
I read in the image two ways to compare: im1 = cv2.imread(fileName) and im2 = cv2.imread(fileName,cv2.IMREAD_UNCHANGED). np.shape(im1) returned (64,64,3) and np.shape(im2) returned (64,64,4). This is why that flag is required--the default imread settings in opencv will read in a transparent image as a normal RGB image.
However opencv reads in as BGR instead of RGB, and since you cannot save out with opencv, you'll need to convert it to the correct order otherwise the image will have reversed color. For example, my cyan image, when viewed with the reversed color appears like this:
You can change this using openCV's color conversion function like this im = cv2.cvtColor(im, cv2.COLOR_BGRA2RGBA) (Here is a list of all the color conversion codes). Again, double check the size of your image if you need to, it should still have four channels since you converted it to RGBA.
You can now add your noise to your image. Just so you know, this is also going to add noise to your alpha channel as well, randomly making some pixels more transparent and others less transparent. The random_noise function from skimage converts your image to float and returns it as float. This means the image values, normally integers ranging from 0 to 255, are converted to decimal values from 0 to 1. Your line img = Image.fromarray(noise_img, 'RGB') does not know what to do with the floating point noise_img. That's why the image is all messed up when you save it, as well as when I tried to show it.
So I took my cyan image, added noise, and then converted the floats back to 8 bits.
noise_img = random_noise(im, mode='s&p', amount=0.1)
noise_img = (noise_img*255).astype(np.uint8)
img = Image.fromarray(noise_img, 'RGBA')
It now looks like this (screenshot) using img.show():
I used the PIL library to save out my image instead of openCV so it's as close to your code as possible.
fp = 'saved_im.png'
img.save(fp, format="png")
I loaded the image into powerpoint to double-check that it preserved the transparency when I saved it using this method. Here is a screenshot of the saved image overlaid on a red circle in powerpoint:

I am trying to convert a numpy array to PIL.Image. But it is giving black images. The numpy array is returned by pyramid_gaussian method from skimage

Here is my code
import numpy as np
from PIL import Image
from skimage.transform import pyramid_gaussian
image = Image.open('/home/sumith/Downloads/AFW/testimages/3854178896.jpg')
rows, cols, dim = np.asarray(image).shape
pyramid = tuple(pyramid_gaussian(image, downscale=2,))
count = 0
for pyr in pyramid[0:8]:
row, col, dim = (np.asarray(pyr).shape)
count += 1
#io.imsave('/home/sumith/imagepyramids/'+count.__str__()+".jpg", pyr)
print(type(pyr))
image = Image.fromarray(pyr.astype('uint8'), 'RGB')
image.save('/home/sumith/imagepyramids/'+count.__str__()+".jpg")
when I print the type(pyr) it is showing as but when I try to convert and save it from PIL image all the images in the pyramid will be black. but if i save it using io.imsave it is working fine .I need the image pyramid's image to be fed into a neural network so if i can feed it as PIL.Image then it will be very helpful.
Thanks in advance!! The black image looks like this black-image
Check the values in pyr. Maybe they are values between 0 and `, while your RGB image is expected to be with values between 0 and 255.

Categories