Related
I am looking to combine 10 audio samples in various manners (format - wav probably, but this can be changed to any format as they will be pre-recorded).
from pydub import AudioSegment
sounds = []
sound1 = AudioSegment.from_wav("Dropbox/PIREAD/1.wav")
sound2 = AudioSegment.from_wav("Dropbox/PIREAD/2.wav")
sound3 = AudioSegment.from_wav("Dropbox/PIREAD/3.wav")
sound4 = AudioSegment.from_wav("Dropbox/PIREAD/4.wav")
sound5 = AudioSegment.from_wav("Dropbox/PIREAD/5.wav")
sound6 = AudioSegment.from_wav("Dropbox/PIREAD/6.wav")
sound7 = AudioSegment.from_wav("Dropbox/PIREAD/7.wav")
sound8 = AudioSegment.from_wav("Dropbox/PIREAD/8.wav")
sound9 = AudioSegment.from_wav("Dropbox/PIREAD/9.wav")
sound0 = AudioSegment.from_wav("Dropbox/PIREAD/0.wav")
sounds=[sound1,sound2,sound3,sound4,sound5,sound6,sound7,sound8,sound9,sound0]
combined_sounds = AudioSegment.empty()
for x in range(10):
for y in range(10):
combined_sounds += sounds[y]
combined_sounds.export("Dropbox/PIREAD/joinedFile.wav", format="wav")
This is literally me reading the numbers 0-9 and assembling them into one overall wav file.
It works - but it is slow once the loop is extended x=100, x=1000.
Q: How can I speed things up?
The actual order of the numbers will be read from a text$ - for example "354224848179261915075" which happens to be the 100th Fibonacci number.
Cheers
Glen
I believe it's slow because when you loop over x, you repeat operations (the loop over y) which could be computed before the loop over x, then assembled.
I looked into AudioSegment and found potentially useful method for you namely from_mono_audiosegments but it is limited to mono sounds and you will need to test if it is faster than += please compare time-wise these options, i.e.
import time
from pydub import AudioSegment
sounds = []
sound1 = AudioSegment.from_wav("Dropbox/PIREAD/1.wav")
sound2 = AudioSegment.from_wav("Dropbox/PIREAD/2.wav")
sound3 = AudioSegment.from_wav("Dropbox/PIREAD/3.wav")
sound4 = AudioSegment.from_wav("Dropbox/PIREAD/4.wav")
sound5 = AudioSegment.from_wav("Dropbox/PIREAD/5.wav")
sound6 = AudioSegment.from_wav("Dropbox/PIREAD/6.wav")
sound7 = AudioSegment.from_wav("Dropbox/PIREAD/7.wav")
sound8 = AudioSegment.from_wav("Dropbox/PIREAD/8.wav")
sound9 = AudioSegment.from_wav("Dropbox/PIREAD/9.wav")
sound0 = AudioSegment.from_wav("Dropbox/PIREAD/0.wav")
sounds=[sound1,sound2,sound3,sound4,sound5,sound6,sound7,sound8,sound9,sound0]
# option1 using +=
t1 = time.time()
combined_sounds1 = AudioSegment.empty()
for s in sounds
combined_sounds1 += s
t2 = time.time()
# end of option1
# option2 using from_mono_audiosegments
t3 = time.time()
combined_sounds2 = AudioSegment.from_mono_audiosegments(*sounds)
t4 = time.time()
# end of option2
print('option1 (seconds):',t2-t1)
print('option2 (seconds):',t4-t3)
Thanks for the suggestions and advice above. This is the final code I used and link to the resultant video (with ffmpeg visualisation):
# Program to display the Fibonacci sequence up to n-th term
from pydub import AudioSegment
combined_sounds = ""
sound1 = AudioSegment.from_wav("1_2.wav")
sound2 = AudioSegment.from_wav("2_2.wav")
sound3 = AudioSegment.from_wav("3_2.wav")
sound4 = AudioSegment.from_wav("4_2.wav")
sound5 = AudioSegment.from_wav("5_2.wav")
sound6 = AudioSegment.from_wav("6_2.wav")
sound7 = AudioSegment.from_wav("7_2.wav")
sound8 = AudioSegment.from_wav("8_2.wav")
sound9 = AudioSegment.from_wav("9_2.wav")
sound0 = AudioSegment.from_wav("0_2.wav")
nterms=1000
# first two terms
n1, n2 = 0, 1
count = 0
fib = ""
# check if the number of terms is valid
if nterms <= 0:
print("Please enter a positive integer")
# if there is only one term, return n1
elif nterms == 1:
print("Fibonacci sequence upto",nterms,":")
print(n1)
# generate fibonacci sequence
else:
print("Fibonacci sequence:")
while count < nterms:
#print(n1)
fib += str(n1)
nth = n1 + n2
# update values
n1 = n2
n2 = nth
count += 1
i=-36
j=0
fibs = [fib[i:i+1000] for i in range(0, len(fib), 1000)]
seg = 0
for a in fibs:
if seg == 2:
break
combined_sounds = AudioSegment.empty()
seg +=1
for x in a:
i,j = -36,0
s = eval("sound"+str(x))
s = s.apply_gain_stereo(i,j)
combined_sounds += s
i,j = j,i
combined_sounds.export("joinedFile"+str(seg)+".wav", format="wav")
This splits the output into 1000 digit wav files. The first 1000 Fibonacci terms produces nearly 15Gb of wavs!
Uploaded to YouTube: https://www.youtube.com/watch?v=U7Z_HOGqjlE
Thanks all.
I'm trying to run a python code for an awr1642BOOST board to make a short range radar. since i dont have an dca1000 evm data capture board I'm trying to obtain the detected data points using the detObj dictionary of the provided python code. On running the the code the 2D scatter window appears blank and remains unresponsive. Please can someone point out what i might be doing wrong. I have also attached a screen shot of the error window and the python shell.[enter image description here][1]
import time
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
# Change the configuration file name
configFileName = '1642config.cfg'
CLIport = {}
Dataport = {}
byteBuffer = np.zeros(2**15,dtype = 'uint8')
byteBufferLength = 0;
# ------------------------------------------------------------------
# Function to configure the serial ports and send the data from
# the configuration file to the radar
def serialConfig(configFileName):
global CLIport
global Dataport
# Open the serial ports for the configuration and the data ports
# Raspberry pi
#CLIport = serial.Serial('/dev/ttyACM0', 115200)
#Dataport = serial.Serial('/dev/ttyACM1', 921600)
# Windows
CLIport = serial.Serial('COM4', 115200)
Dataport = serial.Serial('COM5', 921600)
# Read the configuration file and send it to the board
config = [line.rstrip('\r\n') for line in open(configFileName)]
for i in config:
CLIport.write((i+'\n').encode())
print(i)
time.sleep(0.01)
return CLIport, Dataport
# ------------------------------------------------------------------
# Function to parse the data inside the configuration file
def parseConfigFile(configFileName):
configParameters = {} # Initialize an empty dictionary to store the configuration parameters
# Read the configuration file and send it to the board
config = [line.rstrip('\r\n') for line in open(configFileName)]
for i in config:
# Split the line
splitWords = i.split(" ")
# Hard code the number of antennas, change if other configuration is used
numRxAnt = 4
numTxAnt = 2
# Get the information about the profile configuration
if "profileCfg" in splitWords[0]:
startFreq = int(float(splitWords[2]))
idleTime = int(splitWords[3])
rampEndTime = float(splitWords[5])
freqSlopeConst = float(splitWords[8])
numAdcSamples = int(splitWords[10])
numAdcSamplesRoundTo2 = 1;
while numAdcSamples > numAdcSamplesRoundTo2:
numAdcSamplesRoundTo2 = numAdcSamplesRoundTo2 * 2;
digOutSampleRate = int(splitWords[11]);
# Get the information about the frame configuration
elif "frameCfg" in splitWords[0]:
chirpStartIdx = int(splitWords[1]);
chirpEndIdx = int(splitWords[2]);
numLoops = int(splitWords[3]);
numFrames = int(splitWords[4]);
framePeriodicity = int(splitWords[5]);
# Combine the read data to obtain the configuration parameters
numChirpsPerFrame = (chirpEndIdx - chirpStartIdx + 1) * numLoops
configParameters["numDopplerBins"] = numChirpsPerFrame / numTxAnt
configParameters["numRangeBins"] = numAdcSamplesRoundTo2
configParameters["rangeResolutionMeters"] = (3e8 * digOutSampleRate * 1e3) / (2 * freqSlopeConst * 1e12 * numAdcSamples)
configParameters["rangeIdxToMeters"] = (3e8 * digOutSampleRate * 1e3) / (2 * freqSlopeConst * 1e12 * configParameters["numRangeBins"])
configParameters["dopplerResolutionMps"] = 3e8 / (2 * startFreq * 1e9 * (idleTime + rampEndTime) * 1e-6 * configParameters["numDopplerBins"] * numTxAnt)
configParameters["maxRange"] = (300 * 0.9 * digOutSampleRate)/(2 * freqSlopeConst * 1e3)
configParameters["maxVelocity"] = 3e8 / (4 * startFreq * 1e9 * (idleTime + rampEndTime) * 1e-6 * numTxAnt)
return configParameters
# ------------------------------------------------------------------
# Funtion to read and parse the incoming data
def readAndParseData16xx(Dataport, configParameters):
global byteBuffer, byteBufferLength
# Constants
OBJ_STRUCT_SIZE_BYTES = 12;
BYTE_VEC_ACC_MAX_SIZE = 2**15;
MMWDEMO_UART_MSG_DETECTED_POINTS = 1;
MMWDEMO_UART_MSG_RANGE_PROFILE = 2;
maxBufferSize = 2**15;
magicWord = [2, 1, 4, 3, 6, 5, 8, 7]
# Initialize variables
magicOK = 0 # Checks if magic number has been read
dataOK = 0 # Checks if the data has been read correctly
frameNumber = 0
detObj = {}
tlv_type = 0
readBuffer = Dataport.read(Dataport.in_waiting)
byteVec = np.frombuffer(readBuffer, dtype = 'uint8')
byteCount = len(byteVec)
# Check that the buffer is not full, and then add the data to the buffer
if (byteBufferLength + byteCount) < maxBufferSize:
byteBuffer[byteBufferLength:byteBufferLength + byteCount] = byteVec[:byteCount]
byteBufferLength = byteBufferLength + byteCount
# Check that the buffer has some data
if byteBufferLength > 16:
# Check for all possible locations of the magic word
possibleLocs = np.where(byteBuffer == magicWord[0])[0]
# Confirm that is the beginning of the magic word and store the index in startIdx
startIdx = []
for loc in possibleLocs:
check = byteBuffer[loc:loc+8]
if np.all(check == magicWord):
startIdx.append(loc)
# Check that startIdx is not empty
if startIdx:
# Remove the data before the first start index
if startIdx[0] > 0 and startIdx[0] < byteBufferLength:
byteBuffer[:byteBufferLength-startIdx[0]] = byteBuffer[startIdx[0]:byteBufferLength]
byteBuffer[byteBufferLength-startIdx[0]:] = np.zeros(len(byteBuffer[byteBufferLength-startIdx[0]:]),dtype = 'uint8')
byteBufferLength = byteBufferLength - startIdx[0]
# Check that there have no errors with the byte buffer length
if byteBufferLength < 0:
byteBufferLength = 0
# word array to convert 4 bytes to a 32 bit number
word = [1, 2**8, 2**16, 2**24]
# Read the total packet length
totalPacketLen = np.matmul(byteBuffer[12:12+4],word)
# Check that all the packet has been read
if (byteBufferLength >= totalPacketLen) and (byteBufferLength != 0):
magicOK = 1
# If magicOK is equal to 1 then process the message
if magicOK:
# word array to convert 4 bytes to a 32 bit number
word = [1, 2**8, 2**16, 2**24]
# Initialize the pointer index
idX = 0
# Read the header
magicNumber = byteBuffer[idX:idX+8]
idX += 8
version = format(np.matmul(byteBuffer[idX:idX+4],word),'x')
idX += 4
totalPacketLen = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
platform = format(np.matmul(byteBuffer[idX:idX+4],word),'x')
idX += 4
frameNumber = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
timeCpuCycles = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
numDetectedObj = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
numTLVs = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
subFrameNumber = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
# Read the TLV messages
for tlvIdx in range(numTLVs):
# word array to convert 4 bytes to a 32 bit number
word = [1, 2**8, 2**16, 2**24]
# Check the header of the TLV message
try:
tlv_type = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
tlv_length = np.matmul(byteBuffer[idX:idX+4],word)
idX += 4
except:
pass
# Read the data depending on the TLV message
if tlv_type == MMWDEMO_UART_MSG_DETECTED_POINTS:
# word array to convert 4 bytes to a 16 bit number
word = [1, 2**8]
tlv_numObj = np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
tlv_xyzQFormat = 2**np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
# Initialize the arrays
rangeIdx = np.zeros(tlv_numObj,dtype = 'int16')
dopplerIdx = np.zeros(tlv_numObj,dtype = 'int16')
peakVal = np.zeros(tlv_numObj,dtype = 'int16')
x = np.zeros(tlv_numObj,dtype = 'int16')
y = np.zeros(tlv_numObj,dtype = 'int16')
z = np.zeros(tlv_numObj,dtype = 'int16')
for objectNum in range(tlv_numObj):
# Read the data for each object
rangeIdx[objectNum] = np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
dopplerIdx[objectNum] = np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
peakVal[objectNum] = np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
x[objectNum] = np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
y[objectNum] = np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
z[objectNum] = np.matmul(byteBuffer[idX:idX+2],word)
idX += 2
# Make the necessary corrections and calculate the rest of the data
rangeVal = rangeIdx * configParameters["rangeIdxToMeters"]
dopplerIdx[dopplerIdx > (configParameters["numDopplerBins"]/2 - 1)] = dopplerIdx[dopplerIdx > (configParameters["numDopplerBins"]/2 - 1)] - 65535
dopplerVal = dopplerIdx * configParameters["dopplerResolutionMps"]
#x[x > 32767] = x[x > 32767] - 65536
#y[y > 32767] = y[y > 32767] - 65536
#z[z > 32767] = z[z > 32767] - 65536
x = x / tlv_xyzQFormat
y = y / tlv_xyzQFormat
z = z / tlv_xyzQFormat
# Store the data in the detObj dictionary
detObj = {"numObj": tlv_numObj, "rangeIdx": rangeIdx, "range": rangeVal, "dopplerIdx": dopplerIdx, \
"doppler": dopplerVal, "peakVal": peakVal, "x": x, "y": y, "z": z}
dataOK = 1
# Remove already processed data
if idX > 0 and byteBufferLength > idX:
shiftSize = totalPacketLen
byteBuffer[:byteBufferLength - shiftSize] = byteBuffer[shiftSize:byteBufferLength]
byteBuffer[byteBufferLength - shiftSize:] = np.zeros(len(byteBuffer[byteBufferLength - shiftSize:]),dtype = 'uint8')
byteBufferLength = byteBufferLength - shiftSize
# Check that there are no errors with the buffer length
if byteBufferLength < 0:
byteBufferLength = 0
return dataOK, frameNumber, detObj
# ------------------------------------------------------------------
# Funtion to update the data and display in the plot
def update():
dataOk = 0
global detObj
x = []
y = []
# Read and parse the received data
dataOk, frameNumber, detObj = readAndParseData16xx(Dataport, configParameters)
if dataOk and len(detObj["x"])>0:
#print(detObj)
x = -detObj["x"]
y = detObj["y"]
s.setData(x,y)
QtGui.QApplication.processEvents()
return dataOk
# ------------------------- MAIN -----------------------------------------
# Configurate the serial port
CLIport, Dataport = serialConfig(configFileName)
# Get the configuration parameters from the configuration file
configParameters = parseConfigFile(configFileName)
# START QtAPPfor the plot
app = QtGui.QApplication([])
# Set the plot
pg.setConfigOption('background','w')
win = pg.GraphicsLayoutWidget(title="2D scatter plot")
p = win.addPlot()
p.setXRange(-0.5,0.5)
p.setYRange(0,1.5)
p.setLabel('left',text = 'Y position (m)')
p.setLabel('bottom', text= 'X position (m)')
s = p.plot([],[],pen=None,symbol='o')
win.show()
# Main loop
detObj = {}
frameData = {}
currentIndex = 0
while True:
try:
# Update the data and check if the data is okay
dataOk = update()
if dataOk:
# Store the current frame into frameData
frameData[currentIndex] = detObj
currentIndex += 1
time.sleep(0.03) # Sampling frequency of 30 Hz
# Stop the program and close everything if Ctrl + c is pressed
except KeyboardInterrupt:
CLIport.write(('sensorStop\n').encode())
CLIport.close()
Dataport.close()
win.close()
break```
[1]: https://i.stack.imgur.com/BYLnW.png
I am trying to perform DCT Steganography by converting the image to HSV and applying DCT and quantizing it and hiding the text message and stiching the image. In decoding process, applying DCT and quantizing it and extracting the text message from it. But, here I am getting incorrect answer in it. I am using HSV for getting the same image color similar to original image. I used saturation channel to hide the text in it. Now, I am stuck in it and not getting the correct answer. Please help me out in this.
Here is the code:
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 19 11:13:50 2021
#author: SM
"""
from PIL import Image
import numpy as np
import itertools
import types
import cv2
from Crypto.Cipher import AES
#creation of quantization matrix of quality factor as 50
quant = np.array([[16,11,10,16,24,40,51,61],
[12,12,14,19,26,58,60,55],
[14,13,16,24,40,57,69,56],
[14,17,22,29,51,87,80,62],
[18,22,37,56,68,109,103,77],
[24,35,55,64,81,104,113,92],
[49,64,78,87,103,121,120,101],
[72,92,95,98,112,100,103,99]])
class DiscreteCosineTransform:
#created the constructor
def __init__(self):
self.message = None
self.bitMessage = None
self.oriCol = 0
self.oriRow = 0
self.numBits = 0
#utility and helper function for DCT Based Steganography
#helper function to stich the image back together
def chunks(self,l,n):
m = int(n)
for i in range(0,len(l),m):
yield l[i:i+m]
#function to add padding to make the function dividable by 8x8 blocks
def addPadd(self,img,row,col):
img = cv2.resize(img,(col+(8-col%8),row+(8-row%8)))
return img
#function to transform the message that is wanted to be hidden from plaintext to a list of bits
def toBits(self):
bits = []
for char in self.message:
binval = bin(char)[2:].rjust(8,'0')
#print('bin '+binval)
bits.append(binval)
self.numBits = bin(len(bits))[2:].rjust(8,'0')
return bits
#main part
#encoding function
#applying dct for encoding
def DCTEncoder(self,img,secret):
self.message = str(len(secret)).encode()+b'*'+secret
self.bitMessage = self.toBits()
#get the size of the image in pixels
row, col = img.shape[:2]
self.oriRow = row
self.oriCol = col
if((col/8)*(row/8)<len(secret)):
print("Error: Message too large to encode in image")
return False
if(row%8!=0 or col%8!=0):
img = self.addPadd(img,row,col)
row,col = img.shape[:2]
#split image into RGB channels
hImg,sImg,vImg = cv2.split(img)
#message to be hid in blue channel so converted to type float32 for dct function
#print(bImg.shape)
sImg = np.float32(sImg)
#breaking the image into 8x8 blocks
imgBlocks = [np.round(sImg[j:j+8,i:i+8]-128) for (j,i) in itertools.product(range(0,row,8),range(0,col,8))]
#print(imgBlocks[0])
#blocks are run through dct / apply dct to it
dctBlocks = [np.round(cv2.dct(ib)) for ib in imgBlocks]
#print('DCT Blocks')
#print(dctBlocks[0])
#blocks are run through quantization table / obtaining quantized dct coefficients
quantDCT = [np.round(dbk/quant) for dbk in dctBlocks]
#print('Quant Blocks')
#print(quantDCT[0])
#set LSB in DC value corresponding bit of message
messIndex=0
letterIndex=0
print(self.bitMessage)
for qb in quantDCT:
#find LSB in DCT cofficient and replace it with message bit
#print(len(qb))
DC = qb[0][0]
#print(DC.shape)
DC = np.uint8(DC)
#print(DC)
DC = np.unpackbits(DC)
#print(DC[0])
#print(self.bitMessage[messIndex][letterIndex])
#print(DC[7])
#print(type(DC[7]))
#print(DC[7].shape)
#print(type(self.bitMessage))
#a=self.bitMessage[messIndex][letterIndex]
#print(a)
DC[7] = self.bitMessage[messIndex][letterIndex]
DC = np.packbits(DC)
DC = np.float32(DC)
DC = DC - 255
qb[0][0] = DC
letterIndex = letterIndex + 1
if (letterIndex == 8):
letterIndex = 0
messIndex = messIndex + 1
if (messIndex == len(self.message)):
break
#writing the stereo image
#blocks run inversely through quantization table
sImgBlocks = [quantizedBlock *quant+128 for quantizedBlock in quantDCT]
#blocks run through inverse DCT
#sImgBlocks = [cv2.idct(B)+128 for B in quantizedDCT]
#puts the new image back together
aImg=[]
for chunkRowBlocks in self.chunks(sImgBlocks, col/8):
for rowBlockNum in range(8):
for block in chunkRowBlocks:
aImg.extend(block[rowBlockNum])
print(len(aImg))
aImg = np.array(aImg).reshape(row, col)
#converted from type float32
aImg = np.uint8(aImg)
#show(sImg)
aImg = cv2.merge((hImg,aImg,vImg))
return aImg
#decoding
#apply dct for decoding
def DCTDecoder(self,img):
row, col = img.shape[:2]
messSize = None
messageBits = []
buff = 0
#split the image into RGB channels
hImg,sImg,vImg = cv2.split(img)
#message hid in blue channel so converted to type float32 for dct function
sImg = np.float32(sImg)
#break into 8x8 blocks
imgBlocks = [sImg[j:j+8,i:i+8]-128 for (j,i) in itertools.product(range(0,row,8),range(0,col,8))]
#dctBlocks = [np.round(cv2.dct(ib)) for ib in imgBlocks]
# the blocks are run through quantization table
quantDCT = [ib/quant for ib in imgBlocks]
i=0
flag = 0
nb = ''
#message is extracted from LSB of DCT coefficients
for qb in quantDCT:
DC = qb[0][0]
DC = np.uint8(DC)
#unpacking of bits of DCT
DC = np.unpackbits(DC)
#print('DC',DC,end=' ')
if (flag == 0):
if (DC[7] == 1):
buff+=(0 & 1) << (7-i)
elif (DC[7] == 0):
buff+=(1&1) << (7-i)
else:
if (DC[7] == 1):
nb+='0'
elif (DC[7] == 0):
nb+='1'
i=1+i
#print(i)
if (i == 8):
#print(buff,end=' ')
if (flag == 0):
messageBits.append(buff)
#print(buff,end=' ')
buff = 0
else:
messageBits.append(nb)
#print(nb,end=' ')
nb = ''
i =0
if (messageBits[-1] == 42 and messSize is None):
try:
flag = 1
messSize = int(str(chr(messageBits[0]))+str(chr(messageBits[1])))#int(''.join(messageBits[:-1]))
print(messSize,'a')
except:
print('b')
pass
if (len(messageBits) - len(str(messSize)) - 1 == messSize):
#print(''.join(messageBits)[len(str(messSize))+1:])
return messageBits
pass
print(messageBits)
return ''
def msg_encrypt(msg,cipher):
if (len(msg)%16 != 0):
#a = len(msg)%16 != 0
#print(a)
msg = msg + ' '*(16 - len(msg)%16)
#nonce = cipher.nonce
t1 = msg.encode()
enc_msg = cipher.encrypt(t1)
return enc_msg
def msg_decrypt(ctext,cipher):
dec_msg = cipher.decrypt(ctext)
msg1 = dec_msg.decode()
return msg1
image = cv2.imread('C://Users//hp//Desktop//Lenna.jpg',cv2.IMREAD_UNCHANGED)
image = cv2.cvtColor(image,cv2.COLOR_BGR2HSV_FULL)
#image = cv2.cvtColor(image,cv2.COLOR_RGB2HSV)
secret_msg = 'Shaina'
print(secret_msg)
key = b'Sixteen byte key'
#encryption of message
cipher = AES.new(key,AES.MODE_ECB)
enc_msg = msg_encrypt(secret_msg,cipher)
print(enc_msg)
d = DiscreteCosineTransform()
dct_img_encoded = d.DCTEncoder(image, enc_msg)
dct_img_encoded = cv2.cvtColor(dct_img_encoded,cv2.COLOR_HSV2BGR_FULL)
#dct_img_encoded = cv2.cvtColor(dct_img_encoded,cv2.COLOR_BGR2RGB)
cv2.imwrite('C://Users//hp//Desktop//DCT1.png',dct_img_encoded)
eimg = cv2.imread('C://Users//hp//Desktop//DCT1.png',cv2.IMREAD_UNCHANGED)
eimg = cv2.cvtColor(eimg,cv2.COLOR_BGR2HSV_FULL)
#eimg = cv2.cvtColor(eimg,cv2.COLOR_RGB2HSV)
text = d.DCTDecoder(eimg)
ntext = []
print(text)
for i in range(len(text)):
if(type(text[i]) == str):
ntext.append(text[i])
print(ntext)
#print(type(text))
#print(next)
#binary_data = ''.join([ format(ord(i), "08b") for i in next ])
#all_bytes = [ binary_data[i: i+8] for i in range(0,len(binary_data),8)]
decoded_data = b''
for byte in next:
try:
decoded_data += int (byte,2).to_bytes (len(byte) // 8, byteorder='big')
except Exception as e:
print(byte)
break
print(decoded_data)
#decryption of message
dtext = msg_decrypt(decoded_data,cipher)
print(dtext)
The result I am getting it as:
Please help me out with this.
OK, I've simplified a lot of things and made some changes, and this seems to work with my sample images.
The biggest overall problem you seem to be facing is that the RGB/HSV conversion screws up your least significant bits, thereby losing the embedded message. I'm not convinced manipulating the saturation is the right method. What I've done here is left it in RGB, and I'm manipulating the green band. I'm not doing the quantizing, because I don't think that's a correct method, but I am embedding the message in the bottom 5 bits of the 0th DCT element. That way, I can do some rounding during the decode to allow for a few bits of slop.
Maybe this can help you move forward.
from PIL import Image
import numpy as np
import itertools
import cv2
class DiscreteCosineTransform:
#created the constructor
def __init__(self):
self.message = None
self.numBits = 0
#utility and helper function for DCT Based Steganography
#helper function to stich the image back together
def chunks(self,l,n):
m = int(n)
for i in range(0,len(l),m):
yield l[i:i+m]
#function to add padding to make the function dividable by 8x8 blocks
def addPadd(self,img,row,col):
img = cv2.resize(img,(col+(8-col%8),row+(8-row%8)))
return img
#main part
#encoding function
#applying dct for encoding
def DCTEncoder(self,img,secret):
self.message = str(len(secret)).encode()+b'*'+secret
#get the size of the image in pixels
row, col = img.shape[:2]
if((col/8)*(row/8)<len(secret)):
print("Error: Message too large to encode in image")
return False
if row%8 or col%8:
img = self.addPadd(img,row,col)
row,col = img.shape[:2]
#split image into RGB channels
hImg,sImg,vImg = cv2.split(img)
#message to be hid in saturation channel so converted to type float32 for dct function
#print(bImg.shape)
sImg = np.float32(sImg)
#breaking the image into 8x8 blocks
imgBlocks = [np.round(sImg[j:j+8,i:i+8]-128) for (j,i) in itertools.product(range(0,row,8),range(0,col,8))]
#print('imgBlocks',imgBlocks[0])
#blocks are run through dct / apply dct to it
dctBlocks = [np.round(cv2.dct(ib)) for ib in imgBlocks]
print('imgBlocks', imgBlocks[0])
print('dctBlocks', dctBlocks[0])
#blocks are run through quantization table / obtaining quantized dct coefficients
quantDCT = dctBlocks
print('quantDCT', quantDCT[0])
#set LSB in DC value corresponding bit of message
messIndex=0
letterIndex=0
print(self.message)
for qb in quantDCT:
#find LSB in DCT cofficient and replace it with message bit
bit = (self.message[messIndex] >> (7-letterIndex)) & 1
DC = qb[0][0]
DC = (int(DC) & ~31) | (bit * 15)
qb[0][0] = np.float32(DC)
letterIndex += 1
if letterIndex == 8:
letterIndex = 0
messIndex += 1
if messIndex == len(self.message):
break
#writing the stereo image
#blocks run inversely through quantization table
#blocks run through inverse DCT
sImgBlocks = [cv2.idct(B)+128 for B in quantDCT]
#puts the new image back together
aImg=[]
for chunkRowBlocks in self.chunks(sImgBlocks, col/8):
for rowBlockNum in range(8):
for block in chunkRowBlocks:
aImg.extend(block[rowBlockNum])
aImg = np.array(aImg).reshape(row, col)
#converted from type float32
aImg = np.uint8(aImg)
#show(sImg)
return cv2.merge((hImg,aImg,vImg))
#decoding
#apply dct for decoding
def DCTDecoder(self,img):
row, col = img.shape[:2]
messSize = None
messageBits = []
buff = 0
#split the image into RGB channels
hImg,sImg,vImg = cv2.split(img)
#message hid in saturation channel so converted to type float32 for dct function
sImg = np.float32(sImg)
#break into 8x8 blocks
imgBlocks = [sImg[j:j+8,i:i+8]-128 for (j,i) in itertools.product(range(0,row,8),range(0,col,8))]
dctBlocks = [np.round(cv2.dct(ib)) for ib in imgBlocks]
# the blocks are run through quantization table
print('imgBlocks',imgBlocks[0])
print('dctBlocks',dctBlocks[0])
quantDCT = dctBlocks
i=0
flag = 0
#message is extracted from LSB of DCT coefficients
for qb in quantDCT:
if qb[0][0] > 0:
DC = int((qb[0][0]+7)/16) & 1
else:
DC = int((qb[0][0]-7)/16) & 1
#unpacking of bits of DCT
buff += DC << (7-i)
i += 1
#print(i)
if i == 8:
messageBits.append(buff)
#print(buff,end=' ')
buff = 0
i =0
if messageBits[-1] == 42 and not messSize:
try:
messSize = int(chr(messageBits[0])+chr(messageBits[1]))
print(messSize,'a')
except:
print('b')
if len(messageBits) - len(str(messSize)) - 1 == messSize:
return messageBits
print("msgbits", messageBits)
return None
image = cv2.imread('20210827_092821.jpg',cv2.IMREAD_UNCHANGED)
enc_msg = b'Shaina Sixteen byte key'
#print(enc_msg)
d = DiscreteCosineTransform()
dct_img_encoded = d.DCTEncoder(image, enc_msg)
cv2.imwrite('2021_encoded.png',dct_img_encoded)
eimg = cv2.imread('2021_encoded.png',cv2.IMREAD_UNCHANGED)
text = d.DCTDecoder(eimg)
print(text)
decoded = bytes(text[3:])
print(decoded)
I want to implement the hmac algorithm with SHA-1 by the definition from RFC 2104. The code is running but the results aren't the same as the test-vectors from RFC. I'm not sure if I'm loading the values correctly(String to Hex, or String to Bytes?).
As template I've used the pseudo-code from wikipedia
I'm not sure about the terms 'blocksize' and 'output size'. In the code from wikipedia the outputsize is one of the input values but never used.
This is my code so far:
First I'm setting up a hash-function, then I'm converting my input-strings (key and message) into hex values. Next step is to to look if key hast go get hashed or filled with zeros. Next I'm xor-ing the single chars from the key with those values (I don't know where they come from, but they're in every example without any comment). Last but not least I'm combining an inner string(I_key_pad + message) and hash it which results in an outer strings that im combining with the outer pad and hash it again.
import hashlib
from functools import reduce
def hmac(key, message, hashfunc):
hasher = hashlib.sha1
blocksize = 40
message = toHex(message) #is this right?
key = toHex(key)
#alternative: loading values as bytes
#message = bytes(message, 'utf-8')
#key = bytes(key, 'utf-8')
if len(key) > blocksize:
key = hasher(key)
else:
#key = key.ljust(blocksize, '0') #filling from right to left
#key = key.ljust(blocksize, b'\0') #same as above but for bytes
key = pad(key, blocksize) #filling from left to right
val1 = 0x5c
val2 = 0x36
i = 0
o_key_pad = ""
i_key_pad = ""
while i < blocksize:
o_key_pad += str(ord(key[i]) ^ val1)
i_key_pad += str(ord(key[i]) ^ val2)
i += 1
tmp_string = str(i_key_pad) + str(message)
tmp_string = tmp_string.encode()
inner_hash = hasher(tmp_string).hexdigest()
fullstring = str(o_key_pad) + inner_hash
fullstring = fullstring.encode()
fullstring = hasher(fullstring).hexdigest()
print(fullstring)
def pad(key, blocksize):
key = str(key)
while len(key) < blocksize:
key = '0' + key
key = key
return key
def toHex(s):
lst = []
for ch in s:
hv = hex(ord(ch)).replace('0x', '')
if len(hv) == 1:
hv = '0' + hv
lst.append(hv)
return reduce(lambda x, y: x + y, lst)
def main():
while (1):
key = input("key = ")
message = input("message = ")
hash = input("hash (0: SHA-256, 1: SHA-1) = ")
hmac(key, message, hash)
if __name__ == "__main__":
main()
I'm not understanding all the steps in your code, but here's a short example showing HMAC-SHA1 using only hashlib.sha1, with a helper function xor.
import hashlib
def xor(x, y):
return bytes(x[i] ^ y[i] for i in range(min(len(x), len(y))))
def hmac_sha1(key_K, data):
if len(key_K) > 64:
raise ValueError('The key must be <= 64 bytes in length')
padded_K = key_K + b'\x00' * (64 - len(key_K))
ipad = b'\x36' * 64
opad = b'\x5c' * 64
h_inner = hashlib.sha1(xor(padded_K, ipad))
h_inner.update(data)
h_outer = hashlib.sha1(xor(padded_K, opad))
h_outer.update(h_inner.digest())
return h_outer.digest()
def do_tests():
# test 1
k = b'\x0b' * 20
data = b"Hi There"
result = hmac_sha1(k, data)
print(result.hex())
# add tests as desired
I am writing several functions, the first one inserts a greyscale bitmap image into another colour bitmap image. Now my aim is basically to take each digit of the greyscale pixel image (eg. 123) and replace the end digit of every RGB pixel (244, 244, 244), so it would basically end up like this (241, 242, 243). Essentially this is watermarking the colour image with the greyscale image.
The following code is what I have so far, i'm able to return the tuple values in a list, i just do not know how to manipulate a space the size of a smaller greyscale image in a larger image.
def add_watermark():
image = Image.open()
pixels = list(image.getdata())
image.putdata()
image.save()
for i in range(img.size[0]):
for j in range(img.size[1]):
pixels[i,j] = (i, j, 100)
Can anyone offer some advice?
You're on the right track. That's how you manipulate pixels, though you can do it a little faster using pixel access objects like I've shown below.
It's all pretty straightforward except for extracting and setting the correct digits. In this example, I've done that by dividing by powers of 10 and by using the modulo operator, though there are other ways. Hopefully the comments explain it well enough.
from PIL import Image
def add_watermark(watermark_path, image_in_path, image_out_path):
# Load watermark and image and check sizes and modes
watermark = Image.open(watermark_path)
assert watermark.mode == 'L'
image = Image.open(image_in_path)
assert image.mode == 'RGB'
assert watermark.size == image.size
# Get pixel access objects
watermark_pixels = watermark.load()
image_pixels = image.load()
# Watermark each pixel
for x in range(image.size[0]):
for y in xrange(image.size[1]):
# Get the tuple of rgb values and convert to a list (mutable)
rgb = list(image_pixels[x, y])
for i, p in enumerate(rgb):
# Divide the watermark pixel by 100 (r), then 10 (g), then 1 (b)
# Then take it modulo 10 to get the last digit
watermark_digit = (watermark_pixels[x, y] / (10 ** (2 - i))) % 10
# Divide and multiply value by 10 to zero the last digit
# Then add the watermark digit
rgb[i] = (p / 10) * 10 + watermark_digit
# Convert back to a tuple and store in the image
image_pixels[x, y] = tuple(rgb)
# Save the image
image.save(image_out_path)
If you are interested in watermarking images, you might want to take a look at steganography. As an example, Digital_Sight is a working demonstration of the concept and could be used as a basis for storing text used as a watermark. To study how modifying various pixel-bits in an image can alter its quality, you might want to play around with Color_Disruptor before deciding what data to overwrite.
Digital_Sight
import cStringIO
from PIL import Image
import bz2
import math
################################################################################
PIXELS_PER_BLOCK = 4
BYTES_PER_BLOCK = 3
MAX_DATA_BYTES = 16777215
################################################################################
class ByteWriter:
"ByteWriter(image) -> ByteWriter instance"
def __init__(self, image):
"Initalize the ByteWriter's internal variables."
self.__width, self.__height = image.size
self.__space = bytes_in_image(image)
self.__pixels = image.load()
def write(self, text):
"Compress and write the text to the image's pixels."
data = bz2.compress(text)
compressed_size = len(data)
if compressed_size > self.__space:
raise MemoryError('There is not enough space for the data!')
size_data = self.__encode_size(compressed_size)
tail = '\0' * ((3 - compressed_size) % 3)
buffer = size_data + data + tail
self.__write_buffer(buffer)
#staticmethod
def __encode_size(number):
"Convert number into a 3-byte block for writing."
data = ''
for _ in range(3):
number, lower = divmod(number, 256)
data = chr(lower) + data
return data
def __write_buffer(self, buffer):
"Write the buffer to the image in blocks."
addr_iter = self.__make_addr_iter()
data_iter = self.__make_data_iter(buffer)
for trio in data_iter:
self.__write_trio(trio, addr_iter.next())
def __make_addr_iter(self):
"Iterate over addresses of pixels to write to."
addr_group = []
for x in range(self.__width):
for y in range(self.__height):
addr_group.append((x, y))
if len(addr_group) == 4:
yield tuple(addr_group)
addr_group = []
#staticmethod
def __make_data_iter(buffer):
"Iterate over the buffer a block at a time."
if len(buffer) % 3 != 0:
raise ValueError('Buffer has a bad size!')
data = ''
for char in buffer:
data += char
if len(data) == 3:
yield data
data = ''
def __write_trio(self, trio, addrs):
"Write a 3-byte block to the pixels addresses given."
duo_iter = self.__make_duo_iter(trio)
tri_iter = self.__make_tri_iter(duo_iter)
for (r_duo, g_duo, b_duo), addr in zip(tri_iter, addrs):
r, g, b, a = self.__pixels[addr]
r = self.__set_two_bits(r, r_duo)
g = self.__set_two_bits(g, g_duo)
b = self.__set_two_bits(b, b_duo)
self.__pixels[addr] = r, g, b, a
#staticmethod
def __make_duo_iter(trio):
"Iterate over 2-bits that need to be written."
for char in trio:
byte = ord(char)
duos = []
for _ in range(4):
byte, duo = divmod(byte, 4)
duos.append(duo)
for duo in reversed(duos):
yield duo
#staticmethod
def __make_tri_iter(duo_iter):
"Group bits into their pixel units for writing."
group = []
for duo in duo_iter:
group.append(duo)
if len(group) == 3:
yield tuple(group)
group = []
#staticmethod
def __set_two_bits(byte, duo):
"Write a duo (2-bit) group to a pixel channel (RGB)."
if duo > 3:
raise ValueError('Duo bits has to high of a value!')
byte &= 252
byte |= duo
return byte
################################################################################
class ByteReader:
"ByteReader(image) -> ByteReader instance"
def __init__(self, image):
"Initalize the ByteReader's internal variables."
self.__width, self.__height = image.size
self.__pixels = image.load()
def read(self):
"Read data out of a picture, decompress the data, and return it."
compressed_data = ''
addr_iter = self.__make_addr_iter()
size_block = self.__read_blocks(addr_iter)
size_value = self.__block_to_number(size_block)
blocks_to_read = math.ceil(size_value / 3.0)
for _ in range(blocks_to_read):
compressed_data += self.__read_blocks(addr_iter)
if len(compressed_data) != blocks_to_read * 3:
raise ValueError('Blocks were not read correctly!')
if len(compressed_data) > size_value:
compressed_data = compressed_data[:size_value]
return bz2.decompress(compressed_data)
def __make_addr_iter(self):
"Iterate over the pixel addresses in the image."
addr_group = []
for x in range(self.__width):
for y in range(self.__height):
addr_group.append((x, y))
if len(addr_group) == 4:
yield tuple(addr_group)
addr_group = []
def __read_blocks(self, addr_iter):
"Read data a block at a time (4 pixels for 3 bytes)."
pixels = []
for addr in addr_iter.next():
pixels.append(self.__pixels[addr])
duos = self.__get_pixel_duos(pixels)
data = ''
buffer = []
for duo in duos:
buffer.append(duo)
if len(buffer) == 4:
value = 0
for duo in buffer:
value <<= 2
value |= duo
data += chr(value)
buffer = []
if len(data) != 3:
raise ValueError('Data was not decoded properly!')
return data
#classmethod
def __get_pixel_duos(cls, pixels):
"Extract bits from a given group of pixels."
duos = []
for pixel in pixels:
duos.extend(cls.__extract_duos(pixel))
return duos
#staticmethod
def __extract_duos(pixel):
"Retrieve the bits stored in a pixel."
r, g, b, a = pixel
return r & 3, g & 3, b & 3
#staticmethod
def __block_to_number(block):
"Convert a block into a number (size of data buffer)."
value = 0
for char in block:
value <<= 8
value |= ord(char)
return value
################################################################################
def main(picture, mode, text):
"Dispatch the various operations that can be requested."
image = Image.open(picture)
if image.mode != 'RGBA':
image = image.convert('RGBA')
if mode == 'Evaluate':
evaluate(image)
elif mode == 'Simulate':
simulate(image, text)
elif mode == 'Encode':
encode(image, text)
elif mode == 'Decode':
decode(image)
else:
raise ValueError('Mode %r was not recognized!' % mode)
################################################################################
def evaluate(image):
"Display the number of bytes available in the image."
print 'Usable bytes available =', bytes_in_image(image)
def bytes_in_image(image):
"Calculate the number of usable bytes in an image."
blocks = blocks_in_image(image)
usable_blocks = blocks - 1
usable_bytes = usable_blocks * BYTES_PER_BLOCK
return min(usable_bytes, MAX_DATA_BYTES)
def blocks_in_image(image):
"Find out how many blocks are in an image."
width, height = image.size
pixels = width * height
blocks = pixels / PIXELS_PER_BLOCK
return blocks
################################################################################
def simulate(image, text):
"Find out how much space the text takes in the image that was given."
compressed_data = bz2.compress(text)
compressed_size = len(compressed_data)
usable_bytes = bytes_in_image(image)
space_leftover = usable_bytes - compressed_size
if space_leftover > 0:
print 'You still have %s more bytes for storage.' % space_leftover
elif space_leftover < 0:
print 'You overfilled the image by %s bytes.' % -space_leftover
else:
print 'This is a perfect fit!'
################################################################################
def encode(image, text):
"Encodes text in image and returns picture to the browser."
mutator = ByteWriter(image)
mutator.write(text)
output = cStringIO.StringIO()
image.save(output, 'PNG')
output.seek(0)
print 'Content-Type: image/PNG'
print output.read()
################################################################################
def decode(image):
"Extract the original message and deliver it to the client."
accessor = ByteReader(image)
buffer = accessor.read()
print buffer
################################################################################
if __name__ == '__builtin__':
try:
main(cStringIO.StringIO(PICTURE), MODE, TEXT)
except SystemExit:
pass
Color_Disruptor
from cStringIO import StringIO
from PIL import Image
from random import randrange
def main(data, r_bits, g_bits, b_bits, a_bits):
image = Image.open(data)
if image.mode != 'RGBA':
image = image.convert('RGBA')
width, height = image.size
array = image.load()
data.close()
for x in range(width):
for y in range(height):
r, g, b, a = array[x, y]
r ^= randrange(r_bits)
g ^= randrange(g_bits)
b ^= randrange(b_bits)
a ^= randrange(a_bits)
array[x, y] = r, g, b, a
data = StringIO()
image.save(data, 'PNG')
print 'Content-Type: image/PNG'
print data.getvalue()
if __name__ == '__builtin__':
main(StringIO(DATA), *map(lambda bits: 1 << int(bits), (R, G, B, A)))