2017-11-03 15:25:28 Bryan_QAQ 阅读数 380

       计算机中显示的图形一般可分为两大类,一是位图,二是矢量图。究竟什么是位图什么是矢量图了?我个人的理解是一张位图是一个“集合体”,它是由许多的像素点组成的,而矢量图是一个单个的“整体”,我觉得最好最易懂得办法就是把图片放大,你会发现放大到一定倍数后位图会模糊,也就是会看到我们所说的马赛克,而矢量图没有任何变化。
       今天我要写的内容是图像的像素,我们在做图像处理时,经常会用到循环:

for(i=0,i<width;i++)
{   
    for(j=0,j<height,j++)
        {
            ...........
        }
}

       这里的长和宽单位是像素!比如这里的i取值为1,不是我们认为的1厘米,而是1像素。我们经常可以看到一张图像的像素是300*300,那么这张图片的大小不是300cm*300cm,而是90000像素。简单通俗的理解1cm是不等于1像素的。随便找一张图像,将其放大,你就会理解像素,像素是点,由许多的像素就构成了图像,也就和点成线,线成面的原理一样。下面我将用一张图像用PS处理后来演示,不考虑图像本来的颜色(每个像素是有自己的颜色的,太难画了,我就偷懒了)。我们将图片放大,发现会有许多的不同颜色的方块(其实是点!),将“方块图”抽象出来就得到了像素点“点阵”。
       所以,图像正是由许许多多的点组成的,这些点都有一个共同的名字:像素。

                     
                     
这里写图片描述

2019-03-08 09:10:27 weixin_42927264 阅读数 1118

图像处理技术(RGB分离)

在这里插入图片描述
最近学习了图像处理技术,第一个小工程做的事将一张图片的rgb分离,存为三张图片,就像PS中的RGB通道的三张图片一样。

我们先准备两张24位真彩色图片,一张宽度像素为4的倍数,一张则不是。

我们来看下它的文件头和信息头都储存了什么信息:

位图文件头BITMAPFILEHEADER
这是一个结构,其定义如下:

typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;

这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数),各个域的说明如下:
bfType
指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”。
bfSize
指定文件大小,包括这14个字节。
bfReserved1,bfReserved2
为保留字,不用考虑
bfOffBits
为从文件头到实际的位图数据的偏移字节数

位图信息头BITMAPINFOHEADER
这也是一个结构,其定义如下:

typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;

这个结构的长度是固定的,为40个字节(LONG为32位整数),各个域的说明如下:
biSize
指定这个结构的长度,为40。
biWidth
指定图象的宽度,单位是像素。
biHeight
指定图象的高度,单位是像素。
biPlanes
必须是1,不用考虑。
biBitCount
指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)(新的.bmp格式支持32位色,这里就不做讨论了)。
biCompression
指定位图是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定义好的常量)。要说明的是,Windows位图可以采用RLE4,和RLE8的压缩格式,但用的不多。我们今后所讨论的只有第一种不压缩的情况,即biCompression为BI_RGB的情况。
biSizeImage
指定实际的位图数据占用的字节数,其实也可以从以下的公式中计算出来:
biSizeImage=biWidth’ × biHeight
要注意的是:上述公式中的biWidth’必须是4的整倍数(所以不是biWidth,而是biWidth’,表示大于或等于biWidth的,最接近4的整倍数。举个例子,如果biWidth=240,则biWidth’=240;如果biWidth=241,biWidth’=244)。
如果biCompression为BI_RGB,则该项可能为零
biXPelsPerMeter
指定目标设备的水平分辨率,单位是每米的象素个数,关于分辨率的概念。
biYPelsPerMeter
指定目标设备的垂直分辨率,单位同上。
biClrUsed
指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2biBitCount。
biClrImportant
指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。

一开始我想将原图的每个像素点取出来,然后将RGB分量取出来分别赋值,这样处理对于宽度为4的倍数的图片是没有问题的。但是到了不是4倍数的图片就出问题了。

后来了解到图片的读取和储存都是按字节来进行,而且是必须要是4 的倍数,那我们的解决方案就明确了,把所有字节读出来后再赋值。

那么我们怎么样读出所有的字节来呢?

if (bmpWidth % 4 != 0) {
		bmpWidth = (bmpWidth * infoHeader.biBitCount / 8 + 3) / 4 * 4;
	}
	else {
		bmpWidth = bmpWidth * infoHeader.biBitCount / 8;
	}

利用这个公式转换宽度,再乘以高度就是所有字节的数量了。

下面附上代码:

#pragma once
#include<iostream>
#include<fstream>
#include<Windows.h>

using namespace std;



bool readBmp(char *bmpName) {
	FILE *fb = fopen(bmpName, "rb");
	FILE* pfoutr = fopen("r.bmp", "wb");
	FILE* pfoutg = fopen("g.bmp", "wb");
	FILE* pfoutb = fopen("b.bmp", "wb");
	if (fb == 0) {
		return 0;
	}
	BITMAPFILEHEADER fileHeader;
	BITMAPINFOHEADER infoHeader;
	int bmpWidth =0;//图像的宽
	int bmpHeight=0;//图像的高
	int bmpOffset = 0;

	fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fb);
	fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fb);

	bmpHeight = infoHeader.biHeight;
	bmpWidth = infoHeader.biWidth;
	bmpOffset = fileHeader.bfOffBits;
	
	if (bmpWidth % 4 != 0) {
		bmpWidth = (bmpWidth * infoHeader.biBitCount / 8 + 3) / 4 * 4;
	}
	else {
		bmpWidth = bmpWidth * infoHeader.biBitCount / 8;
	}
	
	if (infoHeader.biBitCount >= 1) {
		
		int size1 = bmpHeight * bmpWidth;
		BYTE *img = new BYTE[size1];
		BYTE *img1 = new BYTE[size1];
		BYTE *img2 = new BYTE[size1];
		BYTE *img3 = new BYTE[size1];
		
		fseek(fb, bmpOffset, 0);
		fread(img, sizeof(BYTE), size1, fb);

		for (int i = 0;i < bmpHeight ;i++) {
			for (int j = 0;j < bmpWidth;j++) {
					switch (j % 3) {
					case 0:
						img1[i*bmpWidth + j] = 0;
						img2[i*bmpWidth + j] = 0;
						img3[i*bmpWidth + j] = img[i*bmpWidth + j];
						break;
					case 1:
						img1[i*bmpWidth + j] = 0;
						img2[i*bmpWidth + j] = img[i*bmpWidth + j];
						img3[i*bmpWidth + j] = 0;
						break;
					case 2:
						img1[i*bmpWidth + j] = img[i*bmpWidth + j];
						img2[i*bmpWidth + j] = 0;
						img3[i*bmpWidth + j] = 0;
						break;
					}
				
				
			}
		}
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutr);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutr);
		fwrite(img1, sizeof(BYTE), size1, pfoutr);
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutg);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutg);
		fwrite(img2, sizeof(BYTE), size1, pfoutg);
		fwrite(&fileHeader, sizeof(fileHeader), 1, pfoutb);
		fwrite(&infoHeader, sizeof(infoHeader), 1, pfoutb);
		fwrite(img3, sizeof(BYTE), size1, pfoutb);
	}


	
	fclose(fb);
	fclose(pfoutr);
	fclose(pfoutg);
	fclose(pfoutb);
	return 1;
}

int main() {
	char bmpName[] = "sea.bmp";
	readBmp(bmpName);
	
}


这代码我就是测试用,写代码时要注意规范,提取函数哈!

原图:
在这里插入图片描述

R通道:在这里插入图片描述

G通道:
在这里插入图片描述

2018-10-20 14:43:31 qq_38137411 阅读数 1692

本文属于原创,转载请注明出处。
实验2.1:图像缩放

  • 实现一个图像缩放函数,可以对输入图像进行任意倍数的缩放;
  • 采用双线性插值进行重采样;
  • X,Y方向的缩放倍数参函数参数的形式传入;
  • 可以只考虑输入图像为3通道,8位深度的情况;
  • 不能调用图像处理库的缩放函数来完成;

实验2.2:图像变形
记[x’, y’]=f([x, y])为像素坐标的一个映射,实现f所表示的图像形变。f的逆映射为:
formula

在这里插入图片描述

在讲实验之前,先说一下变量,之后的变量都是这样,水平方向为height或y,垂直方向为width或x。
在这里插入图片描述
实验2.1:图像缩放
图像缩放要求使用双线性差值算法,算法如下:
在这里插入图片描述

转到二维矩阵中,除掉四条边缘线,该算法可以解释为:
设x和y为缩放倍数,i,j为新图像的某个坐标,tempx,tempy为原图像的坐标,a和b为原图像坐标减去新图像坐标的小数部分,即上图公式中的x,y两个参数,其中floor()函数为向下取整函数,他们之间的关系为:

tempx = floor((i-1)/x);
tempy = floor((j-1)/y);

a = (i-1) / x - tempx;
b = (j-1) / y - tempy;

然后上图公式就可以转化成二维矩阵的公式:

output_img(i,j,:) = input_img(tempx,tempy,:)*(1-a)*(1-b)+input_img(tempx,tempy+1,:)*(1-a)*b...
                +input_img(tempx+1,tempy,:)*a*(1-b)+input_img(tempx+1,tempy+1,:)*a*b;

当然得对四条边缘进行处理,否则矩阵会越界,具体代码如下所示:

function resize( input_img,x,y )
%RESIZE 此处显示有关此函数的摘要
%   input_img为输入图像,output_img为输出图像,x,y为缩放倍数
[I,map] = imread(input_img);
input_img = imread(input_img);
%获取原图像的长宽
[width,height,dimension] = size(I);
%计算新图像的长宽
new_width = round(width*x);
new_height = round(height*y);
figure();
imshow(input_img);
title(['原图像(大小: ',num2str(width),'*',num2str(height),'*',num2str(dimension),')']);
%双线性差值法
for i = 1:new_width
    for j = 1:new_height
        %原来图像的坐标,向下取整
        tempx = floor((i-1)/x);
        tempy = floor((j-1)/y);
        %对四条边和四个顶点进行处理
        if tempx == 0 || tempy == 0 || tempx == width-1 || tempy == height-1
            output_img(1,j,:) = input_img(1,tempy+1,:);
            output_img(i,1,:) = input_img(tempx+1,1,:);
        %对其余像素进行处理
        else
            %计算原图像坐标减去新图像坐标的小数部分
            a = (i-1) / x - tempx;
            b = (j-1) / y - tempy;
            %最小值为1
            tempx = tempx+1;
            tempy = tempy+1;
            output_img(i,j,:) = input_img(tempx,tempy,:)*(1-a)*(1-b)+input_img(tempx,tempy+1,:)*(1-a)*b...
                +input_img(tempx+1,tempy,:)*a*(1-b)+input_img(tempx+1,tempy+1,:)*a*b;
        end
    end
end
figure();
imshow(output_img);
title(['缩放后的图像(大小: ',num2str(new_width),'*',num2str(new_height),'*',num2str(dimension)',')']);
imwrite(output_img,'resize.jpg');

现在看看缩放效果:
原图像

resize('a.jpg',1.3,0.6);

新图像

实验2.2:图像变形
这里的公式在实验要求之中,这里就不多说了,直接给出代码:

function reshape(input_img)
%RESHAPE 此处显示有关此函数的摘要
%   input_img为输入图像,output_img为输出图像,angle为顺时针旋转角度
[I,map] = imread(input_img);
input_img = imread(input_img);
%获取原图像的长宽
[width,height,dimension] = size(I);
for i = 1:width-1
    for j = 1:height-1
        %中心归一化坐标
        tempx = (i-0.5*width)/(0.5*width);
        tempy = (j-0.5*height)/(0.5*height);
        %获取r和angle
        r  = sqrt(tempx^2 + tempy^2);
        angle = (1-r)^2;
        if r >= 1
            x = tempx;
            y = tempy;
        else
            x = cos(angle)*tempx - sin(angle)*tempy;
            y = sin(angle)*tempx + cos(angle)*tempy;
        end
        %必须使用(uint16()函数进行处理坐标,将其转化成无符号16位的int类型,否则坐标索引会出错
        old_x = uint16((x + 1)*0.5*width);
        old_y = uint16((y + 1)*0.5*height);
        %输出图像
        output_img(i,j,:) = input_img(old_x,old_y,:);
    end
end
imshow(output_img);
title('变形后的图像');
imwrite(output_img,'reshape.jpg');
end

这里唯一要注意的就是old_x = uint16((x + 1)*0.5*width);,这个地方得将原来中心归一化后的坐标转回到原来的坐标,并且使用uint16()函数进行处理,将其转化成无符号16位的int类型,否则坐标索引会出错。

re_shape('a.jpg');

下面看看效果:
新图像

2019-11-23 12:13:00 qq_40986693 阅读数 17

图像金字塔的一个应用是图像融合。例如,在图像融合中,需要将两幅图叠在一起,但是由于连接区域图像像素的不连续性,整幅图的效果看起来会很差。这时图像金字塔就可以排上用场了,他可以帮你实现无缝连接。

这里有一点要注意的是,利用金字塔融合需要对你的图片做一定预处理,即图片大小应该为2^6的倍数

# coding=utf-8
import PIL.Image as Image
import shutil
import os
 
 
class Graphics:
    infile = 'D:\\fish1.jpg'
    outfile = 'D:\\fish1_adjust.jpg'
 
    @classmethod
    def fixed_size(cls, width, height):
        """按照固定尺寸处理图片"""
        im = Image.open(cls.infile)
        out = im.resize((width, height),Image.ANTIALIAS)
        out.save(cls.outfile)
 
    @classmethod
    def resize_by_width(cls, w_divide_h):
        """按照宽度进行所需比例缩放"""
        im = Image.open(cls.infile)
        (x, y) = im.size 
        x_s = x
        y_s = x/w_divide_h
        out = im.resize((x_s, y_s), Image.ANTIALIAS) 
        out.save(cls.outfile)
 
    @classmethod
    def resize_by_height(cls, w_divide_h):
        """按照高度进行所需比例缩放"""
        im = Image.open(cls.infile)
        (x, y) = im.size 
        x_s = y*w_divide_h
        y_s = y
        out = im.resize((x_s, y_s), Image.ANTIALIAS) 
        out.save(cls.outfile)
 
    @classmethod
    def resize_by_size(cls, size):
        """按照生成图片文件大小进行处理(单位KB)"""
        size *= 1024
        im = Image.open(cls.infile)
        size_tmp = os.path.getsize(cls.infile)
        q = 100
        while size_tmp > size and q > 0:
            print q
            out = im.resize(im.size, Image.ANTIALIAS)
            out.save(cls.outfile, quality=q)
            size_tmp = os.path.getsize(cls.outfile)
            q -= 5
        if q == 100:
            shutil.copy(cls.infile, cls.outfile)
 
    @classmethod
    def cut_by_ratio(cls, width, height):
        """按照图片长宽比进行分割"""
        im = Image.open(cls.infile)
        width = float(width)
        height = float(height)
        (x, y) = im.size
        if width > height:
            region = (0, int((y-(y * (height / width)))/2), x, int((y+(y * (height / width)))/2))
        elif width < height:
            region = (int((x-(x * (width / height)))/2), 0, int((x+(x * (width / height)))/2), y)
        else:
            region = (0, 0, x, y)
 
        #裁切图片
        crop_img = im.crop(region)
        #保存裁切后的图片
        crop_img.save(cls.outfile)

然后可以对你的图片进行融合

import cv2
import numpy as np
A = cv2.imread('fish1.jpg')
B = cv2.imread('fish2.jpg')
# generate Gaussian pyramid for A
G = A.copy()
gpA = [G]
for i in np.arange(6):     #将苹果进行高斯金字塔处理,总共六级处理
    G = cv2.pyrDown(G)
    gpA.append(G)
# generate Gaussian pyramid for B
G = B.copy()
gpB = [G]
for i in np.arange(6):  # #将橘子进行高斯金字塔处理,总共六级处理
    G = cv2.pyrDown(G)
    gpB.append(G)
# generate Laplacian Pyramid for A
lpA = [gpA[5]]               
for i in np.arange(5,0,-1):    #将苹果进行拉普拉斯金字塔处理,总共5级处理
    GE = cv2.pyrUp(gpA[i])
    L = cv2.subtract(gpA[i-1],GE)
    lpA.append(L)
# generate Laplacian Pyramid for B
lpB = [gpB[5]]
for i in np.arange(5,0,-1):    #将橘子进行拉普拉斯金字塔处理,总共5级处理
    GE = cv2.pyrUp(gpB[i])
    L = cv2.subtract(gpB[i-1],GE)
    lpB.append(L)
# Now add left and right halves of images in each level
#numpy.hstack(tup)
#Take a sequence of arrays and stack them horizontally
#to make a single array.
LS = []
for la,lb in zip(lpA,lpB):
    rows,cols,dpt = la.shape
    ls = np.hstack((la[:,0:cols//2], lb[:,cols//2:]))    #将两个图像的矩阵的左半部分和右半部分拼接到一起
    LS.append(ls)
# now reconstruct
ls_ = LS[0]   #这里LS[0]为高斯金字塔的最小图片
for i in xrange(1,6):                        #第一次循环的图像为高斯金字塔的最小图片,依次通过拉普拉斯金字塔恢复到大图像
    ls_ = cv2.pyrUp(ls_)
    ls_ = cv2.add(ls_, LS[i])                #采用金字塔拼接方法的图像
# image with direct connecting each half
real = np.hstack((A[:,:cols/2],B[:,cols/2:]))   #直接的拼接
cv2.imwrite('Pyramid_blending2.jpg',ls_)
cv2.imwrite('Direct_blending.jpg',real)

这里如果最后提示
slice indices must be integers or None or have an index method
是因为最后没有整除,所以要改为取整 real = np.hstack((A[:,:cols//2],B[:,cols//2:]))
以上可以做出一个小demo

2017-07-19 17:32:33 llh_1178 阅读数 7622

处理图像经常会对图像色彩进行增强,这就是改变图像的亮度和对比度。本章基于opencv对图像的亮度和对比度进行处理操作。其实,这是对图像的每一个像素点进行相应的操作。


上面这个公式可以很好的解释对图像的亮度和对比度操作的原理,第一个参数α必须是大于零,不然则基本上没有意义了。α能代表什么呢?α能使图像像素成倍数的增长或降低(α<1),改变了是图像的对比度,因为使图像的差值变化了。那么β作何解释呢?β可为负,也可为正,那么任何一个像素都在(0, 255)之间,加上一个值或减去一个值则会使这个像素点变大或变小,其实就是向白色或向黑色靠近(0为黑,255为白),所以改变的是图像的亮度。

接下来我们用代码演示:

(一)基于Python语言——演示灰色图像的比色度和反差

import cv2
import matplotlib.pyplot as plt
# 反差与对比度
input_image = cv2.imread("Peppers.jpg")
output_gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
output_min_gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
output_max_gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
# 反差图像,cv2.bitwise_not()函数相当于用255减去每一个像素。
output_contrast_image = cv2.bitwise_not(input_image)
for i in xrange(input_image.shape[0]):
    for j in xrange(input_image.shape[1]):
        b = input_image[i, j, 0]
        g = input_image[i, j, 1]
        r = input_image[i, j, 2]
        output_min_gray[i, j] = min(b, min(g, r))
        output_max_gray[i, j] = max(b, min(g, r))

output_image = [output_gray, output_min_gray, output_max_gray, output_contrast_image]
output_titles = ["output_gray", "output_min_gray", "output_max_gray", "output_contrast_image"]
for i in xrange(4):
    plt.subplot(2, 2, (i+1))
    plt.imshow(output_image[i], "gray")
    plt.title(output_titles[i])
    plt.xticks([]), plt.yticks([])
plt.savefig("contrast_gray")


(二)基于C++语言——演示图像的亮度

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;

int main(int argc, char* argv) {
	Mat src, dst;
	src = imread("test.jpg");
	if (src.empty()) {
		printf("Could not load image...\n");
		return -1;
	}
	char input_win[] = "input image";
	namedWindow(input_win, CV_WINDOW_AUTOSIZE);
	imshow("input window", src);

	int height = src.rows;
	int width = src.cols;
	dst = Mat::zeros(src.size(), src.type());
        float alpha = 0.8;
	float beta = -20;
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			
			float b = src.at<Vec3b>(row, col)[0];
			float g = src.at<Vec3b>(row, col)[1];
			float r = src.at<Vec3b>(row, col)[2];
			// saturate_cast 函数确保像素值在0到255之间。
			dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
			dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
			dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
			
		}
	}
	char output_title[] = "contrast and brightness change demo";
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	imshow(output_title, dst);
	
	waitKey(0);
	return 0;
}




没有更多推荐了,返回首页