In my project I need to open the SEQ file, so I use FileInputStream, it requires to load data to byte array. But because of that each pixels get wrong value (cause they are Integers).
Below in my code you can see that i put pixels in 2d array and for that I count each value of pixel, in line:
wart =(int) (buf[offset]) +(int)(buf[offset+1]) * 255;
I know that values because of byte input format are wrong (first two pixels aka double should be 152,109692453756 and 152,068644316116 but in my Java function they get -2474, -690)
I tried using the mask:
wart =(int) (buf[offset]<< 8) & 0x0000ff00 +(int)(buf[offset+1])& 0x000000ff * 255 ;
it helps a little (values arent negative, but they are "shifted" too much (first two pixels 19456, 18944)
I don't know how to solve this problem. I know that the mask should be different, but I don't know how to set it.
public class Sekwencja2 {
#SuppressWarnings("empty-statement")
public double[] sekwencja2(String nazwa,int nr_klatki) throws FileNotFoundException, IOException{
InputStream is = null;
DataInputStream dis = null;
is = new FileInputStream(nazwa);
dis = new DataInputStream(is);
int length = dis.available();
byte[] buf = new byte[length];
dis.readFully(buf);
int l_klatek = ((length-158864)/158864)+1;
int width = 320;
int height = 240;
int C1=21764040;
double C2=3033.3;
double C3=134.06;
int z = 0;
double[] oneDArray = new double[width*height];
double [][] pixels = new double[width][height];
int offset =0;
char type;
String typeText;
type=(char)buf[0];
typeText =Character.toString(type);
switch (typeText) {
case "A":
if(nr_klatki == 1)
offset= 696;
else
offset = 158136+(nr_klatki-1)*569+(nr_klatki-2)*(320*240*2+3839);
break;
case "F":
offset=(nr_klatki-1)*158864 + 1373;
break;
}
int wart = 0 ;
for(int x = 0; x<320; x++){
for (int y = 0; y<240;y++){
switch (typeText){
case "A":
if(nr_klatki==1)
wart =(int) (buf[offset]) +(int)(buf[offset+1]) * 255;
else
wart = (int)(buf[offset]<< 8)& 0x0000ff00 +(int)(buf[offset+1])&0xff*255 ;
break;
case "F":
wart = (buf[offset]<< 8)& 0x0000ff00 +(buf[offset+1])& 0x000000ff * 255 ;
break;
}
System.out.print(", "+wart);
pixels[x][y]=wart;
offset = offset+2;
}
}
for(int i = 0; i < width; i ++)
{
System.arraycopy(pixels[i], 0, oneDArray, i * height, height);
}
return oneDArray;
}
}
I know it's a mess, a lot of things are commented :)
255 is wrong
it's 256, you always have to multiply by a multiple of the base you are operating, 255 isn't a multiple of 2
analogy:
convert 111 from base 10 to base 10 in your way
1*99 + 1*9 + 1 = 109
so 109 != 111 which is wrong, likewise multiplying by 255 will alter any number you try to convert from binary to binary.
Mask first, like this:
wart = (buf[offset] & 0xFF) | ((buf[offset+1] & 0xFF) << 8);
Related
I have read two posts about extracting samples from AudioInputStream and converting them in to dB.
https://stackoverflow.com/a/26576548/8428414
https://stackoverflow.com/a/26824664/8428414
As far as I understand byte[] bytes; has structure like this:
Index 0: Sample 0 (Left Channel)
Index 1: Sample 0 (Right Channel)
Index 2: Sample 1 (Left Channel)
Index 3: Sample 1 (Right Channel)
Index 4: Sample 2 (Left Channel)
Index 5: Sample 2 (Right Channel)
In the first article it shows how to get samples from one channel (mono).
So, my problem is that I want to get samples separately for the right channel and separately for the left channel in order to calculate dB for right and left channels.
Here is the code. How it can be changed to get right and left channels separately?
I can't understand how the index i changes...
final byte[] buffer = new byte[2048];
float[] samples = new float[buffer.length / 2];
for (int n = 0; n != -1; n = in.read(buffer, 0, buffer.length)) {
line.write(buffer, 0, n);
for (int i = 0, sampleIndex = 0; i < n; ) {
int sample = 0;
sample |= buffer[i++] & 0xFF; // (reverse these two lines
sample |= buffer[i++] << 8; // if the format is big endian)
// normalize to range of +/-1.0f
samples[sampleIndex++] = sample / 32768f;
}
float rms = 0f;
for (float sample : samples) {
rms += sample * sample;
}
rms = (float) Math.sqrt(rms / samples.length);
Hope you could help me. Thank you in advance.
The format the stereo signal is saved in is called interleaved. I.e., as you described correctly, it's LLRRLLRRLLRR.... SO you first need to read a left sample, then a right sample, and so on.
I have edited your code to reflect this. However, there is some room for improvement via refactoring.
Note: The code changes only deal with interleaving. I have not checked the rest of your code.
final byte[] buffer = new byte[2048];
// create two buffers. One for the left, one for the right channel.
float[] leftSamples = new float[buffer.length / 4];
float[] rightSamples = new float[buffer.length / 4];
for (int n = 0; n != -1; n = in.read(buffer, 0, buffer.length)) {
line.write(buffer, 0, n);
for (int i = 0, sampleIndex = 0; i < n; ) {
int sample = 0;
leftSample |= buffer[i++] & 0xFF; // (reverse these two lines
leftSample |= buffer[i++] << 8; // if the format is big endian)
rightSample |= buffer[i++] & 0xFF; // (reverse these two lines
rightSample |= buffer[i++] << 8; // if the format is big endian)
// normalize to range of +/-1.0f
leftSamples[sampleIndex] = leftSample / 32768f;
rightSamples[sampleIndex] = rightSample / 32768f;
sampleIndex++;
}
// now compute RMS for left
float leftRMS = 0f;
for (float sample : leftSamples) {
leftRMS += sample * sample;
}
leftRMS = (float) Math.sqrt(leftRMS / leftSamples.length);
// ...and right
float rightRMS = 0f;
for (float sample : rightSamples) {
rightRMS += sample * sample;
}
rightRMS = (float) Math.sqrt(rightRMS / rightSamples.length);
}
Is there an easy way to convert a byte array of N bytes to a float defined by the Q-Number.
In 2's Compliment (Forgot To mention this before)
Example: 0xFFF0 -> Float-point using a s3.12 Q-Number
https://en.wikipedia.org/wiki/Q_(number_format)
Following suggestion by Peter Lawrey:
private static void test(short x) {
float y = (float)(x & 0x7FFF) / (1 << 12);
if ((x & 0x8000) != 0)
y = -y;
System.out.printf("%04x: %g%n", x, y);
}
Test
test((short)0xFFF0);
test((short)0xFFFF);
test((short)0x0000);
test((short)0x0001);
test((short)0x1000);
test((short)0x9000);
test((short)0x7FFF);
Output
fff0: -7.99609
ffff: -7.99976
0000: 0.00000
0001: 0.000244141
1000: 1.00000
9000: -1.00000
7fff: 7.99976
You can use
byte[] bytes = ....
ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.????); // check your byte order
while(bb.remaining() > 1) {
short s = bb.getShort();
boolean signed = s < 0;
int value = s & 0x7FFF;
double d = s / 4096.0;
if (signed)
d = -d;
System.out.println(d);
}
Here's what I'm working with right now:
for (int i = 0, numSamples = soundBytes.length / 2; i < numSamples; i += 2)
{
// Get the samples.
int sample1 = ((soundBytes[i] & 0xFF) << 8) | (soundBytes[i + 1] & 0xFF); // Automatically converts to unsigned int 0...65535
int sample2 = ((outputBytes[i] & 0xFF) << 8) | (outputBytes[i + 1] & 0xFF); // Automatically converts to unsigned int 0...65535
// Normalize for simplicity.
float normalizedSample1 = sample1 / 65535.0f;
float normalizedSample2 = sample2 / 65535.0f;
float normalizedMixedSample = 0.0f;
// Apply the algorithm.
if (normalizedSample1 < 0.5f && normalizedSample2 < 0.5f)
normalizedMixedSample = 2.0f * normalizedSample1 * normalizedSample2;
else
normalizedMixedSample = 2.0f * (normalizedSample1 + normalizedSample2) - (2.0f * normalizedSample1 * normalizedSample2) - 1.0f;
int mixedSample = (int)(normalizedMixedSample * 65535);
// Replace the sample in soundBytes array with this mixed sample.
soundBytes[i] = (byte)((mixedSample >> 8) & 0xFF);
soundBytes[i + 1] = (byte)(mixedSample & 0xFF);
}
From as far as I can tell, it's an accurate representation of the algorithm defined on this page: http://www.vttoth.com/CMS/index.php/technical-notes/68
However, just mixing a sound with silence (all 0's) results in a sound that very obviously doesn't sound right, maybe it's best to describe it as higher-pitched and louder.
Would appreciate help in determining if I'm implementing the algorithm correctly, or if I simply need to go about it a different way (different algorithm/method)?
In the linked article the author assumes A and B to represent entire streams of audio. More specifically X means the maximum abs value of all of the samples in stream X - where X is either A or B. So what his algorithm does is scans the entirety of both streams to compute the max abs sample of each and then scales things so that the output theoretically peaks at 1.0. You'll need to make multiple passes over the data in order to implement this algorithm and if your data is streaming in then it simply will not work.
Here is an example of how I think the algorithm to work. It assumes that the samples have already been converted to floating point to side step the issue of your conversion code being wrong. I'll explain what is wrong with it later:
double[] samplesA = ConvertToDoubles(samples1);
double[] samplesB = ConvertToDoubles(samples2);
double A = ComputeMax(samplesA);
double B = ComputeMax(samplesB);
// Z always equals 1 which is an un-useful bit of information.
double Z = A+B-A*B;
// really need to find a value x such that xA+xB=1, which I think is:
double x = 1 / (Math.sqrt(A) * Math.sqrt(B));
// Now mix and scale the samples
double[] samples = MixAndScale(samplesA, samplesB, x);
Mixing and scaling:
double[] MixAndScale(double[] samplesA, double[] samplesB, double scalingFactor)
{
double[] result = new double[samplesA.length];
for (int i = 0; i < samplesA.length; i++)
result[i] = scalingFactor * (samplesA[i] + samplesB[i]);
}
Computing the max peak:
double ComputeMaxPeak(double[] samples)
{
double max = 0;
for (int i = 0; i < samples.length; i++)
{
double x = Math.abs(samples[i]);
if (x > max)
max = x;
}
return max;
}
And conversion. Notice how I'm using short so that the sign bit is properly maintained:
double[] ConvertToDouble(byte[] bytes)
{
double[] samples = new double[bytes.length/2];
for (int i = 0; i < samples.length; i++)
{
short tmp = ((short)bytes[i*2])<<8 + ((short)(bytes[i*2+1]);
samples[i] = tmp / 32767.0;
}
return samples;
}
I've got this loop that is run thousands of times (so needs to be efficient). It changes the value of the bitmap pixel.
I want to be able to run thought the loop and "switch" a certain group of pixels to alpha and then switch them back at a later point.
My question is.
How do I switch the values? So say 0xFFCC1BE0 becomes 0x00CC1BE0 and then if I want to switch back to 0xFFCC1BE0 I simply take the 00 and turn it too FF.
I can't make two bitmaps as I run out of memory :-(
Anyhow here's what I've got so far:
private void setTransparencyOnLightMap(float WidthPercentage, float LeftPosition)
{
int blankPixel = 0x00000000;
int savedPixel = 0x00000000;
int desiredAlpha = 200; //Can also be 0x00
//Find away of turning alhpa off and on.
for(int BMx = 0; BMx < mLightMap.getWidth(); BMx++)
{
for(int BMy = 0; BMy < mLightMap.getHeight(); BMy++)
{
if(mLightMap.getPixel(BMx, BMy) != blankPixel) //Make sure don't overwrite blank transparent pixels.
{
savedPixel = mLightMap.getPixel(BMx,BMy);
savedPixel = savedPixel | (desiredAlpha << 24);
mLightMap.setPixel(BMx, BMy, savedPixel);
}
}
}
}
You could switch the alpha of a pixel like so:
savedPixel = savedPixel & 0x00FFFFFF;
savedPixel = savedPixel | (desiredAlpha << 24);
The first line zeros out the 8 most significant bits of savedPixel (these are the bits where the alpha is held). The second line sets the 8 most significant bits of savedPixel to desiredAlpha. Note that desiredAlpha must be between 0 and 255 (These are the ints that can be stored in 8 bits).
Note that this uses bitwise operators, (&, |, <<) which are very efficient.
It seems to me, to reduce memory use, you can just save the original Alpha value of each pixel rather than the whole ARGB value - to do this use a byte array which will be 1/4 the size of the original bitmap. Also use a bit-mask for the new Alpha so you can use bitwise AND (&) as described by Tristan Hull...
byte[] savedAlphaArray = new byte[mLightMap.getWidth(), mLightMap.getHeight()];
int desiredAlphaMask = 0x00FFFFFF;
int pixel;
Then to save the Alpha values and apply the bit-mask do the following...
for (int i = 0; i < mLightMap.getWidth(); i++) {
for (int j = 0; j < mLightMap.getHeight(); j++) {
pixel = mLightMap.getPixel(i, j);
savedAlphaArray[i, j] = (pixel & 0xFF000000) >> 24;
mLightMap.setPixel(i, j, desiredAlphaMask & pixel);
}
}
To 'switch' back do the following...
for (int i = 0; i < mLightMap.getWidth(); i++) {
for (int j = 0; j < mLightMap.getHeight(); j++) {
pixel = mLightMap.getPixel(i, j);
mLightMap.setPixel(i, j, savedAlphaArray[i, j] << 24 & pixel);
}
}
I am trying to get the RGB value of any given pixel on an image into 12 bit binary, with each channel represented by 4 bits. For example, if a pixel has it's red channel at '255', or in binary "11111111". I wish to convert that to "1111".
I have got a code working, but it is rather slow, and I am wondering if there is a better way to do this.
Here's what i did.
I use the method getRGB(x, y) to get the RGB value of one pixel.
e.g. -4278864
I convert that into 3 separate channels e.g. r 190 g 181 b 176
I divide that by 16 to get integer equivalent of 4 bit
representation
I convert the number to a binary.
Add 0's in front of the binary if the binary generated is less
than 4 bit.
Concatenate to a 12bit binary to represent 12bit color output. e.g. 101110111011
Here are my code:
import java.awt.Component;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class LoadImageApp extends Component {
private static int[] rgbArray;
private static double bitPerColor = 4.0;
public static void main(String[] args) {
BufferedImage img = null;
String fileName = "Image.jpg";
try {
//Read in new image file
img = ImageIO.read(new File("src/"+fileName));
}
catch (IOException e){
}
if (img == null) {
System.out.println("No image loaded");
}
else {
//Get RGB Value
int val = img.getRGB(500, 500);
System.out.println("rgbValue from getRGB is: " + val);
//Convert to three separate channels
int a = (0xff000000 & val) >>> 24;
int r = (0x00ff0000 & val) >> 16;
int g = (0x0000ff00 & val) >> 8;
int b = (0x000000ff & val);
System.out.println("rgbValue in RGB is: ");
System.out.println("a " + a + " r " + r + " g " + g + " b " + b);
double power = Math.pow(2.0, bitPerColor);
//Convert each channel to binary
String r4bit = Integer.toBinaryString((int)(r/(power)));
String g4bit = Integer.toBinaryString((int)(g/(power)));
String b4bit = Integer.toBinaryString((int)(b/(power)));
//Convert concatonate 0's in front to get desired bit count
int rDifference = (int)bitPerColor - r4bit.length();
int gDifference = (int)bitPerColor - g4bit.length();
int bDifference = (int)bitPerColor - b4bit.length();
for (int i = rDifference; i > 0; i--){
r4bit="0"+r4bit;}
for (int i = gDifference; i > 0; i--){
g4bit = "0"+g4bit;}
for (int i = bDifference; i > 0; i--){
b4bit = "0"+b4bit;}
//Concatonate three channel together to form one binary
String rgbValue = r4bit + g4bit + b4bit;
System.out.println("rgbValue in binary is: " + rgbValue);
}
}
}
It all works fine as as desired. However it's just really, really ugly, and slow, 2-3 seconds just to read one pixel. I was hoping to use the code to read a section of an image at a time, but i can imaging it taking AGES.
So any help would be very much appreciated.
Thanks in advance.
Your original code (in hex) is 0x00rrggbb. You want to convert this to 0x00000rgb. This will do it:
int rgb24 = ....;
int rgb12 = (rgb24 & 0x00f00000) >> 12 +
(rgb24 & 0x0000f000) >> 8 +
(rgb24 & 0x000000f0) >> 4;
If you wanted to "round" to the nearest color instead of truncating you could do this:
int r = (rgb24 & 0x00ff0000);
int g = (rgb24 & 0x0000ff00);
int b = (rgb24 & 0x000000ff);
r += (r >= 0x00f00000) ? 0x00080000 : 0;
g += (g >= 0x0000f000) ? 0x00000800 : 0;
b += (b >= 0x000000f0) ? 0x00000008 : 0;
int rgb12 = (r & 0x00f00000) >> 12 + (g & 0x0000f000) >> 8 + (b & 0x000000f0) >> 4;
This rounds up but only if the high-order 4 bits are not already 1111 (when you would risk overflow).
There are a few things that might be slowing it down, such as...
Getting each pixel individually from the BufferedImage
Using Math.pow() and possibly Integer.toBinaryString(), multiple times in the loop
Using Strings all the way through, rather than numbers like int or short. If you want a String, maybe do a single conversion from short --> String at the end.
I would probably try to do something like this...
// Get all the pixels
int pixelCount = imageWidth*imageHeight;
int[] pixelArray = new int[pixelCount];
img.getRGB(0,0,imageWidth,imageHeight,pixelArray,0,1);
// For storing the 4-bit data
short[] convertedArray = new short[pixelCount];
// calculate the conversion factor a single time
double power = Math.pow(2.0, bitPerColor);
// Loop over the pixels
for (int p=0;p<pixelCount;p++){
int pixel = pixelArray[p];
// Convert into separate channels
int a = (0xff000000 & val) >>> 24;
int r = (0x00ff0000 & val) >> 16;
int g = (0x0000ff00 & val) >> 8;
int b = (0x000000ff & val);
// Convert to 4-bit
a /= power;
r /= power;
g /= power;
b /= power;
// Create the short for the pixel (4 channels * 4 bits = a 2-byte short)
short newPixel = (a & 0x0000000f) << 24 | (r & 0x0000000f) << 16 | (g & 0x0000000f) << 8 | (b & 0x0000000f);
convertedArray[p] = newPixel;
// If you want to have the value as a String, do it here.
String binary = Short.toBinaryString(newPixel);
}
Offhand, I noticed you used the String plus operator for appending. I know it's simple code but that takes some work when it is in a loop. The operator calls a constructor to execute its function.
How big is the loop anyway?
Instead of using the plus operator, perhaps use other classes like StringBuilder or a Java equivalent of it. Tell me which class you end up using.
Investigate by timing your code!
When you want to see your possible bottlenecks, look at LOOPS in a heap or stack first since there are potentially many code executions. This should be a coding rule somewhere...
Try it out & have fun. Pls vote for my answer. I need a badge and more points :-)
Tommy Kwee
The value I have collected in the RGB when set using SetRGB will it provide me an image of binary for where the area where the image is present is white and the area whre the image is absent is black ?