I was using openCv to apply filters and ran into a problem.
What I did in this specific code was first try and get a grayscale image but preserve the color channels it works fine but when I write to file using Image IO I find the alpha has been altered. So I checked for BGRA and ABGR colorspaces but it still does not work and gives me a transparent image.
public static BufferedImage sepia(BufferedImage image,int intensity)
{
Mat imageMat = bufferedToMat(image);
int sepiaDepth = 20;
int width = image.getWidth();
int height = image.getHeight();
Mat grayScaleMat = new Mat(imageMat.height(),imageMat.width(),CvType.CV_8UC4);
imageMat.copyTo(grayScaleMat);
// double[] test = imageMat.get(0, 0);
// System.out.println(test[0]+" "+test[1]+" "+test[2]+" "+test[3]);
for(int i=0;i<grayScaleMat.cols();i++)
{
for(int j=0;j<grayScaleMat.rows();j++)
{
//can be optimised
double[] data = grayScaleMat.get(j, i);
//System.out.println(data.length);
double blue = data[0];
double green = data[1];
double red = data[2];
//System.out.println(red+" "+blue+" "+green);
double gray = (red + blue + green)/3.0;
//data[0] = gray;
data[0] = gray;
data[1] = gray;
data[2] = gray;
grayScaleMat.put(j, i, data);
}
}
return (Utility.matToBuffered(grayScaleMat));
}
//Only Testing Remove Later
public static void main(String[] args)
{
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
int beta = 25;
String imagePath = "/home/arjun/Pictures/Lenna.png";
BufferedImage image = null;
try{
image = ImageIO.read(new File(imagePath));
}catch(IOException e)
{
e.printStackTrace();
}
int x = image.getType();
System.out.println(x);
BufferedImage output = sepia(image,beta);
int y = output.getType();
System.out.println(y);
File outputfile = new File("/home/arjun/Pictures/sepia2.png");
try{
ImageIO.write(output, "png", outputfile);
}catch(IOException e)
{
e.printStackTrace();
}
}
And the Buffered and Mat conversions here
public static Mat bufferedToMat(BufferedImage image)
{
byte[] pixels = ((DataBufferByte)image.getRaster().getDataBuffer()).getData();
Mat imageMat = new Mat(image.getHeight(),image.getWidth(),CvType.CV_8UC4);
imageMat.put(0, 0, pixels);
return imageMat;
}
public static BufferedImage matToBuffered(Mat imageMat)
{
BufferedImage out;
byte[] data = new byte[imageMat.cols()*imageMat.rows()*(int)imageMat.elemSize()];
imageMat.get(0, 0,data);
int type = BufferedImage.TYPE_3BYTE_BGR;
if(imageMat.channels() == 1)
{
type = BufferedImage.TYPE_BYTE_GRAY;
}
else if(imageMat.channels() == 3)
{
type = BufferedImage.TYPE_3BYTE_BGR;
}
else if(imageMat.channels() == 4)
{
type = BufferedImage.TYPE_4BYTE_ABGR;
}
out = new BufferedImage(imageMat.cols(),imageMat.rows(),type);
out.getRaster().setDataElements(0,0,imageMat.cols(),imageMat.rows(),data);
return out;
}
Input Image:
Output Image:
Related
I am receiving a MultipartFile Spring object from rest controller. I am trying to convert any inage file to JPG image but I just need the byte array to save it on mongoDb
I found this code to do that
public boolean convertImageToJPG(InputStream attachedFile) {
try {
BufferedImage inputImage = ImageIO.read(attachedFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
boolean result = ImageIO.write(inputImage, "jpg", byteArrayOutputStream);
return result;
} catch (IOException e) {
System.err.println("Error " + e);
}
return false;
}
But result as a false with not error, so ImageIO.write is not working
Also I found this to do the same but using File object, I don't want to create the file on directory, I just need the byte array
public static boolean convertFormat(String inputImagePath,
String outputImagePath, String formatName) throws IOException {
FileInputStream inputStream = new FileInputStream(inputImagePath);
FileOutputStream outputStream = new FileOutputStream(outputImagePath);
// reads input image from file
BufferedImage inputImage = ImageIO.read(inputStream);
// writes to the output image in specified format
boolean result = ImageIO.write(inputImage, formatName, outputStream);
// needs to close the streams
outputStream.close();
inputStream.close();
return result;
}
Testing
public class TestImageConverter {
public static void main(String[] args) {
String inputImage = "D:/Photo/Pic1.jpg";
String oututImage = "D:/Photo/Pic1.png";
String formatName = "PNG";
try {
boolean result = ImageConverter.convertFormat(inputImage,
oututImage, formatName);
if (result) {
System.out.println("Image converted successfully.");
} else {
System.out.println("Could not convert image.");
}
} catch (IOException ex) {
System.out.println("Error during converting image.");
ex.printStackTrace();
}
}
}
How can I solve my problem?
UPDATED SOLUTION (alternative with no need for Raster and ColorModel)
It had indeed bothered me that my older solution (see below) still required Rasters and ColorModels. I got challenged on my solution, so I spent some more time looking for alternatives. So the best thing I could come up with now is the following:
try {
final FileInputStream fileInputStream = new FileInputStream("dice.png");
final BufferedImage image = ImageIO.read(fileInputStream);
fileInputStream.close(); // ImageIO.read does not close the input stream
final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
convertedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
final FileOutputStream fileOutputStream = new FileOutputStream("dice-test.jpg");
final boolean canWrite = ImageIO.write(convertedImage, "jpg", fileOutputStream);
fileOutputStream.close(); // ImageIO.write does not close the output stream
if (!canWrite) {
throw new IllegalStateException("Failed to write image.");
}
} catch (IOException e) {
e.printStackTrace();
}
I ended up with a copy of the BufferedImage as I did before. It does more or less the same thing, but you can actually reuse the ColorModel and Raster more easily.
drawImage() seems to take care of most of what I did before manually. And since it is standard java library code all the way, it seems indeed to be a better way.
Note that you end up with an Image of type BufferedImage.TYPE_INT_RGB. While it seems to work for the types jpg, png, and gif, I am not sure what will happen to other file formats or files with a different storage ColorModel - information might be lost (e.g. 4 color-channels to 3). For the mentioned types we don't need an alpha channel, even if we convert from gif or jpg to png (it will be Color.WHITE).
OLD SOLUTION
I was not happy with my first design and also it did not quite work the way it should have.
Therefore, I have created one from scratch. I ended up with a little converter for sRGB files. You can convert from png to jpg and vice versa (Edit: Added gif support also). If you want to handle other types feel free to extend this further. You can more or less add it the same way. It might work for other file types as well, but I have not tested them yet. Luckily, it seems that sRGB is quite common though.
Tbh. I have no idea how many combinations and variants (color palettes, precision, quality, b/w, etc.) you can produce or which common properties they share.
Maybe this is good enough for you. Maybe not. At least it was a nice exercise for me.
This solution is by no means perfect. The results looked okay-ish. The file-type conversion worked and the file-size is also smaller than the png.
try {
final String fileName = "dice.png";
final BufferedImage inputImage = ImageIO.read(new FileInputStream(fileName));
final boolean isSRGB = inputImage.getColorModel().getColorSpace().isCS_sRGB();
final String outputFormat = "gif";
if (!isSRGB) {
throw new IllegalArgumentException("Please provide an image that supports sRGB.");
}
final WritableRaster raster = createRaster(inputImage);
final ColorModel colorModel = createColorModel(inputImage);
final BufferedImage outputImage = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
final String outputFileName = fileName + "-converted." + outputFormat;
final boolean writeResult = ImageIO.write(outputImage, outputFormat, new FileOutputStream(outputFileName));
if (!writeResult) {
throw new IllegalStateException("Could not convert file: " + fileName + " to format: " + outputFormat);
}
System.out.println(">> Created file: " + outputFileName);
} catch (Exception e) {
e.printStackTrace();
}
#NotNull
public static ColorModel createColorModel(#NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
boolean isAlphaPremultiplied = false;
int transparency = Transparency.OPAQUE;
if (type == BufferedImage.TYPE_3BYTE_BGR) {
isAlphaPremultiplied = true;
}
return new ComponentColorModel(
ColorModel.getRGBdefault().getColorSpace(),
false, isAlphaPremultiplied, transparency,
bufferedImage.getData().getDataBuffer().getDataType()
);
}
#NotNull
public static WritableRaster createRaster(#NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
final int width = bufferedImage.getWidth();
final int height = bufferedImage.getHeight();
final int pixelStride = 3;
int[] offset = new int[]{0, 1, 2};
DataBufferByte dataBufferByte;
if (type == BufferedImage.TYPE_4BYTE_ABGR || type == BufferedImage.TYPE_BYTE_INDEXED) {
int dataIndex = 0;
final byte[] data = new byte[height * width * pixelStride];
final int bitmask = 0xff;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final int rgb = bufferedImage.getRGB(x, y);
final int blue = bitmask & rgb;
final int green = bitmask & (rgb >> 8);
final int red = bitmask & (rgb >> 16);
if (rgb == 0) {
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
} else {
data[dataIndex++] = (byte) red;
data[dataIndex++] = (byte) green;
data[dataIndex++] = (byte) blue;
}
}
}
dataBufferByte = new DataBufferByte(data, data.length);
} else if (type == BufferedImage.TYPE_3BYTE_BGR) {
dataBufferByte = (DataBufferByte) bufferedImage.getRaster().getDataBuffer();
offset = new int[]{2, 1, 0};
} else {
throw new IllegalArgumentException("Cannot create raster for unsupported image type.");
}
return Raster.createInterleavedRaster(
dataBufferByte, width, height,
pixelStride * width, pixelStride,
offset,
null
);
}
EDIT: Added support for gif.
I have an grayscale image with dimension 256*256.I am trying to downscale it to 128*128.
I am taking an average of two pixel and writing it to the ouput file.
class Start {
public static void main (String [] args) throws IOException {
File input= new File("E:\\input.raw");
File output= new File("E:\\output.raw");
new Start().resizeImage(input,output,2);
}
public void resizeImage(File input, File output, int downScaleFactor) throws IOException {
byte[] fileContent= Files.readAllBytes(input.toPath());
FileOutputStream stream= new FileOutputStream(output);
int i=0;
int j=1;
int result=0;
for(;i<fileContent.length;i++)
{
if(j>1){
// skip the records.
j--;
continue;
}
else {
result = fileContent[i];
for (; j < downScaleFactor; j++) {
result = ((result + fileContent[i + j]) / 2);
}
j++;
stream.write( fileContent[i]);
}
}
stream.close();
}
}
Above code run successfully , I can see the size of output file size is decreased but when I try to convert
output file (raw file) to jpg online (https://www.iloveimg.com/convert-to-jpg/raw-to-jpg) it is giving me an error saying that file is corrupt.
I have converted input file from same online tool it is working perfectly. Something is wrong with my code which is creating corrupt file.
How can I correct it ?
P.S I can not use any library which directly downscale an image .
Your code is not handling image resizing.
See how-to-resize-images-in-java.
Which, i am copying a simple version here:
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageResizer {
public static void resize(String inputImagePath,
String outputImagePath, int scaledWidth, int scaledHeight)
throws IOException {
// reads input image
File inputFile = new File(inputImagePath);
BufferedImage inputImage = ImageIO.read(inputFile);
// creates output image
BufferedImage outputImage = new BufferedImage(scaledWidth,
scaledHeight, inputImage.getType());
// scales the input image to the output image
Graphics2D g2d = outputImage.createGraphics();
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose();
// extracts extension of output file
String formatName = outputImagePath.substring(outputImagePath
.lastIndexOf(".") + 1);
// writes to output file
ImageIO.write(outputImage, formatName, new File(outputImagePath));
}
public static void resize(String inputImagePath,
String outputImagePath, double percent) throws IOException {
File inputFile = new File(inputImagePath);
BufferedImage inputImage = ImageIO.read(inputFile);
int scaledWidth = (int) (inputImage.getWidth() * percent);
int scaledHeight = (int) (inputImage.getHeight() * percent);
resize(inputImagePath, outputImagePath, scaledWidth, scaledHeight);
}
public static void main(String[] args) {
String inputImagePath = "resources/snoopy.jpg";
String outputImagePath1 = "target/Puppy_Fixed.jpg";
String outputImagePath2 = "target/Puppy_Smaller.jpg";
String outputImagePath3 = "target/Puppy_Bigger.jpg";
try {
// resize to a fixed width (not proportional)
int scaledWidth = 1024;
int scaledHeight = 768;
ImageResizer.resize(inputImagePath, outputImagePath1, scaledWidth, scaledHeight);
// resize smaller by 50%
double percent = 0.5;
ImageResizer.resize(inputImagePath, outputImagePath2, percent);
// resize bigger by 50%
percent = 1.5;
ImageResizer.resize(inputImagePath, outputImagePath3, percent);
} catch (IOException ex) {
System.out.println("Error resizing the image.");
ex.printStackTrace();
}
}
}
I built a little java program that hides messages in an image using the least significant bit method. It works fine when inputting a jpg file. The output may be png or jpg. When inputting a png though, the result looks very stange.
Here are the original and the result images respectively:
public abstract class Builder{
public static void leastSignificantBitEncryption(String imageSource, String message, String newPath) {
BufferedImage image = returnImage(imageSource);
//prepare variables
String[] messageBinString = null;
String[] pixelBinString = null;
final byte[] messageBin = message.getBytes(StandardCharsets.UTF_8);
final byte[] pixelsBin = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
//convert message and image to binary string array
try {
messageBinString = stringToBinaryStrings(messageBin);
pixelBinString = stringToBinaryStrings(pixelsBin);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String[] messageBinStringCut = splitIn2Bit(messageBinString); //split message binary into 2 bit strings
String[] pixelBinStringNew = pixelBinString.clone(); //insert 2 bit strings in last 2 bits of bytes from bitmap
insert2Bit(messageBinStringCut, pixelBinStringNew);
byte[] pixelsBinNew = stringArrayToByteArray(pixelBinStringNew); //Convert string array to byte array
try { //Create new image out of bitmap
int w = image.getWidth();
int h = image.getHeight();
BufferedImage imageNew = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
imageNew.setData(Raster.createRaster(imageNew.getSampleModel(), new DataBufferByte(pixelsBinNew, pixelsBinNew.length), new Point()));
File imageFile = new File(newPath);
ImageIO.write(imageNew, "png", imageFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private static String[] stringToBinaryStrings(byte[] messageBin) throws UnsupportedEncodingException{
String[] bytes = new String[messageBin.length];
int i = 0;
for(byte b : messageBin) {
bytes[i] = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
i++;
}
return bytes;
}
private static String binaryStringsToString(String[] messageBin) throws UnsupportedEncodingException{
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
while(messageBin[i] != null) {
stringBuilder.append((char) Integer.parseInt(messageBin[i], 2));
i++;
}
return stringBuilder.toString();
}
private static BufferedImage returnImage(String imageSource) {
try{
try {
return ImageIO.read(new URL(imageSource));
} catch (MalformedURLException e) {
return ImageIO.read(new File(imageSource));
}
} catch (IOException ioe) {
ioe.printStackTrace();
return null;
}
}
private static byte[] stringArrayToByteArray(String[] stringArray) {
byte[] byteArray = new byte[stringArray.length];
for(int i = 0; i < stringArray.length; i++) {
byteArray[i] = (byte) Integer.parseInt(stringArray[i], 2);
}
return byteArray;
}
private static String[] splitIn2Bit(String[] inputArray) {
String[] outputArray = new String[inputArray.length * 4];
for(int i = 0; i < outputArray.length; i += 4) {
String[] splitByte = inputArray[i / 4].split("(?<=\\G..)");
outputArray[i] = splitByte[0];
outputArray[i + 1] = splitByte[1];
outputArray[i + 2] = splitByte[2];
outputArray[i + 3] = splitByte[3];
}
return outputArray;
}
private static String[] insert2Bit(String[] twoBitArray, String[] insertArray) {
for(int i = 0; i < twoBitArray.length; i++) {
insertArray[i] = insertArray[i].substring(0, 6) + twoBitArray[i];
}
return insertArray;
}
}
Also, the testclass:
public class Test {
public static void main(String[] args) {
Builder.leastSignificantBitEncryption("IMAGEPATH OR URL", "MESSAGE", "PATH FOR IMAGE CONTAINING MESSAGE");
Builder.leastSignificantBitDecryption("PATH OF IMAGE CONTAINING MESSAGE", "PATH FOR TXT CONTAINING OUTPUT");
}
}
The error originates from the fact that the png image has an extra channel for transparency. System.out.println(pixelsBin.length); returns 338355 bytes for the jpg and 451140 bytes for the png.
The simplest solution would be to create the appropriate imageNew depending on the format file. For example,
int w = image.getWidth();
int h = image.getHeight();
BufferedImage imageNew = null;
if (imageSource.matches(".*jpg$")) {
imageNew = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
} else if (imageSource.matches(".*png$")) {
imageNew = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
} else {
// whatever
}
imageNew.setData(Raster.createRaster(imageNew.getSampleModel(), new DataBufferByte(pixelsBinNew, pixelsBinNew.length), new Point()));
However, you have to be aware that the message is not embedded in both types in the same pixels. The byte array of a 3-channel image (no transparency) goes like this
first-pixel-BLUE, first-pixel-GREEN, first-pixel-RED, second-pixel-BLUE, etc
while for a 4-channel image
first-pixel-ALPHA, first-pixel-BLUE, first-pixel-GREEN, first-pixel-RED, second-pixel-ALPHA, etc
If you care about that detail, you might be interested in removing the alpha channel from the png first, so you're always working with 3-channel images.
public static void main(String[] args) {
String finalHex = "";
String input = "Hello There Sir.";
int pixelX = -1;
int pixelY = 0;
try{
BufferedImage bi = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
File out = new File("saved.png");
if(out.exists()==false){
ImageIO.write(bi, "png", out);
System.out.println("PNG WAS CREATED");
}else
System.out.println("ERROR: PNG WAS ALREADY THERE");
for (int i = 0;i < input.length(); i++){
char result = input.charAt(i);
int ascii = (int) result;
String num = Integer.toHexString(ascii).toUpperCase();
if(finalHex.length()==6){
System.out.println(finalHex);
pixelX += 1;
finalHex=("#"+finalHex);
Color c = Color.decode(finalHex);
int rgb = c.getRGB();
System.out.println(rgb);
if(pixelX==63){
pixelX=0;
pixelY+=1;
}
bi.setRGB(pixelX, pixelY, rgb);
finalHex="";
}
finalHex+=num;
}
}catch(IOException e){
System.out.println("ERROR: WELP... SOMETHING SCREWED UP.");
}
}
I am trying to use this to convert text into a png image but I cant get it to write to a png file. I am not that experienced in this area so if anyone could help me out i would be very much appreciated. :)
you should add ImageIO.write(bi, "png", out); after the end of for(int i = 0;i < input.length(); i++){...} this program will write some colored pixels is that what you want??
example:
result picture
I want to resize an image and then to write it back to outputstream, for this I need to convert the scaled image into bytes, how can I convert it?
ByteArrayInputStream bais = new ByteArrayInputStream(ecn.getImageB());
BufferedImage img = ImageIO.read(bais);
int scaleX = (int) (img.getWidth() * 0.5);
int scaleY = (int) (img.getHeight() * 0.5);
Image newImg = img.getScaledInstance(scaleX, scaleY, Image.SCALE_SMOOTH);
outputStream.write(newImg); //cannot resolve
how to fix outputStream.write(newImg)???
Use this method for scaling:
public static BufferedImage scale(BufferedImage sbi,
int imageType, /* type of image */
int destWidth, /* result image width */
int destHeight, /* result image height */
double widthFactor, /* scale factor for width */
double heightFactor /* scale factor for height */ )
{
BufferedImage dbi = null;
if(sbi != null) {
dbi = new BufferedImage(destWidth, destHeight, imageType);
Graphics2D g = dbi.createGraphics();
AffineTransform at = AffineTransform.getScaleInstance(widthFactor, heightFactor);
g.drawRenderedImage(sbi, at);
}
return dbi;
}
Then you'll have a BufferedImage which you can write to a byte array
public static byte[] writeToByteArray(BufferedImage bi, String dImageFormat) throws IOException, Exception {
byte[] scaledImageData = null;
ByteArrayOutputStream baos = null;
try {
if(bi != null) {
baos = new ByteArrayOutputStream();
if(! ImageIO.write(bi, dImageFormat, baos)) {
throw new Exception("no appropriate writer found for the format " + dImageFormat);
}
scaledImageData = baos.toByteArray();
}
} finally {
if(baos != null) {
try {
baos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
return scaledImageData;
}
Include this line and check:-
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpg", outputStream);
byte[] imageInByte=outputStream.toByteArray();