Using "gdal_translate" from .TIF to .JPG how to set background to white? - cmd

I tried using the following line:
gdal_translate -of jpeg -a_nodata 0 -b 1 -b 2 -b 3 c:\myfolder\mytif.tif c:\myfolder\myNewtif.jpg
This produces the image with wanted specs, but turns the background to black (transparency?), eventhough the original has white. Can I accomplish a white background with just gdal_translate?
File dump of a file with mask:
https://filebin.net/f15v63to2x3cc4z0
File dump of a file with no a mask: https://filebin.net/kc940hqotcoeny0w
gdalinfo output of a Tif that produces white background as expected:
Driver: GTiff/GeoTIFF
Files: test.tif
Size is 4799, 3196
Metadata:
TIFFTAG_RESOLUTIONUNIT=2 (pixels/inch)
TIFFTAG_XRESOLUTION=300
TIFFTAG_YRESOLUTION=300
Image Structure Metadata:
COMPRESSION=LZW
INTERLEAVE=PIXEL
Corner Coordinates:
Upper Left ( 0.0, 0.0)
Lower Left ( 0.0, 3196.0)
Upper Right ( 4799.0, 0.0)
Lower Right ( 4799.0, 3196.0)
Center ( 2399.5, 1598.0)
Band 1 Block=4799x1 Type=Byte, ColorInterp=Red
Band 2 Block=4799x1 Type=Byte, ColorInterp=Green
Band 3 Block=4799x1 Type=Byte, ColorInterp=Blue
Tif that produces black background:
Warning 1: TIFFFetchNormalTag:Incompatible type for "RichTIFFIPTC"; tag ignored
Driver: GTiff/GeoTIFF
Files: 100011_1.tif
Size is 1640, 2401
Metadata:
TIFFTAG_DATETIME=2020:01:13 12:29:55
TIFFTAG_RESOLUTIONUNIT=2 (pixels/inch)
TIFFTAG_SOFTWARE=Adobe Photoshop 21.0 (Windows)
TIFFTAG_XRESOLUTION=300
TIFFTAG_YRESOLUTION=300
Image Structure Metadata:
COMPRESSION=LZW
INTERLEAVE=PIXEL
Corner Coordinates:
Upper Left ( 0.0, 0.0)
Lower Left ( 0.0, 2401.0)
Upper Right ( 1640.0, 0.0)
Lower Right ( 1640.0, 2401.0)
Center ( 820.0, 1200.5)
Band 1 Block=1640x39 Type=Byte, ColorInterp=Red
Mask Flags: PER_DATASET ALPHA
Band 2 Block=1640x39 Type=Byte, ColorInterp=Green
Mask Flags: PER_DATASET ALPHA
Band 3 Block=1640x39 Type=Byte, ColorInterp=Blue
Mask Flags: PER_DATASET ALPHA
Band 4 Block=1640x39 Type=Byte, ColorInterp=Alpha
Also of the following images that have black background after translate, gdal produces this warning "Warning 1: TIFFFetchNormalTag: Incompatible type for "RichTIFFIPTC"; tag ignored"

The file that you have shared on filebin contains an "alpha" mask, as you can see in the output of gdalinfo. The mask of this file says that the background is masked, while the rest of the image is not.
If you display the tiff with the default Ubuntu viewer, for example, you can see that the background pixels are masked out (they appear as a checker board)
If you inspect the raster data, you also see that the underlying pixels from the background are black, not white. That is why gdal_translate generates a jpeg with black pixels in the background, it is because they really are black (but masked out) in the original tiff file.
If you absolutely want the background to be white, you can do so with a few lines of Python using the rasterio library for example, by explicitly setting the masked pixels to white:
import rasterio
with rasterio.open("101679_1.tif") as src:
arr = src.read(masked=True)
# Convert all masked values to white
arr[arr.mask] = 255
# Write to jpeg file
profile = src.profile
profile["count"] = 3
profile["driver"] = "jpeg"
with rasterio.open("test.jpeg", "w", **profile) as dst:
dst.write(arr[:3])
This should give the following jpeg file:
The code snippet I have included above will also work on TIF files which already have a white background, because the arr[arr.mask] = 255 line will not do anything if the file contains no mask.
To process a directory full of .tif files, you can do:
from glob import glob
import rasterio
for tif in glob("*.tif"):
with rasterio.open(tif) as src:
arr = src.read(masked=True)
# Convert all masked values to white
arr[arr.mask] = 255
# Write to jpeg file
profile = src.profile
profile["count"] = 3
profile["driver"] = "jpeg"
with rasterio.open(tif.replace(".tif", ".jpeg"), "w", **profile) as dst:
dst.write(arr[:3])

Related

How to read an Unsigned Char image file to Python?

I have an text file called with the extension '.image', at the top of the file is the following:
Image Type: unsigned char
Dimension: 2
Image Size: 512 512
Image Spacing: 1 1
Image Increment: 1 1
Image Axis Labels: "" ""
Image Intensity Label: ""
193
I believe this to be an unsigned char file, but i'm having a hard time opening it in python (and saving it as a jpg, png etc)
Have tried the standard PIL.Image.open(), saving as a string and reading with Image.fromstring('RGB', (512,512), string), and reading as byte-like object Image.open(io.BytesIO(filepath))
Any ideas? Thanks in advance
If we assume there is some arbitrary, unknown length header on the file, we can read the entire file and then rather than parsing the header, just take the final 512x512 bytes from the tail of the file:
#!/usr/bin/env python3
from PIL import Image
import pathlib
# Slurp the entire contents of the file
f = pathlib.Path('image.raw').read_bytes()
# Specify height and width
h, w = 512, 512
# Take final h*w bytes from the tail of the file and treat as greyscale values, i.e. mode='L'
im = Image.frombuffer('L', (w,h), f[-(h*w):], "raw", 'L', 0, 1)
# Save to disk
im.save('result.png')
Or, if you prefer Numpy to Pathlib:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
# Specify height and width
h, w = 512, 512
# Slurp entire file into Numpy array, take final 512x512 bytes, and reshape to 512x512
na = np.fromfile('image.raw', dtype=np.uint8)[-(h*w):].reshape((h,w))
# Make Numpy array into PIL Image and save
Image.fromarray(na).save('result.png')
Or, if you don't really fancy writing any Python, just use tail in Terminal to slice the last 512x512 bytes off your file and tell ImageMagick to make an 8-bit PNG from the grayscale byte values:
tail -c $((512*512)) image.raw | magick -depth 8 -size 512x512 gray:- result.png

Pixel extraction using image masking?

I have two pictures.
I want to extract the pixels in the regions marked by their respective color. (The normal distribution of the pixels in yellow and red)
I know opencv supports bitwise operation, however, I've only seen it being done with black/white mask.
I thought about using np.where(), I am curious to see if there's a better solution?
You can count the number of red/yellow pixels like this:
#!/usr/bin/env python3
import cv2
import numpy as np
# Load image
im = cv2.imread('abYaj.png')
# Make mask of red pixels - True where red, false elsewhere
redMask = (im[:, :, 0:3] == [0,0,255]).all(2)
# Count red pixels
redTotal = np.count_nonzero(redMask) # redTotal=44158
# Make mask of yellow pixels
yellowMask = (im[:, :, 0:3] == [0,255,255]).all(2)
# Count yellow pixels
yellowTotal = np.count_nonzero(yellowMask) # yellowTotal=356636
Alternatively, you could just use ImageMagick in the Terminal to count them and write no code at all:
magick identify -verbose abYag.png
Image:
Filename: abYaj.png
Format: PNG (Portable Network Graphics)
Mime type: image/png
Class: DirectClass
Geometry: 1024x1024+0+0
Units: Undefined
Colorspace: sRGB
...
...
Colors: 3
Histogram:
647782: (0,0,0,0) #00000000 none
44158: (255,0,0,255) #FF0000FF red <--- HERE
356636: (255,255,0,255) #FFFF00FF yellow <--- HERE
Rendering intent: Perceptual
...
...

gnuplot: how to plot one 2D array element per pixel with no margins

I am trying to use gnuplot 5.0 to plot a 2D array of data with no margins or borders or axes... just a 2D image (.png or .jpg) representing some data. I would like to have each array element to correspond to exactly one pixel in the image with no scaling / interpolation etc and no extra white pixels at the edges.
So far, when I try to set the margins to 0 and even using the pixels flag, I am still left with a row of white pixels on the right and top borders of the image.
How can I get just an image file with pixel-by-pixel representation of a data array and nothing extra?
gnuplot script:
#!/usr/bin/gnuplot --persist
set terminal png size 400, 200
set size ratio -1
set lmargin at screen 0
set rmargin at screen 1
set tmargin at screen 0
set bmargin at screen 1
unset colorbox
unset tics
unset xtics
unset ytics
unset border
unset key
set output "pic.png"
plot "T.dat" binary array=400x200 format="%f" with image pixels notitle
Example data from Fortran 90:
program main
implicit none
integer, parameter :: nx = 400
integer, parameter :: ny = 200
real, dimension (:,:), allocatable :: T
allocate (T(nx,ny))
T(:,:)=0.500
T(2,2)=5.
T(nx-1,ny-1)=5.
T(2,ny-1)=5.
T(nx-1,2)=5.
open(3, file="T.dat", access="stream")
write(3) T(:,:)
close(3)
end program main
Some gnuplot terminals implement "with image" by creating a separate png file containing the image and then linking to it inside the resulting plot. Using that separate png image file directly will avoid any issues of page layout, margins, etc. Here I use the canvas terminal. The plot itself is thrown away; all we keep is the png file created with the desired content.
gnuplot> set term canvas name 'myplot'
Terminal type is now 'canvas'
Options are ' rounded size 600,400 enhanced fsize 10 lw 1 fontscale 1 standalone'
gnuplot> set output '/dev/null'
gnuplot> plot "T.dat" binary array=400x200 format="%f" with image
linking image 1 to external file myplot_image_01.png
gnuplot> quit
$identify myplot_image_01.png
myplot_image_01.png PNG 400x200 400x200+0+0 8-bit sRGB 348B 0.000u 0:00.000
Don't use gnuplot.
Instead, write a script that reads your data and converts it into one of the Portable Anymap formats. Here's an example in Python:
#!/usr/bin/env python3
import math
import struct
width = 400
height = 200
levels = 255
raw_datum_fmt = '=d' # native, binary double-precision float
raw_datum_size = struct.calcsize(raw_datum_fmt)
with open('T.dat', 'rb') as f:
print("P2")
print("{} {}".format(width, height))
print("{}".format(levels))
raw_data = f.read(width * height * raw_datum_size)
for y in range(height):
for x in range(width):
raw_datum, = struct.unpack_from(raw_datum_fmt, raw_data, (y * width + x) * raw_datum_size)
datum = math.floor(raw_datum * levels) # assume a number in the range [0, 1]
print("{:>3} ".format(datum), end='')
print()
If you can modify the program which generates the data file, you can even skip the above step and instead generate the data directly in a PNM format.
Either way, you can then use ImageMagick to convert the image to a format of your choice:
./convert.py | convert - pic.png
This should be an easy task, however, apparently it's not.
The following might be a (cumbersome) solution because all other attempts failed. My suspicion is that some graphics library has an issue which you probably cannot solve as a gnuplot user.
You mentioned that ASCII matrix data is also ok. The "trick" here is to plot data with lines where the data is "interrupted" by empty lines, basically drawing single points. Check this in case you need to get your datafile 1:1 into a datablock.
However, if it is not already strange enough, it seems to work for png and gif terminal but not for pngcairo or wxt.
I guess the workaround is probably slow and inefficient but at least it creates the desired output. I'm not sure if there is a limit on size. Tested with 100x100 pixels with Win7, gnuplot 5.2.6. Comments and improvements are welcome.
Code:
### pixel image from matrix data without strange white border
reset session
SizeX = 100
SizeY = 100
set terminal png size SizeX,SizeY
set output "tbPixelImage.png"
# generate some random matrix data
set print $Data2
do for [y=1:SizeY] {
Line = ''
do for [x=1:SizeX] {
Line = Line.sprintf(" %9d",int(rand(0)*0x01000000)) # random color
}
print Line
}
set print
# print $Data2
# convert matrix data into x y z data with empty lines inbetween
set print $Data3
do for [y=1:SizeY] {
do for [x=1:SizeX] {
print sprintf("%g %g %s", x, y, word($Data2[y],x))
print ""
}
}
set print
# print $Data3
set margins 0,0,0,0
unset colorbox
unset border
unset key
unset tics
set xrange[1:SizeX]
set yrange[1:SizeY]
plot $Data3 u 1:2:3 w l lw 1 lc rgb var notitle
set output
### end of code
Result: (100x100 pixels)
(enlarged with black background):
Image with 400x200 pixels (takes about 22 sec on my 8 year old laptop).
What I ended up actually using to get what I needed even though the question / bounty asks for a gnuplot solution:
matplotlib has a function matplotlib.pyplot.imsave which does what I was looking for... i.e. plotting 'just data pixels' and no extras like borders, margins, axes, etc. Originally I only knew about matplotlib.pyplot.imshow and had to pull a lot of tricks to eliminate all the extras from the image file and prevent any interpolation/smoothing etc (and therefore turned to gnuplot at a certain point). With imsave it's fairly easy, so I'm back to using matplotlib for an easy yet still flexible (in terms of colormap, scaling, etc) solution for 'pixel exact' plots. Here's an example:
#!/usr/bin/env python3
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
nx = 400
ny = 200
data = np.fromfile('T.dat', dtype=np.float32, count=nx*ny)
data = data.reshape((nx,ny), order='F')
matplotlib.image.imsave('T.png', np.transpose(data), origin='lower', format='png')
OK, here is another possible solution (I separated it from my first cumbersome approach). It creates the plot immediately, less than a second. No renaming necessary or creation of a useless file.
I guess key is to use term png and ps 0.1.
I don't have a proof but I think ps 1 would be ca. 6 pixels large and would create some overlap and/or white pixels at the corner. Again, for whatever reason it seems to work with term png but not with term pngcairo.
What I tested (Win7, gnuplot 5.2.6) is a binary file having the pattern 00 00 FF repeated all over (I can't display null bytes here). Since gnuplot apparently reads 4 bytes per array item (format="%d"), this leads to an alternating RGB pattern if I am plotting with lc rgb var.
In the same way (hopefully) we can figure out how to read format="%f" and use it together with a color palette. I guess that's what you are looking for, right?
Further test results, comments, improvements and explanations are welcome.
Code:
### pixel image from matrix data without strange white border
reset session
SizeX = 400
SizeY = 200
set terminal png size SizeX,SizeY
set output "tbPixelImage.png"
set margins 0,0,0,0
unset colorbox
unset border
unset key
unset tics
set xrange[0:SizeX-1]
set yrange[0:SizeY-1]
plot "tbBinary.dat" binary array=(SizeX,SizeY) format="%d" w p pt 5 ps 0.1 lc rgb var
### end of code
Result:

I can't create image mosaic in GeoServer(JPG image format)

I am trying to create image mosaic out of bunch of .jpg images.
These images all have EXIF header and it looks like this:
Driver: JPEG/JPEG JFIF
Files: IMG_3290.JPG
Size is 4000, 3000
Coordinate System is `'
Metadata:
EXIF_ApertureValue=(3.625)
EXIF_ColorSpace=1
EXIF_ComponentsConfiguration=0x1 0x2 0x3 00
EXIF_CompressedBitsPerPixel=(5)
EXIF_CustomRendered=0
EXIF_DateTime=2015:09:09 15:27:28
EXIF_DateTimeDigitized=2015:09:09 15:27:28
EXIF_DateTimeOriginal=2015:09:09 15:27:28
EXIF_DigitalZoomRatio=(1)
EXIF_ExifVersion=0230
EXIF_ExposureBiasValue=(0)
EXIF_ExposureMode=0
EXIF_ExposureTime=(0.0005)
EXIF_FileSource=0x3
EXIF_Flash=16
EXIF_FlashpixVersion=0100
EXIF_FNumber=(3.5)
EXIF_FocalLength=(4.5)
EXIF_FocalPlaneResolutionUnit=2
EXIF_FocalPlaneXResolution=(16393.4)
EXIF_FocalPlaneYResolution=(16393.4)
EXIF_GPSAltitude=(91.8)
EXIF_GPSAltitudeRef=00
EXIF_GPSDateStamp=2015:09:09
EXIF_GPSLatitude=(45) (18) (3.564)
EXIF_GPSLatitudeRef=N
EXIF_GPSLongitude=(19) (48) (19.842)
EXIF_GPSLongitudeRef=E
EXIF_GPSMapDatum=WGS-84
EXIF_GPSStatus=A
EXIF_GPSTimeStamp=(14) (27) (29)
EXIF_GPSVersionID=0x2 0x3 00 00
EXIF_ImageDescription=
EXIF_Interoperability_Index=R98
EXIF_Interoperability_Version=0x30 0x31 0x30 0x30
EXIF_ISOSpeedRatings=800
EXIF_Make=Canon
EXIF_MakerNote=
EXIF_MaxApertureValue=(3.625)
EXIF_MeteringMode=5
EXIF_Model=Canon PowerShot SX280 HS
EXIF_Orientation=1
EXIF_PixelXDimension=4000
EXIF_PixelYDimension=3000
EXIF_Related_Image_Length=3000
EXIF_Related_Image_Width=4000
EXIF_ResolutionUnit=2
EXIF_SceneCaptureType=0
EXIF_SensingMethod=2
EXIF_ShutterSpeedValue=(10.9688)
EXIF_UserComment=
EXIF_WhiteBalance=1
EXIF_XResolution=(180)
EXIF_YCbCrPositioning=2
EXIF_YResolution=(180)
Image Structure Metadata:
COMPRESSION=JPEG
INTERLEAVE=PIXEL
SOURCE_COLOR_SPACE=YCbCr
Corner Coordinates:
Upper Left ( 0.0, 0.0)
Lower Left ( 0.0, 3000.0)
Upper Right ( 4000.0, 0.0)
Lower Right ( 4000.0, 3000.0)
Center ( 2000.0, 1500.0)
Band 1 Block=4000x1 Type=Byte, ColorInterp=Red
Overviews: 2000x1500, 1000x750, 500x375
Image Structure Metadata:
COMPRESSION=JPEG
Band 2 Block=4000x1 Type=Byte, ColorInterp=Green
Overviews: 2000x1500, 1000x750, 500x375
Image Structure Metadata:
COMPRESSION=JPEG
Band 3 Block=4000x1 Type=Byte, ColorInterp=Blue
Overviews: 2000x1500, 1000x750, 500x375
Image Structure Metadata:
COMPRESSION=JPEG
So in geoserver's user manual it says to use either geotiff or jpg accompanied by .jgw(world file).
I have been trying to create geotiff image using gdal_translate:
>gdal_translate -a_srs EPSG:4326 -of GTiff IMG_3290.JPG IMG_3290.tiff
And now as a result of gdalinfo command i get:
Coordinate System is:
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0],
UNIT["degree",0.0174532925199433],
AUTHORITY["EPSG","4326"]]
And on geotiff images as a result of gdalinfo i get much more detailed georeference data:
Coordinate System is:
PROJCS["WGS 84 / UTM zone 35N",
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0],
UNIT["degree",0.0174532925199433],
AUTHORITY["EPSG","4326"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["latitude_of_origin",0],
PARAMETER["central_meridian",27],
PARAMETER["scale_factor",0.9996],
PARAMETER["false_easting",500000],
PARAMETER["false_northing",0],
UNIT["metre",1,
AUTHORITY["EPSG","9001"]],
AUTHORITY["EPSG","32635"]]
Origin = (510729.434746513376012,4362942.855643210932612)
Pixel Size = (1.000000000000000,-1.000000000000000)
Metadata:
AREA_OR_POINT=Area
Image Structure Metadata:
COMPRESSION=PACKBITS
INTERLEAVE=BAND
Corner Coordinates:
Upper Left ( 510729.435, 4362942.856) ( 27d 7'28.71"E, 39d24'57.38"N)
Lower Left ( 510729.435, 4347929.856) ( 27d 7'27.84"E, 39d16'50.38"N)
Upper Right ( 527866.435, 4362942.856) ( 27d19'25.38"E, 39d24'56.00"N)
Lower Right ( 527866.435, 4347929.856) ( 27d19'23.13"E, 39d16'49.00"N)
Center ( 519297.935, 4355436.356) ( 27d13'26.27"E, 39d20'53.34"N)
So finally is there a way to convert jpg with exif header into geotiff image?
And also how can i create .jgw file out of jpg image with exif header like the one i showed here?
It seems that info in this exif header isn't enough to create true ortophoto as information about rotation of the image is not present in it.
The Exif Specification provides for the following Tags
0x0010 16 GPSInfo Exif.GPSInfo.GPSImgDirectionRef Ascii Indicates the reference for giving the direction of the image when it is captured. "T" denotes true direction and "M" is magnetic direction.
0x0011 17 GPSInfo Exif.GPSInfo.GPSImgDirection Rational Indicates the direction of the image when it was captured. The range of values is from 0.00 to 359.99.
If your Drone Software can populate that at the time the image was taken, you will have what you need

Shift hue on existing image(s) via imagemagick -modulate from Photoshop Hue/Saturation values

I am trying to shift the hue of this image (and others like it) with varying gradients, etc from the "blue" to another color as defined by my designer from the mockups done in Photoshop.
I've attached 2 Hue/Saturation screens from Photoshop with HSL values for purple and orange but using -modulate in Imagemagick via this ruby code:
# burnt orange - works
hue = 25.0 # must be a decimal
sat = 100
bri = 0
# purple - does not work
#hue = 266.0 # must be a decimal
#sat = 100
#bri = 0
# formula from -modulate http://www.imagemagick.org/Usage/color_mods/#modulate
h = ( hue * 100/180 ) + 100
system("convert -define modulate:colorspace=HSB home_tab_back_right.png -modulate #{h},#{sat},#{bri} #{h}-#{sat}-#{bri}-home_tab_back_right.png")
This will work for the burnt orange image, but not for purple and other colors I've been given.
I have had more success by passing "modulate:colorspace=HSB" than without, but still not able to consistently plugin values received from Photoshop and have it take the base blue image and shift the hue.
Can this be done?
I noticed a bug in the above code. The ImageMagick "-modulate" option actually expects the numbers in the order of: $brightness, $saturation, $hue.
I needed to turn "original.png" images in subfolders from the original Blue color, to Red and Green versions.
This is what I used for Red:
for dir in *; do convert -define modulate:colorspace=HSB $dir/original.png -modulate 100,100,180 $dir/original_red.png; done;
This is what I used for Green:
for dir in *; do convert -define modulate:colorspace=HSB $dir/original.png -modulate 100,100,270 $dir/original_green.png; done;
Note that in above, I kept the Brightness and Saturation the same as original (100) and tuned the Hue (180 for Blue > Red, and 270 for Green > Red).
Hope this is helpful!

Resources